mirror of
https://github.com/pixeltris/TwitchAdSolutions.git
synced 2025-04-29 22:24:29 +02:00
Fix buffering issue after ad segments
- Also switch to an MutationObserver model for mute-black / vide-swap #16
This commit is contained in:
parent
a6928c9d04
commit
2424ab3cc1
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @description Multiple solutions for blocking Twitch ads
|
||||
// @author pixeltris
|
||||
// @match *://*.twitch.tv/*
|
||||
@ -58,8 +58,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -91,6 +89,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -130,6 +129,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -205,7 +207,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -261,20 +263,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -323,8 +347,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -373,33 +395,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -427,6 +428,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -538,6 +545,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -869,6 +877,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -902,9 +911,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -945,6 +967,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -972,11 +999,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -49,8 +49,6 @@ twitch-videoad.js application/javascript
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -82,6 +80,7 @@ twitch-videoad.js application/javascript
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -121,6 +120,9 @@ twitch-videoad.js application/javascript
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -196,7 +198,7 @@ twitch-videoad.js application/javascript
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -252,20 +254,42 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -314,8 +338,6 @@ twitch-videoad.js application/javascript
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -364,33 +386,12 @@ twitch-videoad.js application/javascript
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -418,6 +419,12 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -529,6 +536,7 @@ twitch-videoad.js application/javascript
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -860,6 +868,7 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -893,9 +902,22 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -936,6 +958,11 @@ twitch-videoad.js application/javascript
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -963,11 +990,11 @@ twitch-videoad.js application/javascript
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @name TwitchAdSolutions (mute-black)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/mute-black/mute-black.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/mute-black/mute-black.user.js
|
||||
// @description Multiple solutions for blocking Twitch ads (mute-black)
|
||||
@ -60,8 +60,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -93,6 +91,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -132,6 +131,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -207,7 +209,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -263,20 +265,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -325,8 +349,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -375,33 +397,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -429,6 +430,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -540,6 +547,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -871,6 +879,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -904,9 +913,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -947,6 +969,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -974,11 +1001,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -49,8 +49,6 @@ twitch-videoad.js application/javascript
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -82,6 +80,7 @@ twitch-videoad.js application/javascript
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -121,6 +120,9 @@ twitch-videoad.js application/javascript
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -196,7 +198,7 @@ twitch-videoad.js application/javascript
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -252,20 +254,42 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -314,8 +338,6 @@ twitch-videoad.js application/javascript
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -364,33 +386,12 @@ twitch-videoad.js application/javascript
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -418,6 +419,12 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -529,6 +536,7 @@ twitch-videoad.js application/javascript
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -860,6 +868,7 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -893,9 +902,22 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -936,6 +958,11 @@ twitch-videoad.js application/javascript
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -963,11 +990,11 @@ twitch-videoad.js application/javascript
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @name TwitchAdSolutions (notify-reload)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-reload/notify-reload.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-reload/notify-reload.user.js
|
||||
// @description Multiple solutions for blocking Twitch ads (notify-reload)
|
||||
@ -60,8 +60,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -93,6 +91,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -132,6 +131,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -207,7 +209,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -263,20 +265,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -325,8 +349,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -375,33 +397,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -429,6 +430,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -540,6 +547,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -871,6 +879,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -904,9 +913,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -947,6 +969,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -974,11 +1001,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -49,8 +49,6 @@ twitch-videoad.js application/javascript
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -82,6 +80,7 @@ twitch-videoad.js application/javascript
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -121,6 +120,9 @@ twitch-videoad.js application/javascript
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -196,7 +198,7 @@ twitch-videoad.js application/javascript
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -252,20 +254,42 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -314,8 +338,6 @@ twitch-videoad.js application/javascript
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -364,33 +386,12 @@ twitch-videoad.js application/javascript
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -418,6 +419,12 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -529,6 +536,7 @@ twitch-videoad.js application/javascript
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -860,6 +868,7 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -893,9 +902,22 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -936,6 +958,11 @@ twitch-videoad.js application/javascript
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -963,11 +990,11 @@ twitch-videoad.js application/javascript
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @name TwitchAdSolutions (notify-strip-reload)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-strip-reload/notify-strip-reload.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-strip-reload/notify-strip-reload.user.js
|
||||
// @description Multiple solutions for blocking Twitch ads (notify-strip-reload)
|
||||
@ -60,8 +60,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -93,6 +91,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -132,6 +131,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -207,7 +209,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -263,20 +265,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -325,8 +349,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -375,33 +397,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -429,6 +430,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -540,6 +547,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -871,6 +879,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -904,9 +913,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -947,6 +969,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -974,11 +1001,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -49,8 +49,6 @@ twitch-videoad.js application/javascript
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -82,6 +80,7 @@ twitch-videoad.js application/javascript
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -121,6 +120,9 @@ twitch-videoad.js application/javascript
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -196,7 +198,7 @@ twitch-videoad.js application/javascript
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -252,20 +254,42 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -314,8 +338,6 @@ twitch-videoad.js application/javascript
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -364,33 +386,12 @@ twitch-videoad.js application/javascript
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -418,6 +419,12 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -529,6 +536,7 @@ twitch-videoad.js application/javascript
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -860,6 +868,7 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -893,9 +902,22 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -936,6 +958,11 @@ twitch-videoad.js application/javascript
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -963,11 +990,11 @@ twitch-videoad.js application/javascript
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @name TwitchAdSolutions (notify-strip)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-strip/notify-strip.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-strip/notify-strip.user.js
|
||||
// @description Multiple solutions for blocking Twitch ads (notify-strip)
|
||||
@ -60,8 +60,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -93,6 +91,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -132,6 +131,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -207,7 +209,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -263,20 +265,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -325,8 +349,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -375,33 +397,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -429,6 +430,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -540,6 +547,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -871,6 +879,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -904,9 +913,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -947,6 +969,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -974,11 +1001,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -49,8 +49,6 @@ twitch-videoad.js application/javascript
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -82,6 +80,7 @@ twitch-videoad.js application/javascript
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -121,6 +120,9 @@ twitch-videoad.js application/javascript
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -196,7 +198,7 @@ twitch-videoad.js application/javascript
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -252,20 +254,42 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -314,8 +338,6 @@ twitch-videoad.js application/javascript
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -364,33 +386,12 @@ twitch-videoad.js application/javascript
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -418,6 +419,12 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -529,6 +536,7 @@ twitch-videoad.js application/javascript
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -860,6 +868,7 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -893,9 +902,22 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -936,6 +958,11 @@ twitch-videoad.js application/javascript
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -963,11 +990,11 @@ twitch-videoad.js application/javascript
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @name TwitchAdSolutions (proxy-m3u8)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/proxy-m3u8/proxy-m3u8.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/proxy-m3u8/proxy-m3u8.user.js
|
||||
// @description Multiple solutions for blocking Twitch ads (proxy-m3u8)
|
||||
@ -60,8 +60,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -93,6 +91,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -132,6 +131,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -207,7 +209,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -263,20 +265,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -325,8 +349,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -375,33 +397,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -429,6 +430,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -540,6 +547,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -871,6 +879,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -904,9 +913,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -947,6 +969,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -974,11 +1001,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -49,8 +49,6 @@ twitch-videoad.js application/javascript
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -82,6 +80,7 @@ twitch-videoad.js application/javascript
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -121,6 +120,9 @@ twitch-videoad.js application/javascript
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -196,7 +198,7 @@ twitch-videoad.js application/javascript
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -252,20 +254,42 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -314,8 +338,6 @@ twitch-videoad.js application/javascript
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -364,33 +386,12 @@ twitch-videoad.js application/javascript
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -418,6 +419,12 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -529,6 +536,7 @@ twitch-videoad.js application/javascript
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -860,6 +868,7 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -893,9 +902,22 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -936,6 +958,11 @@ twitch-videoad.js application/javascript
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -963,11 +990,11 @@ twitch-videoad.js application/javascript
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @name TwitchAdSolutions (strip)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/strip/strip.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/strip/strip.user.js
|
||||
// @description Multiple solutions for blocking Twitch ads (strip)
|
||||
@ -60,8 +60,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -93,6 +91,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -132,6 +131,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -207,7 +209,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -263,20 +265,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -325,8 +349,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -375,33 +397,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -429,6 +430,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -540,6 +547,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -871,6 +879,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -904,9 +913,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -947,6 +969,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -974,11 +1001,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
4
utils.cs
4
utils.cs
@ -126,6 +126,10 @@ namespace TwitchAdUtils
|
||||
{
|
||||
modifiedOptions = true;
|
||||
}
|
||||
if (lineTrimmed.StartsWith("// @name "))
|
||||
{
|
||||
line = line += " (" + dirInfo.Name + ")";
|
||||
}
|
||||
if (lineTrimmed.StartsWith("// @description"))
|
||||
{
|
||||
string url = "https://github.com/pixeltris/TwitchAdSolutions/raw/master/" + dirInfo.Name + "/" + dirInfo.Name + suffixUserscript;
|
||||
|
@ -49,8 +49,6 @@ twitch-videoad.js application/javascript
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -82,6 +80,7 @@ twitch-videoad.js application/javascript
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -121,6 +120,9 @@ twitch-videoad.js application/javascript
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -196,7 +198,7 @@ twitch-videoad.js application/javascript
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -252,20 +254,42 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -314,8 +338,6 @@ twitch-videoad.js application/javascript
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -364,33 +386,12 @@ twitch-videoad.js application/javascript
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -418,6 +419,12 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -529,6 +536,7 @@ twitch-videoad.js application/javascript
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -860,6 +868,7 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -893,9 +902,22 @@ twitch-videoad.js application/javascript
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -936,6 +958,11 @@ twitch-videoad.js application/javascript
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -963,11 +990,11 @@ twitch-videoad.js application/javascript
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions
|
||||
// @name TwitchAdSolutions (video-swap)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 1.3
|
||||
// @version 1.4
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/video-swap/video-swap.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/video-swap/video-swap.user.js
|
||||
// @description Multiple solutions for blocking Twitch ads (video-swap)
|
||||
@ -60,8 +60,6 @@
|
||||
scope.StreamInfos = [];
|
||||
scope.StreamInfosByUrl = [];
|
||||
scope.CurrentChannelNameFromM3U8 = null;
|
||||
scope.LastAdUrl = null;
|
||||
scope.LastAdTime = 0;
|
||||
// Need this in both scopes. Window scope needs to update this to worker scope.
|
||||
scope.gql_device_id = null;
|
||||
}
|
||||
@ -93,6 +91,7 @@
|
||||
${processM3U8.toString()}
|
||||
${getSegmentInfos.toString()}
|
||||
${getSegmentInfosLines.toString()}
|
||||
${getFinalSegUrl.toString()}
|
||||
${hookWorkerFetch.toString()}
|
||||
${declareOptions.toString()}
|
||||
${getAccessToken.toString()}
|
||||
@ -132,6 +131,9 @@
|
||||
else if (e.data.key == 'UboReloadPlayer') {
|
||||
reloadTwitchPlayer();
|
||||
}
|
||||
else if (e.data.key == 'UboPauseResumePlayer') {
|
||||
reloadTwitchPlayer(true);
|
||||
}
|
||||
}
|
||||
function getAdDiv() {
|
||||
var playerRootDiv = document.querySelector('.video-player');
|
||||
@ -207,7 +209,7 @@
|
||||
if (i >= 2 && lines[i - 2].startsWith('#EXT-X-PROGRAM-DATE-TIME')) {
|
||||
segInfo.dateTimeLineIndex = i - 2;
|
||||
segInfo.dateTimeLine = lines[i - 2];
|
||||
segInfo.dateTime = new Date(lines[i - 2].split(':')[1]);
|
||||
segInfo.dateTime = new Date(lines[i - 2].substr(lines[i - 2].indexOf(':')));
|
||||
}
|
||||
result.segs.push(segInfo);
|
||||
}
|
||||
@ -263,20 +265,42 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function getFinalSegUrl(lines) {
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("http")) {
|
||||
return lines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function processM3U8(url, textStr, realFetch) {
|
||||
var haveAdTags = textStr.includes(AD_SIGNIFIER);
|
||||
if (OPT_MODE_STRIP_AD_SEGMENTS) {
|
||||
var si = StreamInfosByUrl[url];
|
||||
if (si != null) {
|
||||
si.BackupSeqNumber = -1;
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
||||
var oldRealSeq = si.RealSeqNumber;
|
||||
si.RealSeqNumber = parseInt(/#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(lines[i])[1]);
|
||||
if (!haveAdTags && si.FakeSeqNumber > 0) {
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better)
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
/*// We have some sequencing issues... for now lets pause/play and stop modifying sequence.
|
||||
// TODO: Improve sequencing (determine if the m3u8 urls have actually changed)
|
||||
si.FakeSeqNumber = 0;
|
||||
si.BackupSeqNumber = -1;
|
||||
postMessage({key:'UboPauseResumePlayer'});*/
|
||||
// We previously modified the sequence number, we need to keep doing so (alternatively pause/playing might work better
|
||||
var finalSegUrl = getFinalSegUrl(lines);
|
||||
if (finalSegUrl != si.FinalSegUrl) {
|
||||
si.FinalSegUrl = finalSegUrl;
|
||||
// TODO: Maybe only do the jump check if there was an ad recently? (within the last 5 m3u8 requests)
|
||||
var jump = Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
if (jump <= 3) {
|
||||
si.FakeSeqNumber += Math.max(0, si.RealSeqNumber - oldRealSeq);
|
||||
} else if (jump > 0) {
|
||||
si.FakeSeqNumber++;
|
||||
}
|
||||
}
|
||||
lines[i] = '#EXT-X-MEDIA-SEQUENCE:' + si.FakeSeqNumber;
|
||||
console.log('No ad, but modifying sequence realSeq:' + si.RealSeqNumber + ' fakeSeq:' + si.FakeSeqNumber);
|
||||
}
|
||||
@ -325,8 +349,6 @@
|
||||
return textStr;
|
||||
}
|
||||
if (haveAdTags) {
|
||||
LastAdUrl = url;
|
||||
LastAdTime = Date.now();
|
||||
var streamInfo = StreamInfosByUrl[url];
|
||||
if (streamInfo == null) {
|
||||
console.log('Unknown stream url ' + url);
|
||||
@ -375,33 +397,12 @@
|
||||
var lines = textStr.replace('\r', '').split('\n');
|
||||
var newLines = [];
|
||||
if (backupM3u8 != null) {
|
||||
var seqMatch = /#EXT-X-MEDIA-SEQUENCE:([0-9]*)/.exec(backupM3u8);
|
||||
if (seqMatch != null) {
|
||||
var oldBackupSeqNumber = streamInfo.BackupSeqNumber;
|
||||
streamInfo.BackupSeqNumber = Math.max(0, parseInt(seqMatch[1]));
|
||||
if (streamInfo.RealSeqNumber > 0) {
|
||||
// We already have a real stream, this must be a midroll. We should therefore increment rather than just using backup directly.
|
||||
// - If we don't do this then our sequence number will be broken and the stream will get stuck in a loading state.
|
||||
if (streamInfo.FakeSeqNumber == 0) {
|
||||
streamInfo.FakeSeqNumber = streamInfo.RealSeqNumber;
|
||||
}
|
||||
if (oldBackupSeqNumber == -1) {
|
||||
// First backup sequence, assume +1
|
||||
streamInfo.FakeSeqNumber++;
|
||||
}
|
||||
else {
|
||||
streamInfo.FakeSeqNumber += Math.max(0, streamInfo.BackupSeqNumber - oldBackupSeqNumber);
|
||||
}
|
||||
} else {
|
||||
streamInfo.FakeSeqNumber = streamInfo.BackupSeqNumber;
|
||||
}
|
||||
}
|
||||
var backupLines = backupM3u8.replace('\r', '').split('\n');
|
||||
var segInfos = getSegmentInfos(streamInfo, lines, backupLines);
|
||||
newLines.push('#EXTM3U');
|
||||
newLines.push('#EXT-X-VERSION:3');
|
||||
newLines.push('#EXT-X-TARGETDURATION:' + segInfos.backup.targetDuration);
|
||||
newLines.push('#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
newLines.push('');//#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber);
|
||||
// The following will could cause issues when we stop stripping segments
|
||||
//newLines.push('#EXT-X-TWITCH-ELAPSED-SECS:' + streamInfo.backup.elapsedSecs);
|
||||
//newLines.push('#EXT-X-TWITCH-TOTAL-SECS:' + streamInfo.backup.totalSecs);
|
||||
@ -429,6 +430,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
var finalSegUrl = getFinalSegUrl(newLines);
|
||||
if (finalSegUrl != streamInfo.FinalSegUrl) {
|
||||
streamInfo.FinalSegUrl = finalSegUrl;
|
||||
streamInfo.FakeSeqNumber++;// We might need something better than this for lager jumps in seq?
|
||||
}
|
||||
newLines[3] = '#EXT-X-MEDIA-SEQUENCE:' + streamInfo.FakeSeqNumber;
|
||||
if (pushedLiveSegs > 0 || pushedBackupSegs > 0) {
|
||||
console.log('liveSegs:' + pushedLiveSegs + ' backupSegs:' + pushedBackupSegs + ' prefetch:' + pushedPrefetchSegs + ' realSeq:' + streamInfo.RealSeqNumber + ' fakeSeq:' + streamInfo.FakeSeqNumber);
|
||||
} else {
|
||||
@ -540,6 +547,7 @@
|
||||
streamInfo.RealSeqNumber = -1;
|
||||
streamInfo.BackupSeqNumber = -1;
|
||||
streamInfo.FakeSeqNumber = 0;
|
||||
streamInfo.FinalSegUrl = null;
|
||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||
@ -871,6 +879,7 @@
|
||||
}
|
||||
}
|
||||
function pollForAds() {
|
||||
//console.log('pollForAds ' + new Date(Date.now()));
|
||||
//check ad by looking for text banner
|
||||
var adBanner = document.querySelectorAll("span.tw-c-text-overlay");
|
||||
var foundAd = false;
|
||||
@ -904,9 +913,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(pollForAds,100);
|
||||
//setTimeout(pollForAds,100);
|
||||
}
|
||||
function reloadTwitchPlayer() {
|
||||
function pollForAdsObserver() {
|
||||
pollForAds();
|
||||
var vids = document.getElementsByClassName('video-player');
|
||||
for (var i = 0; i < vids.length; i++) {
|
||||
var observer = new MutationObserver(pollForAds);
|
||||
observer.observe(vids[i], {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: false,
|
||||
characterData: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function reloadTwitchPlayer(isPausePlay) {
|
||||
// Taken from ttv-tools / ffz
|
||||
// https://github.com/Nerixyz/ttv-tools/blob/master/src/context/twitch-player.ts
|
||||
// https://github.com/FrankerFaceZ/FrankerFaceZ/blob/master/src/sites/twitch-twilight/modules/player.jsx
|
||||
@ -947,6 +969,11 @@
|
||||
if (player.paused) {
|
||||
return;
|
||||
}
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
return;
|
||||
}
|
||||
const sink = player.mediaSinkManager || (player.core ? player.core.mediaSinkManager : null);
|
||||
if (sink && sink.video && sink.video._ffz_compressor) {
|
||||
const video = sink.video;
|
||||
@ -974,11 +1001,11 @@
|
||||
var script = document.createElement('script');
|
||||
script.src = "https://cdn.jsdelivr.net/npm/hls.js@latest";
|
||||
script.onload = function() {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
pollForAds();
|
||||
pollForAdsObserver();
|
||||
}
|
||||
}
|
||||
hookFetch();
|
||||
|
Loading…
x
Reference in New Issue
Block a user