Skip to content

Commit f5594de

Browse files
committed
Fix GQL requests failing under Brave (userscript) #392
Also moved back to old player type #387
1 parent accf04a commit f5594de

File tree

4 files changed

+346
-112
lines changed

4 files changed

+346
-112
lines changed

vaft/vaft-ublock-origin.js

Lines changed: 87 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
twitch-videoad.js text/javascript
22
(function() {
33
if ( /(^|\.)twitch\.tv$/.test(document.location.hostname) === false ) { return; }
4-
var ourTwitchAdSolutionsVersion = 5;// Only bump this when there's a breaking change to Twitch, the script, or there's a conflict with an unmaintained extension which uses this script
5-
if (window.twitchAdSolutionsVersion && window.twitchAdSolutionsVersion >= ourTwitchAdSolutionsVersion) {
6-
console.log("skipping vaft as there's another script active. ourVersion:" + ourTwitchAdSolutionsVersion + " activeVersion:" + window.twitchAdSolutionsVersion);
7-
window.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
4+
var ourTwitchAdSolutionsVersion = 6;// Only bump this when there's a breaking change to Twitch, the script, or there's a conflict with an unmaintained extension which uses this script
5+
if (typeof unsafeWindow === 'undefined') {
6+
unsafeWindow = window;
7+
}
8+
if (typeof unsafeWindow.twitchAdSolutionsVersion !== 'undefined' && unsafeWindow.twitchAdSolutionsVersion >= ourTwitchAdSolutionsVersion) {
9+
console.log("skipping vaft as there's another script active. ourVersion:" + ourTwitchAdSolutionsVersion + " activeVersion:" + unsafeWindow.twitchAdSolutionsVersion);
10+
unsafeWindow.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
811
return;
912
}
10-
window.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
13+
unsafeWindow.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
1114
function declareOptions(scope) {
1215
scope.AdSignifier = 'stitched';
1316
scope.ClientID = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
1417
scope.ClientVersion = 'null';
1518
scope.ClientSession = 'null';
1619
scope.PlayerType2 = 'embed'; //Source
1720
scope.PlayerType3 = 'site'; //Source
18-
scope.PlayerType4 = 'picture-by-picture'; //360p
21+
scope.PlayerType4 = 'autoplay'; //360p
1922
scope.CurrentChannelName = null;
2023
scope.UsherParams = null;
2124
scope.WasShowingAd = false;
@@ -90,8 +93,8 @@ twitch-videoad.js text/javascript
9093
|| workerStringReinsert.some((x) => workerString.includes(x));
9194
}
9295
function hookWindowWorker() {
93-
var reinsert = getWorkersForReinsert(window.Worker);
94-
var newWorker = class Worker extends getCleanWorker(window.Worker) {
96+
var reinsert = getWorkersForReinsert(unsafeWindow.Worker);
97+
var newWorker = class Worker extends getCleanWorker(unsafeWindow.Worker) {
9598
constructor(twitchBlobUrl, options) {
9699
var isTwitchWorker = false;
97100
try {
@@ -242,10 +245,10 @@ twitch-videoad.js text/javascript
242245
qualityToSelect = 0;
243246
}
244247
}
245-
var currentQualityLS = window.localStorage.getItem('video-quality');
248+
var currentQualityLS = unsafeWindow.localStorage.getItem('video-quality');
246249
lowQuality[qualityToSelect].click();
247250
settingsCog.click();
248-
window.localStorage.setItem('video-quality', currentQualityLS);
251+
unsafeWindow.localStorage.setItem('video-quality', currentQualityLS);
249252
if (e.data.value != null) {
250253
OriginalVideoPlayerQuality = null;
251254
IsPlayerAutoQuality = null;
@@ -292,7 +295,7 @@ twitch-videoad.js text/javascript
292295
}
293296
};
294297
var workerInstance = reinsertWorkers(newWorker, reinsert);
295-
Object.defineProperty(window, 'Worker', {
298+
Object.defineProperty(unsafeWindow, 'Worker', {
296299
get: function() {
297300
return workerInstance;
298301
},
@@ -836,26 +839,81 @@ twitch-videoad.js text/javascript
836839
} catch (err) {}
837840
} catch (err) {}
838841
}
839-
window.reloadTwitchPlayer = doTwitchPlayerTask;
842+
unsafeWindow.reloadTwitchPlayer = doTwitchPlayerTask;
840843
var localDeviceID = null;
841-
localDeviceID = window.localStorage.getItem('local_copy_unique_id');
844+
localDeviceID = unsafeWindow.localStorage.getItem('local_copy_unique_id');
842845
function postTwitchWorkerMessage(key, value) {
843846
twitchWorkers.forEach((worker) => {
844847
worker.postMessage({key: key, value: value});
845848
});
846849
}
850+
function makeGmXmlHttpRequest(fetchRequest) {
851+
return new Promise((resolve, reject) => {
852+
GM.xmlHttpRequest({
853+
method: fetchRequest.options.method,
854+
url: fetchRequest.url,
855+
data: fetchRequest.options.body,
856+
headers: fetchRequest.options.headers,
857+
onload: response => resolve(response),
858+
onerror: error => reject(error)
859+
});
860+
});
861+
}
862+
// Taken from https://github.com/dimdenGD/YeahTwitter/blob/9e0520f5abe029f57929795d8de0d2e5d3751cf3/us.js#L48
863+
function parseHeaders(headersString) {
864+
const headers = new Headers();
865+
const lines = headersString.trim().split(/[\r\n]+/);
866+
lines.forEach(line => {
867+
const parts = line.split(':');
868+
const header = parts.shift();
869+
const value = parts.join(':');
870+
headers.append(header, value);
871+
});
872+
return headers;
873+
}
874+
var serverLikesThisBrowser = false;
875+
var serverHatesThisBrowser = false;
847876
async function handleWorkerFetchRequest(fetchRequest) {
848877
try {
849-
const response = await window.realFetch(fetchRequest.url, fetchRequest.options);
850-
const responseBody = await response.text();
851-
const responseObject = {
852-
id: fetchRequest.id,
853-
status: response.status,
854-
statusText: response.statusText,
855-
headers: Object.fromEntries(response.headers.entries()),
856-
body: responseBody
857-
};
858-
return responseObject;
878+
if (serverLikesThisBrowser || !serverHatesThisBrowser) {
879+
const response = await unsafeWindow.realFetch(fetchRequest.url, fetchRequest.options);
880+
const responseBody = await response.text();
881+
const responseObject = {
882+
id: fetchRequest.id,
883+
status: response.status,
884+
statusText: response.statusText,
885+
headers: Object.fromEntries(response.headers.entries()),
886+
body: responseBody
887+
};
888+
if (responseObject.status === 200) {
889+
var resp = JSON.parse(responseBody);
890+
if (typeof resp.errors !== 'undefined') {
891+
serverHatesThisBrowser = true;
892+
} else {
893+
serverLikesThisBrowser = true;
894+
}
895+
}
896+
if (serverLikesThisBrowser || !serverHatesThisBrowser) {
897+
return responseObject;
898+
}
899+
}
900+
if (typeof GM !== 'undefined' && typeof GM.xmlHttpRequest !== 'undefined') {
901+
fetchRequest.options.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux i686; rv:140.0) Gecko/20100101 Firefox/140.0';
902+
fetchRequest.options.headers['Referer'] = 'https://www.twitch.tv/';
903+
fetchRequest.options.headers['Origin'] = 'https://www.twitch.tv/';
904+
fetchRequest.options.headers['Host'] = 'gql.twitch.tv';
905+
const response = await makeGmXmlHttpRequest(fetchRequest);
906+
const responseBody = response.responseText;
907+
const responseObject = {
908+
id: fetchRequest.id,
909+
status: response.status,
910+
statusText: response.statusText,
911+
headers: Object.fromEntries(parseHeaders(response.responseHeaders).entries()),
912+
body: responseBody
913+
};
914+
return responseObject;
915+
}
916+
throw { message: 'Failed to resolve GQL request. Try the userscript version of the ad blocking solution' };
859917
} catch (error) {
860918
return {
861919
id: fetchRequest.id,
@@ -864,12 +922,12 @@ twitch-videoad.js text/javascript
864922
}
865923
}
866924
function hookFetch() {
867-
var realFetch = window.fetch;
868-
window.realFetch = realFetch;
869-
window.fetch = function(url, init, ...args) {
925+
var realFetch = unsafeWindow.fetch;
926+
unsafeWindow.realFetch = realFetch;
927+
unsafeWindow.fetch = function(url, init, ...args) {
870928
if (typeof url === 'string') {
871929
//Check if squad stream.
872-
if (window.location.pathname.includes('/squad')) {
930+
if (unsafeWindow.location.pathname.includes('/squad')) {
873931
postTwitchWorkerMessage('UpdateIsSquadStream', true);
874932
} else {
875933
postTwitchWorkerMessage('UpdateIsSquadStream', false);
@@ -992,13 +1050,13 @@ twitch-videoad.js text/javascript
9921050
}
9931051
}catch{}
9941052
}
995-
declareOptions(window);
1053+
declareOptions(unsafeWindow);
9961054
hookWindowWorker();
9971055
hookFetch();
9981056
if (document.readyState === "complete" || document.readyState === "loaded" || document.readyState === "interactive") {
9991057
onContentLoaded();
10001058
} else {
1001-
window.addEventListener("DOMContentLoaded", function() {
1059+
unsafeWindow.addEventListener("DOMContentLoaded", function() {
10021060
onContentLoaded();
10031061
});
10041062
}

vaft/vaft.user.js

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,37 @@
11
// ==UserScript==
22
// @name TwitchAdSolutions (vaft)
33
// @namespace https://github.com/pixeltris/TwitchAdSolutions
4-
// @version 20.0.0
4+
// @version 21.0.0
55
// @description Multiple solutions for blocking Twitch ads (vaft)
66
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js
77
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js
88
// @author https://github.com/cleanlock/VideoAdBlockForTwitch#credits
99
// @match *://*.twitch.tv/*
1010
// @run-at document-start
1111
// @inject-into page
12-
// @grant none
12+
// @grant GM.xmlHttpRequest
13+
// @connect gql.twitch.tv
1314
// ==/UserScript==
1415
(function() {
1516
'use strict';
16-
var ourTwitchAdSolutionsVersion = 5;// Only bump this when there's a breaking change to Twitch, the script, or there's a conflict with an unmaintained extension which uses this script
17-
if (window.twitchAdSolutionsVersion && window.twitchAdSolutionsVersion >= ourTwitchAdSolutionsVersion) {
18-
console.log("skipping vaft as there's another script active. ourVersion:" + ourTwitchAdSolutionsVersion + " activeVersion:" + window.twitchAdSolutionsVersion);
19-
window.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
17+
var ourTwitchAdSolutionsVersion = 6;// Only bump this when there's a breaking change to Twitch, the script, or there's a conflict with an unmaintained extension which uses this script
18+
if (typeof unsafeWindow === 'undefined') {
19+
unsafeWindow = window;
20+
}
21+
if (typeof unsafeWindow.twitchAdSolutionsVersion !== 'undefined' && unsafeWindow.twitchAdSolutionsVersion >= ourTwitchAdSolutionsVersion) {
22+
console.log("skipping vaft as there's another script active. ourVersion:" + ourTwitchAdSolutionsVersion + " activeVersion:" + unsafeWindow.twitchAdSolutionsVersion);
23+
unsafeWindow.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
2024
return;
2125
}
22-
window.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
26+
unsafeWindow.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
2327
function declareOptions(scope) {
2428
scope.AdSignifier = 'stitched';
2529
scope.ClientID = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
2630
scope.ClientVersion = 'null';
2731
scope.ClientSession = 'null';
2832
scope.PlayerType2 = 'embed'; //Source
2933
scope.PlayerType3 = 'site'; //Source
30-
scope.PlayerType4 = 'picture-by-picture'; //360p
34+
scope.PlayerType4 = 'autoplay'; //360p
3135
scope.CurrentChannelName = null;
3236
scope.UsherParams = null;
3337
scope.WasShowingAd = false;
@@ -102,8 +106,8 @@
102106
|| workerStringReinsert.some((x) => workerString.includes(x));
103107
}
104108
function hookWindowWorker() {
105-
var reinsert = getWorkersForReinsert(window.Worker);
106-
var newWorker = class Worker extends getCleanWorker(window.Worker) {
109+
var reinsert = getWorkersForReinsert(unsafeWindow.Worker);
110+
var newWorker = class Worker extends getCleanWorker(unsafeWindow.Worker) {
107111
constructor(twitchBlobUrl, options) {
108112
var isTwitchWorker = false;
109113
try {
@@ -254,10 +258,10 @@
254258
qualityToSelect = 0;
255259
}
256260
}
257-
var currentQualityLS = window.localStorage.getItem('video-quality');
261+
var currentQualityLS = unsafeWindow.localStorage.getItem('video-quality');
258262
lowQuality[qualityToSelect].click();
259263
settingsCog.click();
260-
window.localStorage.setItem('video-quality', currentQualityLS);
264+
unsafeWindow.localStorage.setItem('video-quality', currentQualityLS);
261265
if (e.data.value != null) {
262266
OriginalVideoPlayerQuality = null;
263267
IsPlayerAutoQuality = null;
@@ -304,7 +308,7 @@
304308
}
305309
};
306310
var workerInstance = reinsertWorkers(newWorker, reinsert);
307-
Object.defineProperty(window, 'Worker', {
311+
Object.defineProperty(unsafeWindow, 'Worker', {
308312
get: function() {
309313
return workerInstance;
310314
},
@@ -848,26 +852,81 @@
848852
} catch (err) {}
849853
} catch (err) {}
850854
}
851-
window.reloadTwitchPlayer = doTwitchPlayerTask;
855+
unsafeWindow.reloadTwitchPlayer = doTwitchPlayerTask;
852856
var localDeviceID = null;
853-
localDeviceID = window.localStorage.getItem('local_copy_unique_id');
857+
localDeviceID = unsafeWindow.localStorage.getItem('local_copy_unique_id');
854858
function postTwitchWorkerMessage(key, value) {
855859
twitchWorkers.forEach((worker) => {
856860
worker.postMessage({key: key, value: value});
857861
});
858862
}
863+
function makeGmXmlHttpRequest(fetchRequest) {
864+
return new Promise((resolve, reject) => {
865+
GM.xmlHttpRequest({
866+
method: fetchRequest.options.method,
867+
url: fetchRequest.url,
868+
data: fetchRequest.options.body,
869+
headers: fetchRequest.options.headers,
870+
onload: response => resolve(response),
871+
onerror: error => reject(error)
872+
});
873+
});
874+
}
875+
// Taken from https://github.com/dimdenGD/YeahTwitter/blob/9e0520f5abe029f57929795d8de0d2e5d3751cf3/us.js#L48
876+
function parseHeaders(headersString) {
877+
const headers = new Headers();
878+
const lines = headersString.trim().split(/[\r\n]+/);
879+
lines.forEach(line => {
880+
const parts = line.split(':');
881+
const header = parts.shift();
882+
const value = parts.join(':');
883+
headers.append(header, value);
884+
});
885+
return headers;
886+
}
887+
var serverLikesThisBrowser = false;
888+
var serverHatesThisBrowser = false;
859889
async function handleWorkerFetchRequest(fetchRequest) {
860890
try {
861-
const response = await window.realFetch(fetchRequest.url, fetchRequest.options);
862-
const responseBody = await response.text();
863-
const responseObject = {
864-
id: fetchRequest.id,
865-
status: response.status,
866-
statusText: response.statusText,
867-
headers: Object.fromEntries(response.headers.entries()),
868-
body: responseBody
869-
};
870-
return responseObject;
891+
if (serverLikesThisBrowser || !serverHatesThisBrowser) {
892+
const response = await unsafeWindow.realFetch(fetchRequest.url, fetchRequest.options);
893+
const responseBody = await response.text();
894+
const responseObject = {
895+
id: fetchRequest.id,
896+
status: response.status,
897+
statusText: response.statusText,
898+
headers: Object.fromEntries(response.headers.entries()),
899+
body: responseBody
900+
};
901+
if (responseObject.status === 200) {
902+
var resp = JSON.parse(responseBody);
903+
if (typeof resp.errors !== 'undefined') {
904+
serverHatesThisBrowser = true;
905+
} else {
906+
serverLikesThisBrowser = true;
907+
}
908+
}
909+
if (serverLikesThisBrowser || !serverHatesThisBrowser) {
910+
return responseObject;
911+
}
912+
}
913+
if (typeof GM !== 'undefined' && typeof GM.xmlHttpRequest !== 'undefined') {
914+
fetchRequest.options.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux i686; rv:140.0) Gecko/20100101 Firefox/140.0';
915+
fetchRequest.options.headers['Referer'] = 'https://www.twitch.tv/';
916+
fetchRequest.options.headers['Origin'] = 'https://www.twitch.tv/';
917+
fetchRequest.options.headers['Host'] = 'gql.twitch.tv';
918+
const response = await makeGmXmlHttpRequest(fetchRequest);
919+
const responseBody = response.responseText;
920+
const responseObject = {
921+
id: fetchRequest.id,
922+
status: response.status,
923+
statusText: response.statusText,
924+
headers: Object.fromEntries(parseHeaders(response.responseHeaders).entries()),
925+
body: responseBody
926+
};
927+
return responseObject;
928+
}
929+
throw { message: 'Failed to resolve GQL request. Try the userscript version of the ad blocking solution' };
871930
} catch (error) {
872931
return {
873932
id: fetchRequest.id,
@@ -876,12 +935,12 @@
876935
}
877936
}
878937
function hookFetch() {
879-
var realFetch = window.fetch;
880-
window.realFetch = realFetch;
881-
window.fetch = function(url, init, ...args) {
938+
var realFetch = unsafeWindow.fetch;
939+
unsafeWindow.realFetch = realFetch;
940+
unsafeWindow.fetch = function(url, init, ...args) {
882941
if (typeof url === 'string') {
883942
//Check if squad stream.
884-
if (window.location.pathname.includes('/squad')) {
943+
if (unsafeWindow.location.pathname.includes('/squad')) {
885944
postTwitchWorkerMessage('UpdateIsSquadStream', true);
886945
} else {
887946
postTwitchWorkerMessage('UpdateIsSquadStream', false);
@@ -1004,13 +1063,13 @@
10041063
}
10051064
}catch{}
10061065
}
1007-
declareOptions(window);
1066+
declareOptions(unsafeWindow);
10081067
hookWindowWorker();
10091068
hookFetch();
10101069
if (document.readyState === "complete" || document.readyState === "loaded" || document.readyState === "interactive") {
10111070
onContentLoaded();
10121071
} else {
1013-
window.addEventListener("DOMContentLoaded", function() {
1072+
unsafeWindow.addEventListener("DOMContentLoaded", function() {
10141073
onContentLoaded();
10151074
});
10161075
}

0 commit comments

Comments
 (0)