mirror of
https://github.com/pixeltris/TwitchAdSolutions.git
synced 2025-04-29 22:24:29 +02:00
Reduce number of web requests
This commit is contained in:
parent
9ce9071dbc
commit
4ef55e67a2
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name TwitchAdSolutions
|
// @name TwitchAdSolutions
|
||||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||||
// @version 1.5
|
// @version 1.6
|
||||||
// @description Multiple solutions for blocking Twitch ads
|
// @description Multiple solutions for blocking Twitch ads
|
||||||
// @author pixeltris
|
// @author pixeltris
|
||||||
// @match *://*.twitch.tv/*
|
// @match *://*.twitch.tv/*
|
||||||
@ -15,8 +15,6 @@
|
|||||||
scope.OPT_ROLLING_DEVICE_ID = false;
|
scope.OPT_ROLLING_DEVICE_ID = false;
|
||||||
scope.OPT_MODE_STRIP_AD_SEGMENTS = false;
|
scope.OPT_MODE_STRIP_AD_SEGMENTS = false;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED = false;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED = false;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST = false;
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION = 10000;// In milliseconds
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = false;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = false;
|
||||||
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
||||||
@ -154,24 +152,9 @@
|
|||||||
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
||||||
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
||||||
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST && streamInfo != null && !streamInfo.NotifyObservedNoAds) {
|
// Deferred to after backup obtained to reduce slowdown. Midrolls are futile.
|
||||||
var noAds = false;
|
if (OPT_MODE_NOTIFY_ADS_WATCHED && !streamInfo.IsMidroll && (streamInfo.BackupFailed || streamInfo.BackupUrl != null)) {
|
||||||
var encodingsM3u8Response = await realFetch(streamInfo.RootM3U8Url);
|
await tryNotifyAdsWatchedM3U8(textStr);
|
||||||
if (encodingsM3u8Response.status === 200) {
|
|
||||||
var encodingsM3u8 = await encodingsM3u8Response.text();
|
|
||||||
var streamM3u8Url = encodingsM3u8.match(/^https:.*\.m3u8$/m)[0];
|
|
||||||
var streamM3u8Response = await realFetch(streamM3u8Url);
|
|
||||||
if (streamM3u8Response.status == 200) {
|
|
||||||
noAds = (await tryNotifyAdsWatchedM3U8(await streamM3u8Response.text())) == 1;
|
|
||||||
console.log('Notify ad watched. Response has ads: ' + !noAds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (streamInfo.NotifyFirstTime == 0) {
|
|
||||||
streamInfo.NotifyFirstTime = Date.now();
|
|
||||||
}
|
|
||||||
if (noAds && !streamInfo.NotifyObservedNoAds && Date.now() >= streamInfo.NotifyFirstTime + OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION) {
|
|
||||||
streamInfo.NotifyObservedNoAds = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
postMessage({
|
postMessage({
|
||||||
key: 'UboFoundAdSegment',
|
key: 'UboFoundAdSegment',
|
||||||
@ -353,12 +336,6 @@
|
|||||||
streamInfo.BackupRegRes = null;
|
streamInfo.BackupRegRes = null;
|
||||||
streamInfo.IsMidroll = false;
|
streamInfo.IsMidroll = false;
|
||||||
streamInfo.HadAds = false;
|
streamInfo.HadAds = false;
|
||||||
streamInfo.NotifyFirstTime = 0;
|
|
||||||
streamInfo.NotifyObservedNoAds = false;
|
|
||||||
streamInfo.RealSeqNumber = -1;
|
|
||||||
streamInfo.BackupSeqNumber = -1;
|
|
||||||
streamInfo.FakeSeqNumber = 0;
|
|
||||||
streamInfo.FinalSegUrl = null;
|
|
||||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (var i = 0; i < lines.length; i++) {
|
||||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||||
@ -447,58 +424,63 @@
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
||||||
//console.log(streamM3u8);
|
try {
|
||||||
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
//console.log(streamM3u8);
|
||||||
return 1;
|
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
||||||
}
|
return 1;
|
||||||
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
}
|
||||||
if (matches.length > 1) {
|
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
||||||
const attrString = matches[1];
|
if (matches.length > 1) {
|
||||||
const attr = parseAttributes(attrString);
|
const attrString = matches[1];
|
||||||
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
const attr = parseAttributes(attrString);
|
||||||
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
||||||
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
||||||
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
||||||
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
||||||
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
||||||
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
||||||
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
||||||
const baseData = {
|
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
||||||
stitched: true,
|
const baseData = {
|
||||||
roll_type: rollType,
|
stitched: true,
|
||||||
player_mute: false,
|
roll_type: rollType,
|
||||||
player_volume: 0.5,
|
player_mute: false,
|
||||||
visible: true,
|
player_volume: 0.5,
|
||||||
};
|
visible: true,
|
||||||
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
};
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
||||||
// This is all that's actually required at the moment
|
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
// This is all that's actually required at the moment
|
||||||
} else {
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
const extendedData = {
|
} else {
|
||||||
...baseData,
|
const extendedData = {
|
||||||
ad_id: adId,
|
...baseData,
|
||||||
ad_position: podPosition,
|
ad_id: adId,
|
||||||
duration: 30,
|
ad_position: podPosition,
|
||||||
creative_id: creativeId,
|
duration: 30,
|
||||||
total_ads: podLength,
|
creative_id: creativeId,
|
||||||
order_id: orderId,
|
total_ads: podLength,
|
||||||
line_item_id: lineItemId,
|
order_id: orderId,
|
||||||
};
|
line_item_id: lineItemId,
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
};
|
||||||
for (let quartile = 0; quartile < 4; quartile++) {
|
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
||||||
await gqlRequest(
|
for (let quartile = 0; quartile < 4; quartile++) {
|
||||||
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
await gqlRequest(
|
||||||
...extendedData,
|
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
||||||
quartile: quartile + 1,
|
...extendedData,
|
||||||
})
|
quartile: quartile + 1,
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
}
|
}
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
function hookFetch() {
|
function hookFetch() {
|
||||||
var realFetch = window.fetch;
|
var realFetch = window.fetch;
|
||||||
|
@ -6,8 +6,6 @@ twitch-videoad.js application/javascript
|
|||||||
scope.OPT_ROLLING_DEVICE_ID = true;
|
scope.OPT_ROLLING_DEVICE_ID = true;
|
||||||
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST = false;
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION = 10000;// In milliseconds
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = true;
|
||||||
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
||||||
@ -145,24 +143,9 @@ twitch-videoad.js application/javascript
|
|||||||
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
||||||
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
||||||
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST && streamInfo != null && !streamInfo.NotifyObservedNoAds) {
|
// Deferred to after backup obtained to reduce slowdown. Midrolls are futile.
|
||||||
var noAds = false;
|
if (OPT_MODE_NOTIFY_ADS_WATCHED && !streamInfo.IsMidroll && (streamInfo.BackupFailed || streamInfo.BackupUrl != null)) {
|
||||||
var encodingsM3u8Response = await realFetch(streamInfo.RootM3U8Url);
|
await tryNotifyAdsWatchedM3U8(textStr);
|
||||||
if (encodingsM3u8Response.status === 200) {
|
|
||||||
var encodingsM3u8 = await encodingsM3u8Response.text();
|
|
||||||
var streamM3u8Url = encodingsM3u8.match(/^https:.*\.m3u8$/m)[0];
|
|
||||||
var streamM3u8Response = await realFetch(streamM3u8Url);
|
|
||||||
if (streamM3u8Response.status == 200) {
|
|
||||||
noAds = (await tryNotifyAdsWatchedM3U8(await streamM3u8Response.text())) == 1;
|
|
||||||
console.log('Notify ad watched. Response has ads: ' + !noAds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (streamInfo.NotifyFirstTime == 0) {
|
|
||||||
streamInfo.NotifyFirstTime = Date.now();
|
|
||||||
}
|
|
||||||
if (noAds && !streamInfo.NotifyObservedNoAds && Date.now() >= streamInfo.NotifyFirstTime + OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION) {
|
|
||||||
streamInfo.NotifyObservedNoAds = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
postMessage({
|
postMessage({
|
||||||
key: 'UboFoundAdSegment',
|
key: 'UboFoundAdSegment',
|
||||||
@ -344,12 +327,6 @@ twitch-videoad.js application/javascript
|
|||||||
streamInfo.BackupRegRes = null;
|
streamInfo.BackupRegRes = null;
|
||||||
streamInfo.IsMidroll = false;
|
streamInfo.IsMidroll = false;
|
||||||
streamInfo.HadAds = false;
|
streamInfo.HadAds = false;
|
||||||
streamInfo.NotifyFirstTime = 0;
|
|
||||||
streamInfo.NotifyObservedNoAds = false;
|
|
||||||
streamInfo.RealSeqNumber = -1;
|
|
||||||
streamInfo.BackupSeqNumber = -1;
|
|
||||||
streamInfo.FakeSeqNumber = 0;
|
|
||||||
streamInfo.FinalSegUrl = null;
|
|
||||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (var i = 0; i < lines.length; i++) {
|
||||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||||
@ -438,58 +415,63 @@ twitch-videoad.js application/javascript
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
||||||
//console.log(streamM3u8);
|
try {
|
||||||
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
//console.log(streamM3u8);
|
||||||
return 1;
|
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
||||||
}
|
return 1;
|
||||||
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
}
|
||||||
if (matches.length > 1) {
|
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
||||||
const attrString = matches[1];
|
if (matches.length > 1) {
|
||||||
const attr = parseAttributes(attrString);
|
const attrString = matches[1];
|
||||||
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
const attr = parseAttributes(attrString);
|
||||||
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
||||||
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
||||||
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
||||||
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
||||||
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
||||||
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
||||||
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
||||||
const baseData = {
|
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
||||||
stitched: true,
|
const baseData = {
|
||||||
roll_type: rollType,
|
stitched: true,
|
||||||
player_mute: false,
|
roll_type: rollType,
|
||||||
player_volume: 0.5,
|
player_mute: false,
|
||||||
visible: true,
|
player_volume: 0.5,
|
||||||
};
|
visible: true,
|
||||||
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
};
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
||||||
// This is all that's actually required at the moment
|
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
// This is all that's actually required at the moment
|
||||||
} else {
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
const extendedData = {
|
} else {
|
||||||
...baseData,
|
const extendedData = {
|
||||||
ad_id: adId,
|
...baseData,
|
||||||
ad_position: podPosition,
|
ad_id: adId,
|
||||||
duration: 30,
|
ad_position: podPosition,
|
||||||
creative_id: creativeId,
|
duration: 30,
|
||||||
total_ads: podLength,
|
creative_id: creativeId,
|
||||||
order_id: orderId,
|
total_ads: podLength,
|
||||||
line_item_id: lineItemId,
|
order_id: orderId,
|
||||||
};
|
line_item_id: lineItemId,
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
};
|
||||||
for (let quartile = 0; quartile < 4; quartile++) {
|
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
||||||
await gqlRequest(
|
for (let quartile = 0; quartile < 4; quartile++) {
|
||||||
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
await gqlRequest(
|
||||||
...extendedData,
|
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
||||||
quartile: quartile + 1,
|
...extendedData,
|
||||||
})
|
quartile: quartile + 1,
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
}
|
}
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
function hookFetch() {
|
function hookFetch() {
|
||||||
var realFetch = window.fetch;
|
var realFetch = window.fetch;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name TwitchAdSolutions (notify-reload)
|
// @name TwitchAdSolutions (notify-reload)
|
||||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||||
// @version 1.5
|
// @version 1.6
|
||||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-reload/notify-reload.user.js
|
// @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
|
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-reload/notify-reload.user.js
|
||||||
// @description Multiple solutions for blocking Twitch ads (notify-reload)
|
// @description Multiple solutions for blocking Twitch ads (notify-reload)
|
||||||
@ -17,8 +17,6 @@
|
|||||||
scope.OPT_ROLLING_DEVICE_ID = true;
|
scope.OPT_ROLLING_DEVICE_ID = true;
|
||||||
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST = false;
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION = 10000;// In milliseconds
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = true;
|
||||||
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
||||||
@ -156,24 +154,9 @@
|
|||||||
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
||||||
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
||||||
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST && streamInfo != null && !streamInfo.NotifyObservedNoAds) {
|
// Deferred to after backup obtained to reduce slowdown. Midrolls are futile.
|
||||||
var noAds = false;
|
if (OPT_MODE_NOTIFY_ADS_WATCHED && !streamInfo.IsMidroll && (streamInfo.BackupFailed || streamInfo.BackupUrl != null)) {
|
||||||
var encodingsM3u8Response = await realFetch(streamInfo.RootM3U8Url);
|
await tryNotifyAdsWatchedM3U8(textStr);
|
||||||
if (encodingsM3u8Response.status === 200) {
|
|
||||||
var encodingsM3u8 = await encodingsM3u8Response.text();
|
|
||||||
var streamM3u8Url = encodingsM3u8.match(/^https:.*\.m3u8$/m)[0];
|
|
||||||
var streamM3u8Response = await realFetch(streamM3u8Url);
|
|
||||||
if (streamM3u8Response.status == 200) {
|
|
||||||
noAds = (await tryNotifyAdsWatchedM3U8(await streamM3u8Response.text())) == 1;
|
|
||||||
console.log('Notify ad watched. Response has ads: ' + !noAds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (streamInfo.NotifyFirstTime == 0) {
|
|
||||||
streamInfo.NotifyFirstTime = Date.now();
|
|
||||||
}
|
|
||||||
if (noAds && !streamInfo.NotifyObservedNoAds && Date.now() >= streamInfo.NotifyFirstTime + OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION) {
|
|
||||||
streamInfo.NotifyObservedNoAds = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
postMessage({
|
postMessage({
|
||||||
key: 'UboFoundAdSegment',
|
key: 'UboFoundAdSegment',
|
||||||
@ -355,12 +338,6 @@
|
|||||||
streamInfo.BackupRegRes = null;
|
streamInfo.BackupRegRes = null;
|
||||||
streamInfo.IsMidroll = false;
|
streamInfo.IsMidroll = false;
|
||||||
streamInfo.HadAds = false;
|
streamInfo.HadAds = false;
|
||||||
streamInfo.NotifyFirstTime = 0;
|
|
||||||
streamInfo.NotifyObservedNoAds = false;
|
|
||||||
streamInfo.RealSeqNumber = -1;
|
|
||||||
streamInfo.BackupSeqNumber = -1;
|
|
||||||
streamInfo.FakeSeqNumber = 0;
|
|
||||||
streamInfo.FinalSegUrl = null;
|
|
||||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (var i = 0; i < lines.length; i++) {
|
||||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||||
@ -449,58 +426,63 @@
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
||||||
//console.log(streamM3u8);
|
try {
|
||||||
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
//console.log(streamM3u8);
|
||||||
return 1;
|
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
||||||
}
|
return 1;
|
||||||
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
}
|
||||||
if (matches.length > 1) {
|
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
||||||
const attrString = matches[1];
|
if (matches.length > 1) {
|
||||||
const attr = parseAttributes(attrString);
|
const attrString = matches[1];
|
||||||
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
const attr = parseAttributes(attrString);
|
||||||
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
||||||
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
||||||
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
||||||
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
||||||
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
||||||
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
||||||
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
||||||
const baseData = {
|
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
||||||
stitched: true,
|
const baseData = {
|
||||||
roll_type: rollType,
|
stitched: true,
|
||||||
player_mute: false,
|
roll_type: rollType,
|
||||||
player_volume: 0.5,
|
player_mute: false,
|
||||||
visible: true,
|
player_volume: 0.5,
|
||||||
};
|
visible: true,
|
||||||
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
};
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
||||||
// This is all that's actually required at the moment
|
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
// This is all that's actually required at the moment
|
||||||
} else {
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
const extendedData = {
|
} else {
|
||||||
...baseData,
|
const extendedData = {
|
||||||
ad_id: adId,
|
...baseData,
|
||||||
ad_position: podPosition,
|
ad_id: adId,
|
||||||
duration: 30,
|
ad_position: podPosition,
|
||||||
creative_id: creativeId,
|
duration: 30,
|
||||||
total_ads: podLength,
|
creative_id: creativeId,
|
||||||
order_id: orderId,
|
total_ads: podLength,
|
||||||
line_item_id: lineItemId,
|
order_id: orderId,
|
||||||
};
|
line_item_id: lineItemId,
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
};
|
||||||
for (let quartile = 0; quartile < 4; quartile++) {
|
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
||||||
await gqlRequest(
|
for (let quartile = 0; quartile < 4; quartile++) {
|
||||||
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
await gqlRequest(
|
||||||
...extendedData,
|
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
||||||
quartile: quartile + 1,
|
...extendedData,
|
||||||
})
|
quartile: quartile + 1,
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
}
|
}
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
function hookFetch() {
|
function hookFetch() {
|
||||||
var realFetch = window.fetch;
|
var realFetch = window.fetch;
|
||||||
|
@ -6,8 +6,6 @@ twitch-videoad.js application/javascript
|
|||||||
scope.OPT_ROLLING_DEVICE_ID = true;
|
scope.OPT_ROLLING_DEVICE_ID = true;
|
||||||
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST = true;
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION = 10000;// In milliseconds
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = false;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = false;
|
||||||
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
||||||
@ -145,24 +143,9 @@ twitch-videoad.js application/javascript
|
|||||||
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
||||||
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
||||||
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST && streamInfo != null && !streamInfo.NotifyObservedNoAds) {
|
// Deferred to after backup obtained to reduce slowdown. Midrolls are futile.
|
||||||
var noAds = false;
|
if (OPT_MODE_NOTIFY_ADS_WATCHED && !streamInfo.IsMidroll && (streamInfo.BackupFailed || streamInfo.BackupUrl != null)) {
|
||||||
var encodingsM3u8Response = await realFetch(streamInfo.RootM3U8Url);
|
await tryNotifyAdsWatchedM3U8(textStr);
|
||||||
if (encodingsM3u8Response.status === 200) {
|
|
||||||
var encodingsM3u8 = await encodingsM3u8Response.text();
|
|
||||||
var streamM3u8Url = encodingsM3u8.match(/^https:.*\.m3u8$/m)[0];
|
|
||||||
var streamM3u8Response = await realFetch(streamM3u8Url);
|
|
||||||
if (streamM3u8Response.status == 200) {
|
|
||||||
noAds = (await tryNotifyAdsWatchedM3U8(await streamM3u8Response.text())) == 1;
|
|
||||||
console.log('Notify ad watched. Response has ads: ' + !noAds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (streamInfo.NotifyFirstTime == 0) {
|
|
||||||
streamInfo.NotifyFirstTime = Date.now();
|
|
||||||
}
|
|
||||||
if (noAds && !streamInfo.NotifyObservedNoAds && Date.now() >= streamInfo.NotifyFirstTime + OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION) {
|
|
||||||
streamInfo.NotifyObservedNoAds = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
postMessage({
|
postMessage({
|
||||||
key: 'UboFoundAdSegment',
|
key: 'UboFoundAdSegment',
|
||||||
@ -344,12 +327,6 @@ twitch-videoad.js application/javascript
|
|||||||
streamInfo.BackupRegRes = null;
|
streamInfo.BackupRegRes = null;
|
||||||
streamInfo.IsMidroll = false;
|
streamInfo.IsMidroll = false;
|
||||||
streamInfo.HadAds = false;
|
streamInfo.HadAds = false;
|
||||||
streamInfo.NotifyFirstTime = 0;
|
|
||||||
streamInfo.NotifyObservedNoAds = false;
|
|
||||||
streamInfo.RealSeqNumber = -1;
|
|
||||||
streamInfo.BackupSeqNumber = -1;
|
|
||||||
streamInfo.FakeSeqNumber = 0;
|
|
||||||
streamInfo.FinalSegUrl = null;
|
|
||||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (var i = 0; i < lines.length; i++) {
|
||||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||||
@ -438,58 +415,63 @@ twitch-videoad.js application/javascript
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
||||||
//console.log(streamM3u8);
|
try {
|
||||||
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
//console.log(streamM3u8);
|
||||||
return 1;
|
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
||||||
}
|
return 1;
|
||||||
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
}
|
||||||
if (matches.length > 1) {
|
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
||||||
const attrString = matches[1];
|
if (matches.length > 1) {
|
||||||
const attr = parseAttributes(attrString);
|
const attrString = matches[1];
|
||||||
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
const attr = parseAttributes(attrString);
|
||||||
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
||||||
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
||||||
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
||||||
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
||||||
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
||||||
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
||||||
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
||||||
const baseData = {
|
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
||||||
stitched: true,
|
const baseData = {
|
||||||
roll_type: rollType,
|
stitched: true,
|
||||||
player_mute: false,
|
roll_type: rollType,
|
||||||
player_volume: 0.5,
|
player_mute: false,
|
||||||
visible: true,
|
player_volume: 0.5,
|
||||||
};
|
visible: true,
|
||||||
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
};
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
||||||
// This is all that's actually required at the moment
|
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
// This is all that's actually required at the moment
|
||||||
} else {
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
const extendedData = {
|
} else {
|
||||||
...baseData,
|
const extendedData = {
|
||||||
ad_id: adId,
|
...baseData,
|
||||||
ad_position: podPosition,
|
ad_id: adId,
|
||||||
duration: 30,
|
ad_position: podPosition,
|
||||||
creative_id: creativeId,
|
duration: 30,
|
||||||
total_ads: podLength,
|
creative_id: creativeId,
|
||||||
order_id: orderId,
|
total_ads: podLength,
|
||||||
line_item_id: lineItemId,
|
order_id: orderId,
|
||||||
};
|
line_item_id: lineItemId,
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
};
|
||||||
for (let quartile = 0; quartile < 4; quartile++) {
|
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
||||||
await gqlRequest(
|
for (let quartile = 0; quartile < 4; quartile++) {
|
||||||
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
await gqlRequest(
|
||||||
...extendedData,
|
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
||||||
quartile: quartile + 1,
|
...extendedData,
|
||||||
})
|
quartile: quartile + 1,
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
}
|
}
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
function hookFetch() {
|
function hookFetch() {
|
||||||
var realFetch = window.fetch;
|
var realFetch = window.fetch;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
OPT_ROLLING_DEVICE_ID true
|
OPT_ROLLING_DEVICE_ID true
|
||||||
OPT_MODE_STRIP_AD_SEGMENTS true
|
OPT_MODE_STRIP_AD_SEGMENTS true
|
||||||
OPT_MODE_NOTIFY_ADS_WATCHED true
|
OPT_MODE_NOTIFY_ADS_WATCHED true
|
||||||
OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST true
|
|
||||||
OPT_ACCESS_TOKEN_PLAYER_TYPE 'site'
|
OPT_ACCESS_TOKEN_PLAYER_TYPE 'site'
|
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name TwitchAdSolutions (notify-strip)
|
// @name TwitchAdSolutions (notify-strip)
|
||||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||||
// @version 1.5
|
// @version 1.6
|
||||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-strip/notify-strip.user.js
|
// @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
|
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/notify-strip/notify-strip.user.js
|
||||||
// @description Multiple solutions for blocking Twitch ads (notify-strip)
|
// @description Multiple solutions for blocking Twitch ads (notify-strip)
|
||||||
@ -17,8 +17,6 @@
|
|||||||
scope.OPT_ROLLING_DEVICE_ID = true;
|
scope.OPT_ROLLING_DEVICE_ID = true;
|
||||||
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
scope.OPT_MODE_STRIP_AD_SEGMENTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST = true;
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION = 10000;// In milliseconds
|
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS = true;
|
||||||
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = false;
|
scope.OPT_MODE_NOTIFY_ADS_WATCHED_RELOAD_PLAYER_ON_AD_SEGMENT = false;
|
||||||
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
scope.OPT_BACKUP_PLAYER_TYPE = 'picture-by-picture';//'picture-by-picture';'thunderdome';
|
||||||
@ -156,24 +154,9 @@
|
|||||||
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
streamInfo.IsMidroll = textStr.includes('"MIDROLL"') || textStr.includes('"midroll"');
|
||||||
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
postMessage({key:'UboShowAdBanner',isMidroll:streamInfo.IsMidroll});
|
||||||
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
// Notify ads "watched" TODO: Keep crafting these requests even after ad tags are gone as sometimes it stops too early.
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST && streamInfo != null && !streamInfo.NotifyObservedNoAds) {
|
// Deferred to after backup obtained to reduce slowdown. Midrolls are futile.
|
||||||
var noAds = false;
|
if (OPT_MODE_NOTIFY_ADS_WATCHED && !streamInfo.IsMidroll && (streamInfo.BackupFailed || streamInfo.BackupUrl != null)) {
|
||||||
var encodingsM3u8Response = await realFetch(streamInfo.RootM3U8Url);
|
await tryNotifyAdsWatchedM3U8(textStr);
|
||||||
if (encodingsM3u8Response.status === 200) {
|
|
||||||
var encodingsM3u8 = await encodingsM3u8Response.text();
|
|
||||||
var streamM3u8Url = encodingsM3u8.match(/^https:.*\.m3u8$/m)[0];
|
|
||||||
var streamM3u8Response = await realFetch(streamM3u8Url);
|
|
||||||
if (streamM3u8Response.status == 200) {
|
|
||||||
noAds = (await tryNotifyAdsWatchedM3U8(await streamM3u8Response.text())) == 1;
|
|
||||||
console.log('Notify ad watched. Response has ads: ' + !noAds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (streamInfo.NotifyFirstTime == 0) {
|
|
||||||
streamInfo.NotifyFirstTime = Date.now();
|
|
||||||
}
|
|
||||||
if (noAds && !streamInfo.NotifyObservedNoAds && Date.now() >= streamInfo.NotifyFirstTime + OPT_MODE_NOTIFY_ADS_WATCHED_PERSIST_EXPECTED_DURATION) {
|
|
||||||
streamInfo.NotifyObservedNoAds = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
postMessage({
|
postMessage({
|
||||||
key: 'UboFoundAdSegment',
|
key: 'UboFoundAdSegment',
|
||||||
@ -355,12 +338,6 @@
|
|||||||
streamInfo.BackupRegRes = null;
|
streamInfo.BackupRegRes = null;
|
||||||
streamInfo.IsMidroll = false;
|
streamInfo.IsMidroll = false;
|
||||||
streamInfo.HadAds = false;
|
streamInfo.HadAds = false;
|
||||||
streamInfo.NotifyFirstTime = 0;
|
|
||||||
streamInfo.NotifyObservedNoAds = false;
|
|
||||||
streamInfo.RealSeqNumber = -1;
|
|
||||||
streamInfo.BackupSeqNumber = -1;
|
|
||||||
streamInfo.FakeSeqNumber = 0;
|
|
||||||
streamInfo.FinalSegUrl = null;
|
|
||||||
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
var lines = encodingsM3u8.replace('\r', '').split('\n');
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (var i = 0; i < lines.length; i++) {
|
||||||
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
if (!lines[i].startsWith('#') && lines[i].includes('.m3u8')) {
|
||||||
@ -449,58 +426,63 @@
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
async function tryNotifyAdsWatchedM3U8(streamM3u8) {
|
||||||
//console.log(streamM3u8);
|
try {
|
||||||
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
//console.log(streamM3u8);
|
||||||
return 1;
|
if (!streamM3u8.includes(AD_SIGNIFIER)) {
|
||||||
}
|
return 1;
|
||||||
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
}
|
||||||
if (matches.length > 1) {
|
var matches = streamM3u8.match(/#EXT-X-DATERANGE:(ID="stitched-ad-[^\n]+)\n/);
|
||||||
const attrString = matches[1];
|
if (matches.length > 1) {
|
||||||
const attr = parseAttributes(attrString);
|
const attrString = matches[1];
|
||||||
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
const attr = parseAttributes(attrString);
|
||||||
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
var podLength = parseInt(attr['X-TV-TWITCH-AD-POD-LENGTH'] ? attr['X-TV-TWITCH-AD-POD-LENGTH'] : '1');
|
||||||
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
var podPosition = parseInt(attr['X-TV-TWITCH-AD-POD-POSITION'] ? attr['X-TV-TWITCH-AD-POD-POSITION'] : '0');
|
||||||
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
var radToken = attr['X-TV-TWITCH-AD-RADS-TOKEN'];
|
||||||
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
var lineItemId = attr['X-TV-TWITCH-AD-LINE-ITEM-ID'];
|
||||||
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
var orderId = attr['X-TV-TWITCH-AD-ORDER-ID'];
|
||||||
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
var creativeId = attr['X-TV-TWITCH-AD-CREATIVE-ID'];
|
||||||
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
var adId = attr['X-TV-TWITCH-AD-ADVERTISER-ID'];
|
||||||
const baseData = {
|
var rollType = attr['X-TV-TWITCH-AD-ROLL-TYPE'].toLowerCase();
|
||||||
stitched: true,
|
const baseData = {
|
||||||
roll_type: rollType,
|
stitched: true,
|
||||||
player_mute: false,
|
roll_type: rollType,
|
||||||
player_volume: 0.5,
|
player_mute: false,
|
||||||
visible: true,
|
player_volume: 0.5,
|
||||||
};
|
visible: true,
|
||||||
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
};
|
||||||
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
for (let podPosition = 0; podPosition < podLength; podPosition++) {
|
||||||
// This is all that's actually required at the moment
|
if (OPT_MODE_NOTIFY_ADS_WATCHED_MIN_REQUESTS) {
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
// This is all that's actually required at the moment
|
||||||
} else {
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
const extendedData = {
|
} else {
|
||||||
...baseData,
|
const extendedData = {
|
||||||
ad_id: adId,
|
...baseData,
|
||||||
ad_position: podPosition,
|
ad_id: adId,
|
||||||
duration: 30,
|
ad_position: podPosition,
|
||||||
creative_id: creativeId,
|
duration: 30,
|
||||||
total_ads: podLength,
|
creative_id: creativeId,
|
||||||
order_id: orderId,
|
total_ads: podLength,
|
||||||
line_item_id: lineItemId,
|
order_id: orderId,
|
||||||
};
|
line_item_id: lineItemId,
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
};
|
||||||
for (let quartile = 0; quartile < 4; quartile++) {
|
await gqlRequest(makeGraphQlPacket('video_ad_impression', radToken, extendedData));
|
||||||
await gqlRequest(
|
for (let quartile = 0; quartile < 4; quartile++) {
|
||||||
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
await gqlRequest(
|
||||||
...extendedData,
|
makeGraphQlPacket('video_ad_quartile_complete', radToken, {
|
||||||
quartile: quartile + 1,
|
...extendedData,
|
||||||
})
|
quartile: quartile + 1,
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
||||||
}
|
}
|
||||||
await gqlRequest(makeGraphQlPacket('video_ad_pod_complete', radToken, baseData));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
function hookFetch() {
|
function hookFetch() {
|
||||||
var realFetch = window.fetch;
|
var realFetch = window.fetch;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user