1
0
mirror of https://github.com/pixeltris/TwitchAdSolutions.git synced 2024-11-22 02:12:45 +01:00

vaft improvements

Hybrid embed, proxy, fallback implementation
https://github.com/cleanlock/VideoAdBlockForTwitch/pull/63
This commit is contained in:
pixeltris 2022-08-27 15:32:21 +01:00
parent 010167aa03
commit 51ae5b1b6a
2 changed files with 317 additions and 165 deletions

View File

@ -62,16 +62,20 @@ twitch-videoad.js application/javascript
scope.ClientID = 'kimne78kx3ncx6brgo4mv6wki5h1ko'; scope.ClientID = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
scope.ClientVersion = 'null'; scope.ClientVersion = 'null';
scope.ClientSession = 'null'; scope.ClientSession = 'null';
scope.PlayerType1 = 'site'; //Source //scope.PlayerType1 = 'site'; //Source - NOTE: This is unused as it's implicitly used by the website iself
scope.PlayerType2 = 'thunderdome'; //480p scope.PlayerType2 = 'embed'; //Source
scope.PlayerType3 = 'pop_tart'; //480p scope.PlayerType3 = 'proxy'; //Source
scope.PlayerType4 = 'picture-by-picture'; //360p scope.PlayerType4 = 'thunderdome'; //480p
scope.CurrentChannelName = null; scope.CurrentChannelName = null;
scope.UsherParams = null; scope.UsherParams = null;
scope.WasShowingAd = false; scope.WasShowingAd = false;
scope.GQLDeviceID = null; scope.GQLDeviceID = null;
scope.HideBlockingMessage = false; scope.HideBlockingMessage = false;
scope.IsSquadStream = false; scope.IsSquadStream = false;
scope.StreamInfos = [];
scope.StreamInfosByUrl = [];
scope.MainUrlByUrl = [];
scope.EncodingCacheTimeout = 60000;
} }
declareOptions(window); declareOptions(window);
var twitchMainWorker = null; var twitchMainWorker = null;
@ -91,7 +95,9 @@ twitch-videoad.js application/javascript
return; return;
} }
var newBlobStr = ` var newBlobStr = `
${getNewUsher.toString()} ${getStreamUrlForResolution.toString()}
${getStreamForResolution.toString()}
${stripUnusedParams.toString()}
${processM3U8.toString()} ${processM3U8.toString()}
${hookWorkerFetch.toString()} ${hookWorkerFetch.toString()}
${declareOptions.toString()} ${declareOptions.toString()}
@ -142,6 +148,9 @@ twitch-videoad.js application/javascript
} else if (e.data.key == 'ForceChangeQuality') { } else if (e.data.key == 'ForceChangeQuality') {
//This is used to fix the bug where the video would freeze. //This is used to fix the bug where the video would freeze.
try { try {
if (navigator.userAgent.toLowerCase().indexOf('firefox') == -1) {
return;
}
var autoQuality = doTwitchPlayerTask(false, false, false, true, false); var autoQuality = doTwitchPlayerTask(false, false, false, true, false);
var currentQuality = doTwitchPlayerTask(false, true, false, false, false); var currentQuality = doTwitchPlayerTask(false, true, false, false, false);
if (IsPlayerAutoQuality == null) { if (IsPlayerAutoQuality == null) {
@ -253,6 +262,7 @@ twitch-videoad.js application/javascript
return req.responseText.split("'")[1]; return req.responseText.split("'")[1];
} }
function hookWorkerFetch() { function hookWorkerFetch() {
console.log('Twitch adblocker is enabled');
var realFetch = fetch; var realFetch = fetch;
fetch = async function(url, options) { fetch = async function(url, options) {
if (typeof url === 'string') { if (typeof url === 'string') {
@ -289,67 +299,111 @@ twitch-videoad.js application/javascript
if (isPBYPRequest) { if (isPBYPRequest) {
url = ''; url = '';
} }
//Make new Usher request if needed to create fallback if UBlock bypass method fails. return new Promise(function(resolve, reject) {
var useNewUsher = false; var processAfter = async function(response) {
if (url.includes('subscriber%22%3Afalse') && url.includes('hide_ads%22%3Afalse') && url.includes('show_ads%22%3Atrue')) { encodingsM3u8 = await response.text();
useNewUsher = true; var streamInfo = StreamInfos[channelName];
} if (streamInfo == null) {
if (url.includes('subscriber%22%3Atrue') && url.includes('hide_ads%22%3Afalse') && url.includes('show_ads%22%3Atrue')) { StreamInfos[channelName] = streamInfo = {};
useNewUsher = true; }
} streamInfo.ChannelName = channelName;
if (useNewUsher == true) { streamInfo.Urls = [];// xxx.m3u8 -> "284x160" (resolution)
return new Promise(function(resolve, reject) { streamInfo.EncodingsM3U8Cache = [];
var processAfter = async function(response) { var lines = encodingsM3u8.replace('\r', '').split('\n');
encodingsM3u8 = await getNewUsher(realFetch, response, channelName); for (var i = 0; i < lines.length; i++) {
if (encodingsM3u8.length > 1) { if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
resolve(new Response(encodingsM3u8)); streamInfo.Urls[lines[i]] = -1;
} else { if (i > 0 && lines[i - 1].startsWith('#EXT-X-STREAM-INF')) {
postMessage({ var res = parseAttributes(lines[i - 1])['RESOLUTION'];
key: 'HideAdBlockBanner' if (res) {
}); streamInfo.Urls[lines[i]] = res;
resolve(encodingsM3u8); }
}
StreamInfosByUrl[lines[i]] = streamInfo;
MainUrlByUrl[lines[i]] = url;
} }
}; }
var send = function() { resolve(new Response(encodingsM3u8));
return realFetch(url, options).then(function(response) { };
processAfter(response); var send = function() {
})['catch'](function(err) { return realFetch(url, options).then(function(response) {
reject(err); processAfter(response);
}); })['catch'](function(err) {
}; reject(err);
send(); });
}); };
} send();
});
} }
} }
return realFetch.apply(this, arguments); return realFetch.apply(this, arguments);
}; };
} }
//Added as fallback for when UBlock method fails. function getStreamUrlForResolution(targetResolution, encodingsM3u8) {
async function getNewUsher(realFetch, originalResponse, channelName) { var encodingsLines = encodingsM3u8.replace('\r', '').split('\n');
var accessTokenResponse = await getAccessToken(channelName, PlayerType1); var firstUrl = null;
var encodingsM3u8 = ''; for (var i = 0; i < encodingsLines.length; i++) {
if (accessTokenResponse.status === 200) { if (!encodingsLines[i].startsWith('#') && encodingsLines[i].includes('.m3u8')) {
var accessToken = await accessTokenResponse.json(); if (i > 0 && encodingsLines[i - 1].startsWith('#EXT-X-STREAM-INF')) {
try { var res = parseAttributes(encodingsLines[i - 1])['RESOLUTION'];
var urlInfo = new URL('https://usher.ttvnw.net/api/channel/hls/' + channelName + '.m3u8' + UsherParams); if (res && (!targetResolution || res == targetResolution)) {
urlInfo.searchParams.set('sig', accessToken.data.streamPlaybackAccessToken.signature); return encodingsLines[i];
urlInfo.searchParams.set('token', accessToken.data.streamPlaybackAccessToken.value); }
var encodingsM3u8Response = await realFetch(urlInfo.href); if (firstUrl == null) {
if (encodingsM3u8Response.status === 200) { firstUrl = encodingsLines[i];
encodingsM3u8 = await encodingsM3u8Response.text(); }
return encodingsM3u8;
} else {
return originalResponse;
} }
} catch (err) {} }
return originalResponse;
} else {
return originalResponse;
} }
return firstUrl;
}
async function getStreamForResolution(streamInfo, targetResolution, encodingsM3u8, fallbackStreamStr, playerType, realFetch) {
if (streamInfo.EncodingsM3U8Cache[playerType].Resolution != targetResolution ||
streamInfo.EncodingsM3U8Cache[playerType].RequestTime < Date.now() - EncodingCacheTimeout) {
console.log(`Blocking ads (type:${playerType}, resolution:${targetResolution})`);
}
streamInfo.EncodingsM3U8Cache[playerType].RequestTime = Date.now();
streamInfo.EncodingsM3U8Cache[playerType].Value = encodingsM3u8;
streamInfo.EncodingsM3U8Cache[playerType].Resolution = targetResolution;
var streamM3u8Url = getStreamUrlForResolution(targetResolution, encodingsM3u8);
var streamM3u8Response = await realFetch(streamM3u8Url);
if (streamM3u8Response.status == 200) {
var m3u8Text = await streamM3u8Response.text();
WasShowingAd = true;
if (HideBlockingMessage == false) {
postMessage({
key: 'ShowAdBlockBanner'
});
} else if (HideBlockingMessage == true) {
postMessage({
key: 'HideAdBlockBanner'
});
}
postMessage({
key: 'ForceChangeQuality'
});
if (!m3u8Text || m3u8Text.includes(AdSignifier)) {
streamInfo.EncodingsM3U8Cache[playerType].Value = null;
}
return m3u8Text;
} else {
streamInfo.EncodingsM3U8Cache[playerType].Value = null;
return fallbackStreamStr;
}
}
function stripUnusedParams(str, params) {
if (!params) {
params = [ 'token', 'sig' ];
}
var tempUrl = new URL('https://localhost/' + str);
for (var i = 0; i < params.length; i++) {
tempUrl.searchParams.delete(params[i]);
}
return tempUrl.pathname.substring(1) + tempUrl.search;
} }
async function processM3U8(url, textStr, realFetch, playerType) { async function processM3U8(url, textStr, realFetch, playerType) {
//Checks the m3u8 for ads and if it finds one, instead returns an ad-free stream. //Checks the m3u8 for ads and if it finds one, instead returns an ad-free stream.
var streamInfo = StreamInfosByUrl[url];
//Ad blocking for squad streams is disabled due to the way multiple weaver urls are used. No workaround so far. //Ad blocking for squad streams is disabled due to the way multiple weaver urls are used. No workaround so far.
if (IsSquadStream == true) { if (IsSquadStream == true) {
return textStr; return textStr;
@ -363,10 +417,54 @@ twitch-videoad.js application/javascript
} }
var haveAdTags = textStr.includes(AdSignifier); var haveAdTags = textStr.includes(AdSignifier);
if (haveAdTags) { if (haveAdTags) {
//Reduces ad frequency. var isMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
try { //Reduces ad frequency. TODO: Reduce the number of requests. This is really spamming Twitch with requests.
tryNotifyTwitch(textStr); if (!isMidroll) {
} catch (err) {} try {
tryNotifyTwitch(textStr);
} catch (err) {}
}
var currentResolution = null;
if (streamInfo && streamInfo.Urls) {
for (const [resUrl, resName] of Object.entries(streamInfo.Urls)) {
if (resUrl == url) {
currentResolution = resName;
//console.log(resName);
break;
}
}
}
// Keep the m3u8 around for a little while (once per ad) before requesting a new one
var encodingsM3U8Cache = streamInfo.EncodingsM3U8Cache[playerType];
if (encodingsM3U8Cache) {
if (encodingsM3U8Cache.Value && encodingsM3U8Cache.RequestTime >= Date.now() - EncodingCacheTimeout) {
try {
var result = getStreamForResolution(streamInfo, currentResolution, encodingsM3U8Cache.Value, null, playerType, realFetch);
if (result) {
return result;
}
} catch (err) {
encodingsM3U8Cache.Value = null;
}
}
} else {
streamInfo.EncodingsM3U8Cache[playerType] = {
RequestTime: Date.now(),
Value: null,
Resolution: null
};
}
if (playerType === 'proxy') {
try {
/*var tempUrl = stripUnusedParams(MainUrlByUrl[url]);
const match = /(hls|vod)\/(.+?)$/gim.exec(tempUrl);*/
var encodingsM3u8Response = await realFetch('https://api.ttv.lol/playlist/' + CurrentChannelName + '.m3u8%3Fallow_source%3Dtrue'/* + encodeURIComponent(match[2])*/, {headers: {'X-Donate-To': 'https://ttv.lol/donate'}});
if (encodingsM3u8Response.status === 200) {
return getStreamForResolution(streamInfo, currentResolution, await encodingsM3u8Response.text(), textStr, playerType, realFetch);
}
} catch (err) {}
return textStr;
}
var accessTokenResponse = await getAccessToken(CurrentChannelName, playerType); var accessTokenResponse = await getAccessToken(CurrentChannelName, playerType);
if (accessTokenResponse.status === 200) { if (accessTokenResponse.status === 200) {
var accessToken = await accessTokenResponse.json(); var accessToken = await accessTokenResponse.json();
@ -376,29 +474,7 @@ twitch-videoad.js application/javascript
urlInfo.searchParams.set('token', accessToken.data.streamPlaybackAccessToken.value); urlInfo.searchParams.set('token', accessToken.data.streamPlaybackAccessToken.value);
var encodingsM3u8Response = await realFetch(urlInfo.href); var encodingsM3u8Response = await realFetch(urlInfo.href);
if (encodingsM3u8Response.status === 200) { if (encodingsM3u8Response.status === 200) {
var encodingsM3u8 = await encodingsM3u8Response.text(); return getStreamForResolution(streamInfo, currentResolution, await encodingsM3u8Response.text(), textStr, playerType, realFetch);
streamM3u8Url = encodingsM3u8.match(/^https:.*\.m3u8$/mg)[0];
var streamM3u8Response = await realFetch(streamM3u8Url);
if (streamM3u8Response.status == 200) {
var m3u8Text = await streamM3u8Response.text();
console.log("Blocking ads...");
WasShowingAd = true;
if (HideBlockingMessage == false) {
postMessage({
key: 'ShowAdBlockBanner'
});
} else if (HideBlockingMessage == true) {
postMessage({
key: 'HideAdBlockBanner'
});
}
postMessage({
key: 'ForceChangeQuality'
});
return m3u8Text;
} else {
return textStr;
}
} else { } else {
return textStr; return textStr;
} }

View File

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name TwitchAdSolutions (vaft) // @name TwitchAdSolutions (vaft)
// @namespace https://github.com/pixeltris/TwitchAdSolutions // @namespace https://github.com/pixeltris/TwitchAdSolutions
// @version 5.4.0 // @version 5.4.0-f1
// @description Multiple solutions for blocking Twitch ads (vaft) // @description Multiple solutions for blocking Twitch ads (vaft)
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js // @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js // @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js
@ -73,16 +73,20 @@
scope.ClientID = 'kimne78kx3ncx6brgo4mv6wki5h1ko'; scope.ClientID = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
scope.ClientVersion = 'null'; scope.ClientVersion = 'null';
scope.ClientSession = 'null'; scope.ClientSession = 'null';
scope.PlayerType1 = 'site'; //Source //scope.PlayerType1 = 'site'; //Source - NOTE: This is unused as it's implicitly used by the website iself
scope.PlayerType2 = 'thunderdome'; //480p scope.PlayerType2 = 'embed'; //Source
scope.PlayerType3 = 'pop_tart'; //480p scope.PlayerType3 = 'proxy'; //Source
scope.PlayerType4 = 'picture-by-picture'; //360p scope.PlayerType4 = 'thunderdome'; //480p
scope.CurrentChannelName = null; scope.CurrentChannelName = null;
scope.UsherParams = null; scope.UsherParams = null;
scope.WasShowingAd = false; scope.WasShowingAd = false;
scope.GQLDeviceID = null; scope.GQLDeviceID = null;
scope.HideBlockingMessage = false; scope.HideBlockingMessage = false;
scope.IsSquadStream = false; scope.IsSquadStream = false;
scope.StreamInfos = [];
scope.StreamInfosByUrl = [];
scope.MainUrlByUrl = [];
scope.EncodingCacheTimeout = 60000;
} }
declareOptions(window); declareOptions(window);
var twitchMainWorker = null; var twitchMainWorker = null;
@ -102,7 +106,9 @@
return; return;
} }
var newBlobStr = ` var newBlobStr = `
${getNewUsher.toString()} ${getStreamUrlForResolution.toString()}
${getStreamForResolution.toString()}
${stripUnusedParams.toString()}
${processM3U8.toString()} ${processM3U8.toString()}
${hookWorkerFetch.toString()} ${hookWorkerFetch.toString()}
${declareOptions.toString()} ${declareOptions.toString()}
@ -153,6 +159,9 @@
} else if (e.data.key == 'ForceChangeQuality') { } else if (e.data.key == 'ForceChangeQuality') {
//This is used to fix the bug where the video would freeze. //This is used to fix the bug where the video would freeze.
try { try {
if (navigator.userAgent.toLowerCase().indexOf('firefox') == -1) {
return;
}
var autoQuality = doTwitchPlayerTask(false, false, false, true, false); var autoQuality = doTwitchPlayerTask(false, false, false, true, false);
var currentQuality = doTwitchPlayerTask(false, true, false, false, false); var currentQuality = doTwitchPlayerTask(false, true, false, false, false);
if (IsPlayerAutoQuality == null) { if (IsPlayerAutoQuality == null) {
@ -264,6 +273,7 @@
return req.responseText.split("'")[1]; return req.responseText.split("'")[1];
} }
function hookWorkerFetch() { function hookWorkerFetch() {
console.log('Twitch adblocker is enabled');
var realFetch = fetch; var realFetch = fetch;
fetch = async function(url, options) { fetch = async function(url, options) {
if (typeof url === 'string') { if (typeof url === 'string') {
@ -300,67 +310,111 @@
if (isPBYPRequest) { if (isPBYPRequest) {
url = ''; url = '';
} }
//Make new Usher request if needed to create fallback if UBlock bypass method fails. return new Promise(function(resolve, reject) {
var useNewUsher = false; var processAfter = async function(response) {
if (url.includes('subscriber%22%3Afalse') && url.includes('hide_ads%22%3Afalse') && url.includes('show_ads%22%3Atrue')) { encodingsM3u8 = await response.text();
useNewUsher = true; var streamInfo = StreamInfos[channelName];
} if (streamInfo == null) {
if (url.includes('subscriber%22%3Atrue') && url.includes('hide_ads%22%3Afalse') && url.includes('show_ads%22%3Atrue')) { StreamInfos[channelName] = streamInfo = {};
useNewUsher = true; }
} streamInfo.ChannelName = channelName;
if (useNewUsher == true) { streamInfo.Urls = [];// xxx.m3u8 -> "284x160" (resolution)
return new Promise(function(resolve, reject) { streamInfo.EncodingsM3U8Cache = [];
var processAfter = async function(response) { var lines = encodingsM3u8.replace('\r', '').split('\n');
encodingsM3u8 = await getNewUsher(realFetch, response, channelName); for (var i = 0; i < lines.length; i++) {
if (encodingsM3u8.length > 1) { if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
resolve(new Response(encodingsM3u8)); streamInfo.Urls[lines[i]] = -1;
} else { if (i > 0 && lines[i - 1].startsWith('#EXT-X-STREAM-INF')) {
postMessage({ var res = parseAttributes(lines[i - 1])['RESOLUTION'];
key: 'HideAdBlockBanner' if (res) {
}); streamInfo.Urls[lines[i]] = res;
resolve(encodingsM3u8); }
}
StreamInfosByUrl[lines[i]] = streamInfo;
MainUrlByUrl[lines[i]] = url;
} }
}; }
var send = function() { resolve(new Response(encodingsM3u8));
return realFetch(url, options).then(function(response) { };
processAfter(response); var send = function() {
})['catch'](function(err) { return realFetch(url, options).then(function(response) {
reject(err); processAfter(response);
}); })['catch'](function(err) {
}; reject(err);
send(); });
}); };
} send();
});
} }
} }
return realFetch.apply(this, arguments); return realFetch.apply(this, arguments);
}; };
} }
//Added as fallback for when UBlock method fails. function getStreamUrlForResolution(targetResolution, encodingsM3u8) {
async function getNewUsher(realFetch, originalResponse, channelName) { var encodingsLines = encodingsM3u8.replace('\r', '').split('\n');
var accessTokenResponse = await getAccessToken(channelName, PlayerType1); var firstUrl = null;
var encodingsM3u8 = ''; for (var i = 0; i < encodingsLines.length; i++) {
if (accessTokenResponse.status === 200) { if (!encodingsLines[i].startsWith('#') && encodingsLines[i].includes('.m3u8')) {
var accessToken = await accessTokenResponse.json(); if (i > 0 && encodingsLines[i - 1].startsWith('#EXT-X-STREAM-INF')) {
try { var res = parseAttributes(encodingsLines[i - 1])['RESOLUTION'];
var urlInfo = new URL('https://usher.ttvnw.net/api/channel/hls/' + channelName + '.m3u8' + UsherParams); if (res && (!targetResolution || res == targetResolution)) {
urlInfo.searchParams.set('sig', accessToken.data.streamPlaybackAccessToken.signature); return encodingsLines[i];
urlInfo.searchParams.set('token', accessToken.data.streamPlaybackAccessToken.value); }
var encodingsM3u8Response = await realFetch(urlInfo.href); if (firstUrl == null) {
if (encodingsM3u8Response.status === 200) { firstUrl = encodingsLines[i];
encodingsM3u8 = await encodingsM3u8Response.text(); }
return encodingsM3u8;
} else {
return originalResponse;
} }
} catch (err) {} }
return originalResponse;
} else {
return originalResponse;
} }
return firstUrl;
}
async function getStreamForResolution(streamInfo, targetResolution, encodingsM3u8, fallbackStreamStr, playerType, realFetch) {
if (streamInfo.EncodingsM3U8Cache[playerType].Resolution != targetResolution ||
streamInfo.EncodingsM3U8Cache[playerType].RequestTime < Date.now() - EncodingCacheTimeout) {
console.log(`Blocking ads (type:${playerType}, resolution:${targetResolution})`);
}
streamInfo.EncodingsM3U8Cache[playerType].RequestTime = Date.now();
streamInfo.EncodingsM3U8Cache[playerType].Value = encodingsM3u8;
streamInfo.EncodingsM3U8Cache[playerType].Resolution = targetResolution;
var streamM3u8Url = getStreamUrlForResolution(targetResolution, encodingsM3u8);
var streamM3u8Response = await realFetch(streamM3u8Url);
if (streamM3u8Response.status == 200) {
var m3u8Text = await streamM3u8Response.text();
WasShowingAd = true;
if (HideBlockingMessage == false) {
postMessage({
key: 'ShowAdBlockBanner'
});
} else if (HideBlockingMessage == true) {
postMessage({
key: 'HideAdBlockBanner'
});
}
postMessage({
key: 'ForceChangeQuality'
});
if (!m3u8Text || m3u8Text.includes(AdSignifier)) {
streamInfo.EncodingsM3U8Cache[playerType].Value = null;
}
return m3u8Text;
} else {
streamInfo.EncodingsM3U8Cache[playerType].Value = null;
return fallbackStreamStr;
}
}
function stripUnusedParams(str, params) {
if (!params) {
params = [ 'token', 'sig' ];
}
var tempUrl = new URL('https://localhost/' + str);
for (var i = 0; i < params.length; i++) {
tempUrl.searchParams.delete(params[i]);
}
return tempUrl.pathname.substring(1) + tempUrl.search;
} }
async function processM3U8(url, textStr, realFetch, playerType) { async function processM3U8(url, textStr, realFetch, playerType) {
//Checks the m3u8 for ads and if it finds one, instead returns an ad-free stream. //Checks the m3u8 for ads and if it finds one, instead returns an ad-free stream.
var streamInfo = StreamInfosByUrl[url];
//Ad blocking for squad streams is disabled due to the way multiple weaver urls are used. No workaround so far. //Ad blocking for squad streams is disabled due to the way multiple weaver urls are used. No workaround so far.
if (IsSquadStream == true) { if (IsSquadStream == true) {
return textStr; return textStr;
@ -374,10 +428,54 @@
} }
var haveAdTags = textStr.includes(AdSignifier); var haveAdTags = textStr.includes(AdSignifier);
if (haveAdTags) { if (haveAdTags) {
//Reduces ad frequency. var isMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
try { //Reduces ad frequency. TODO: Reduce the number of requests. This is really spamming Twitch with requests.
tryNotifyTwitch(textStr); if (!isMidroll) {
} catch (err) {} try {
tryNotifyTwitch(textStr);
} catch (err) {}
}
var currentResolution = null;
if (streamInfo && streamInfo.Urls) {
for (const [resUrl, resName] of Object.entries(streamInfo.Urls)) {
if (resUrl == url) {
currentResolution = resName;
//console.log(resName);
break;
}
}
}
// Keep the m3u8 around for a little while (once per ad) before requesting a new one
var encodingsM3U8Cache = streamInfo.EncodingsM3U8Cache[playerType];
if (encodingsM3U8Cache) {
if (encodingsM3U8Cache.Value && encodingsM3U8Cache.RequestTime >= Date.now() - EncodingCacheTimeout) {
try {
var result = getStreamForResolution(streamInfo, currentResolution, encodingsM3U8Cache.Value, null, playerType, realFetch);
if (result) {
return result;
}
} catch (err) {
encodingsM3U8Cache.Value = null;
}
}
} else {
streamInfo.EncodingsM3U8Cache[playerType] = {
RequestTime: Date.now(),
Value: null,
Resolution: null
};
}
if (playerType === 'proxy') {
try {
/*var tempUrl = stripUnusedParams(MainUrlByUrl[url]);
const match = /(hls|vod)\/(.+?)$/gim.exec(tempUrl);*/
var encodingsM3u8Response = await realFetch('https://api.ttv.lol/playlist/' + CurrentChannelName + '.m3u8%3Fallow_source%3Dtrue'/* + encodeURIComponent(match[2])*/, {headers: {'X-Donate-To': 'https://ttv.lol/donate'}});
if (encodingsM3u8Response.status === 200) {
return getStreamForResolution(streamInfo, currentResolution, await encodingsM3u8Response.text(), textStr, playerType, realFetch);
}
} catch (err) {}
return textStr;
}
var accessTokenResponse = await getAccessToken(CurrentChannelName, playerType); var accessTokenResponse = await getAccessToken(CurrentChannelName, playerType);
if (accessTokenResponse.status === 200) { if (accessTokenResponse.status === 200) {
var accessToken = await accessTokenResponse.json(); var accessToken = await accessTokenResponse.json();
@ -387,29 +485,7 @@
urlInfo.searchParams.set('token', accessToken.data.streamPlaybackAccessToken.value); urlInfo.searchParams.set('token', accessToken.data.streamPlaybackAccessToken.value);
var encodingsM3u8Response = await realFetch(urlInfo.href); var encodingsM3u8Response = await realFetch(urlInfo.href);
if (encodingsM3u8Response.status === 200) { if (encodingsM3u8Response.status === 200) {
var encodingsM3u8 = await encodingsM3u8Response.text(); return getStreamForResolution(streamInfo, currentResolution, await encodingsM3u8Response.text(), textStr, playerType, realFetch);
streamM3u8Url = encodingsM3u8.match(/^https:.*\.m3u8$/mg)[0];
var streamM3u8Response = await realFetch(streamM3u8Url);
if (streamM3u8Response.status == 200) {
var m3u8Text = await streamM3u8Response.text();
console.log("Blocking ads...");
WasShowingAd = true;
if (HideBlockingMessage == false) {
postMessage({
key: 'ShowAdBlockBanner'
});
} else if (HideBlockingMessage == true) {
postMessage({
key: 'HideAdBlockBanner'
});
}
postMessage({
key: 'ForceChangeQuality'
});
return m3u8Text;
} else {
return textStr;
}
} else { } else {
return textStr; return textStr;
} }