vaft buffering mitigation tweaks #492
And ad segment cache memory leak fix
This commit is contained in:
+33
-24
@@ -2,7 +2,7 @@ twitch-videoad.js text/javascript
|
||||
(function() {
|
||||
if ( /(^|\.)twitch\.tv$/.test(document.location.hostname) === false ) { return; }
|
||||
'use strict';
|
||||
const ourTwitchAdSolutionsVersion = 22;// Used to prevent conflicts with outdated versions of the scripts
|
||||
const ourTwitchAdSolutionsVersion = 23;// Used to prevent conflicts with outdated versions of the scripts
|
||||
if (typeof window.twitchAdSolutionsVersion !== 'undefined' && window.twitchAdSolutionsVersion >= ourTwitchAdSolutionsVersion) {
|
||||
console.log("skipping vaft as there's another script active. ourVersion:" + ourTwitchAdSolutionsVersion + " activeVersion:" + window.twitchAdSolutionsVersion);
|
||||
window.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
|
||||
@@ -23,7 +23,7 @@ twitch-videoad.js text/javascript
|
||||
scope.SkipPlayerReloadOnHevc = false;// If true this will skip player reload on streams which have 2k/4k quality (if you enable this and you use the 2k/4k quality setting you'll get error #4000 / #3000 / spinning wheel on chrome based browsers)
|
||||
scope.AlwaysReloadPlayerOnAd = false;// Always pause/play when entering/leaving ads
|
||||
scope.ReloadPlayerAfterAd = true;// After the ad finishes do a player reload instead of pause/play
|
||||
scope.PlayerReloadMinimalRequestsTime = 1500;
|
||||
scope.PlayerReloadMinimalRequestsTime = 1600;
|
||||
scope.PlayerReloadMinimalRequestsPlayerIndex = 2;//autoplay
|
||||
scope.HasTriggeredPlayerReload = false;
|
||||
scope.StreamInfos = [];
|
||||
@@ -35,11 +35,13 @@ twitch-videoad.js text/javascript
|
||||
scope.AuthorizationHeader = undefined;
|
||||
scope.SimulatedAdsDepth = 0;
|
||||
scope.PlayerBufferingFix = true;// If true this will pause/play the player when it gets stuck buffering
|
||||
scope.PlayerBufferingDelay = 500;// How often should we check the player state (in milliseconds)
|
||||
scope.PlayerBufferingDelay = 600;// How often should we check the player state (in milliseconds)
|
||||
scope.PlayerBufferingSameStateCount = 3;// How many times of seeing the same player state until we trigger pause/play (it will only trigger it one time until the player state changes again)
|
||||
scope.PlayerBufferingDangerZone = 1;// The buffering time left (in seconds) when we should ignore the players playback position in the player state check
|
||||
scope.PlayerBufferingDoPlayerReload = false;// If true this will do a player reload instead of pause/play (player reloading is better at fixing the playback issues but it takes slightly longer)
|
||||
scope.PlayerBufferingMinRepeatDelay = 5000;// Minimum delay (in milliseconds) between each pause/play (this is to avoid over pressing pause/play when there are genuine buffering problems)
|
||||
scope.PlayerBufferingMinRepeatDelay = 12000;// Minimum delay (in milliseconds) between each pause/play (this is to avoid over pressing pause/play when there are genuine buffering problems)
|
||||
scope.PlayerBufferingPrerollCheckEnabled = false;// Enable this if you're getting an immediate pause/play/reload as you open a stream (which is causing the stream to take longer to load). One problem with this being true is that it can cause the player to get stuck in some instances requiring the user to press pause/play
|
||||
scope.PlayerBufferingPrerollCheckOffset = 5;// How far the stream need to move before doing the buffering mitigation (depends on PlayerBufferingPrerollCheckEnabled being true)
|
||||
scope.V2API = false;
|
||||
scope.IsAdStrippingEnabled = true;
|
||||
scope.AdSegmentCache = new Map();
|
||||
@@ -410,7 +412,7 @@ twitch-videoad.js text/javascript
|
||||
streamInfo.NumStrippedAdSegments = 0;
|
||||
}
|
||||
streamInfo.IsStrippingAdSegments = hasStrippedAdSegments;
|
||||
AdSegmentCache.forEach((key, value, map) => {
|
||||
AdSegmentCache.forEach((value, key, map) => {
|
||||
if (value < Date.now() - 120000) {
|
||||
map.delete(key);
|
||||
}
|
||||
@@ -641,6 +643,7 @@ twitch-videoad.js text/javascript
|
||||
}
|
||||
function gqlRequest(body, playerType) {
|
||||
if (!GQLDeviceID) {
|
||||
GQLDeviceID = '';
|
||||
const dcharacters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const dcharactersLength = dcharacters.length;
|
||||
for (let i = 0; i < 32; i++) {
|
||||
@@ -696,29 +699,33 @@ twitch-videoad.js text/javascript
|
||||
const position = player.core?.state?.position;
|
||||
const bufferedPosition = player.core?.state?.bufferedPosition;
|
||||
const bufferDuration = player.getBufferDuration();
|
||||
//console.log('position:' + position + ' bufferDuration:' + bufferDuration + ' bufferPosition:' + bufferedPosition);
|
||||
// NOTE: This could be improved. It currently lets the player fully eat the full buffer before it triggers pause/play
|
||||
if (position > 5 &&// changed from >0 to >5 due to issues with prerolls. TODO: Improve this, player could get stuck
|
||||
(playerBufferState.position == position || bufferDuration < PlayerBufferingDangerZone) &&
|
||||
playerBufferState.bufferedPosition == bufferedPosition &&
|
||||
playerBufferState.bufferDuration >= bufferDuration &&
|
||||
(position != 0 || bufferedPosition != 0 || bufferDuration != 0)
|
||||
) {
|
||||
playerBufferState.numSame++;
|
||||
if (playerBufferState.numSame == PlayerBufferingSameStateCount) {
|
||||
console.log('Attempt to fix buffering position:' + playerBufferState.position + ' bufferedPosition:' + playerBufferState.bufferedPosition + ' bufferDuration:' + playerBufferState.bufferDuration);
|
||||
doTwitchPlayerTask(!PlayerBufferingDoPlayerReload, PlayerBufferingDoPlayerReload, false);
|
||||
const isPausePlay = !PlayerBufferingDoPlayerReload;
|
||||
const isReload = PlayerBufferingDoPlayerReload;
|
||||
doTwitchPlayerTask(isPausePlay, isReload);
|
||||
playerBufferState.lastFixTime = Date.now();
|
||||
if (position !== undefined && bufferedPosition !== undefined) {
|
||||
//console.log('position:' + position + ' bufferDuration:' + bufferDuration + ' bufferPosition:' + bufferedPosition);
|
||||
// NOTE: This could be improved. It currently lets the player fully eat the full buffer before it triggers pause/play
|
||||
if ((!PlayerBufferingPrerollCheckEnabled || position > PlayerBufferingPrerollCheckOffset) &&
|
||||
(playerBufferState.position == position || bufferDuration < PlayerBufferingDangerZone) &&
|
||||
playerBufferState.bufferedPosition == bufferedPosition &&
|
||||
playerBufferState.bufferDuration >= bufferDuration &&
|
||||
(position != 0 || bufferedPosition != 0 || bufferDuration != 0)
|
||||
) {
|
||||
playerBufferState.numSame++;
|
||||
if (playerBufferState.numSame == PlayerBufferingSameStateCount) {
|
||||
console.log('Attempt to fix buffering position:' + playerBufferState.position + ' bufferedPosition:' + playerBufferState.bufferedPosition + ' bufferDuration:' + playerBufferState.bufferDuration);
|
||||
const isPausePlay = !PlayerBufferingDoPlayerReload;
|
||||
const isReload = PlayerBufferingDoPlayerReload;
|
||||
doTwitchPlayerTask(isPausePlay, isReload);
|
||||
playerBufferState.lastFixTime = Date.now();
|
||||
playerBufferState.numSame = 0;
|
||||
}
|
||||
} else {
|
||||
playerBufferState.numSame = 0;
|
||||
}
|
||||
playerBufferState.position = position;
|
||||
playerBufferState.bufferedPosition = bufferedPosition;
|
||||
playerBufferState.bufferDuration = bufferDuration;
|
||||
} else {
|
||||
playerBufferState.numSame = 0;
|
||||
}
|
||||
playerBufferState.position = position;
|
||||
playerBufferState.bufferedPosition = bufferedPosition;
|
||||
playerBufferState.bufferDuration = bufferDuration;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('error when monitoring player for buffering: ' + err);
|
||||
@@ -826,6 +833,8 @@ twitch-videoad.js text/javascript
|
||||
if (player.isPaused() || player.core?.paused) {
|
||||
return;
|
||||
}
|
||||
playerBufferState.lastFixTime = Date.now();
|
||||
playerBufferState.numSame = 0;
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
|
||||
+34
-25
@@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name TwitchAdSolutions (vaft)
|
||||
// @namespace https://github.com/pixeltris/TwitchAdSolutions
|
||||
// @version 35.0.0
|
||||
// @version 36.0.0
|
||||
// @description Multiple solutions for blocking Twitch ads (vaft)
|
||||
// @updateURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js
|
||||
// @downloadURL https://github.com/pixeltris/TwitchAdSolutions/raw/master/vaft/vaft.user.js
|
||||
@@ -13,7 +13,7 @@
|
||||
// ==/UserScript==
|
||||
(function() {
|
||||
'use strict';
|
||||
const ourTwitchAdSolutionsVersion = 22;// Used to prevent conflicts with outdated versions of the scripts
|
||||
const ourTwitchAdSolutionsVersion = 23;// Used to prevent conflicts with outdated versions of the scripts
|
||||
if (typeof window.twitchAdSolutionsVersion !== 'undefined' && window.twitchAdSolutionsVersion >= ourTwitchAdSolutionsVersion) {
|
||||
console.log("skipping vaft as there's another script active. ourVersion:" + ourTwitchAdSolutionsVersion + " activeVersion:" + window.twitchAdSolutionsVersion);
|
||||
window.twitchAdSolutionsVersion = ourTwitchAdSolutionsVersion;
|
||||
@@ -34,7 +34,7 @@
|
||||
scope.SkipPlayerReloadOnHevc = false;// If true this will skip player reload on streams which have 2k/4k quality (if you enable this and you use the 2k/4k quality setting you'll get error #4000 / #3000 / spinning wheel on chrome based browsers)
|
||||
scope.AlwaysReloadPlayerOnAd = false;// Always pause/play when entering/leaving ads
|
||||
scope.ReloadPlayerAfterAd = true;// After the ad finishes do a player reload instead of pause/play
|
||||
scope.PlayerReloadMinimalRequestsTime = 1500;
|
||||
scope.PlayerReloadMinimalRequestsTime = 1600;
|
||||
scope.PlayerReloadMinimalRequestsPlayerIndex = 2;//autoplay
|
||||
scope.HasTriggeredPlayerReload = false;
|
||||
scope.StreamInfos = [];
|
||||
@@ -46,11 +46,13 @@
|
||||
scope.AuthorizationHeader = undefined;
|
||||
scope.SimulatedAdsDepth = 0;
|
||||
scope.PlayerBufferingFix = true;// If true this will pause/play the player when it gets stuck buffering
|
||||
scope.PlayerBufferingDelay = 500;// How often should we check the player state (in milliseconds)
|
||||
scope.PlayerBufferingDelay = 600;// How often should we check the player state (in milliseconds)
|
||||
scope.PlayerBufferingSameStateCount = 3;// How many times of seeing the same player state until we trigger pause/play (it will only trigger it one time until the player state changes again)
|
||||
scope.PlayerBufferingDangerZone = 1;// The buffering time left (in seconds) when we should ignore the players playback position in the player state check
|
||||
scope.PlayerBufferingDoPlayerReload = false;// If true this will do a player reload instead of pause/play (player reloading is better at fixing the playback issues but it takes slightly longer)
|
||||
scope.PlayerBufferingMinRepeatDelay = 5000;// Minimum delay (in milliseconds) between each pause/play (this is to avoid over pressing pause/play when there are genuine buffering problems)
|
||||
scope.PlayerBufferingMinRepeatDelay = 12000;// Minimum delay (in milliseconds) between each pause/play (this is to avoid over pressing pause/play when there are genuine buffering problems)
|
||||
scope.PlayerBufferingPrerollCheckEnabled = false;// Enable this if you're getting an immediate pause/play/reload as you open a stream (which is causing the stream to take longer to load). One problem with this being true is that it can cause the player to get stuck in some instances requiring the user to press pause/play
|
||||
scope.PlayerBufferingPrerollCheckOffset = 5;// How far the stream need to move before doing the buffering mitigation (depends on PlayerBufferingPrerollCheckEnabled being true)
|
||||
scope.V2API = false;
|
||||
scope.IsAdStrippingEnabled = true;
|
||||
scope.AdSegmentCache = new Map();
|
||||
@@ -421,7 +423,7 @@
|
||||
streamInfo.NumStrippedAdSegments = 0;
|
||||
}
|
||||
streamInfo.IsStrippingAdSegments = hasStrippedAdSegments;
|
||||
AdSegmentCache.forEach((key, value, map) => {
|
||||
AdSegmentCache.forEach((value, key, map) => {
|
||||
if (value < Date.now() - 120000) {
|
||||
map.delete(key);
|
||||
}
|
||||
@@ -652,6 +654,7 @@
|
||||
}
|
||||
function gqlRequest(body, playerType) {
|
||||
if (!GQLDeviceID) {
|
||||
GQLDeviceID = '';
|
||||
const dcharacters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const dcharactersLength = dcharacters.length;
|
||||
for (let i = 0; i < 32; i++) {
|
||||
@@ -707,29 +710,33 @@
|
||||
const position = player.core?.state?.position;
|
||||
const bufferedPosition = player.core?.state?.bufferedPosition;
|
||||
const bufferDuration = player.getBufferDuration();
|
||||
//console.log('position:' + position + ' bufferDuration:' + bufferDuration + ' bufferPosition:' + bufferedPosition);
|
||||
// NOTE: This could be improved. It currently lets the player fully eat the full buffer before it triggers pause/play
|
||||
if (position > 5 &&// changed from >0 to >5 due to issues with prerolls. TODO: Improve this, player could get stuck
|
||||
(playerBufferState.position == position || bufferDuration < PlayerBufferingDangerZone) &&
|
||||
playerBufferState.bufferedPosition == bufferedPosition &&
|
||||
playerBufferState.bufferDuration >= bufferDuration &&
|
||||
(position != 0 || bufferedPosition != 0 || bufferDuration != 0)
|
||||
) {
|
||||
playerBufferState.numSame++;
|
||||
if (playerBufferState.numSame == PlayerBufferingSameStateCount) {
|
||||
console.log('Attempt to fix buffering position:' + playerBufferState.position + ' bufferedPosition:' + playerBufferState.bufferedPosition + ' bufferDuration:' + playerBufferState.bufferDuration);
|
||||
doTwitchPlayerTask(!PlayerBufferingDoPlayerReload, PlayerBufferingDoPlayerReload, false);
|
||||
const isPausePlay = !PlayerBufferingDoPlayerReload;
|
||||
const isReload = PlayerBufferingDoPlayerReload;
|
||||
doTwitchPlayerTask(isPausePlay, isReload);
|
||||
playerBufferState.lastFixTime = Date.now();
|
||||
if (position !== undefined && bufferedPosition !== undefined) {
|
||||
//console.log('position:' + position + ' bufferDuration:' + bufferDuration + ' bufferPosition:' + bufferedPosition);
|
||||
// NOTE: This could be improved. It currently lets the player fully eat the full buffer before it triggers pause/play
|
||||
if ((!PlayerBufferingPrerollCheckEnabled || position > PlayerBufferingPrerollCheckOffset) &&
|
||||
(playerBufferState.position == position || bufferDuration < PlayerBufferingDangerZone) &&
|
||||
playerBufferState.bufferedPosition == bufferedPosition &&
|
||||
playerBufferState.bufferDuration >= bufferDuration &&
|
||||
(position != 0 || bufferedPosition != 0 || bufferDuration != 0)
|
||||
) {
|
||||
playerBufferState.numSame++;
|
||||
if (playerBufferState.numSame == PlayerBufferingSameStateCount) {
|
||||
console.log('Attempt to fix buffering position:' + playerBufferState.position + ' bufferedPosition:' + playerBufferState.bufferedPosition + ' bufferDuration:' + playerBufferState.bufferDuration);
|
||||
const isPausePlay = !PlayerBufferingDoPlayerReload;
|
||||
const isReload = PlayerBufferingDoPlayerReload;
|
||||
doTwitchPlayerTask(isPausePlay, isReload);
|
||||
playerBufferState.lastFixTime = Date.now();
|
||||
playerBufferState.numSame = 0;
|
||||
}
|
||||
} else {
|
||||
playerBufferState.numSame = 0;
|
||||
}
|
||||
playerBufferState.position = position;
|
||||
playerBufferState.bufferedPosition = bufferedPosition;
|
||||
playerBufferState.bufferDuration = bufferDuration;
|
||||
} else {
|
||||
playerBufferState.numSame = 0;
|
||||
}
|
||||
playerBufferState.position = position;
|
||||
playerBufferState.bufferedPosition = bufferedPosition;
|
||||
playerBufferState.bufferDuration = bufferDuration;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('error when monitoring player for buffering: ' + err);
|
||||
@@ -837,6 +844,8 @@
|
||||
if (player.isPaused() || player.core?.paused) {
|
||||
return;
|
||||
}
|
||||
playerBufferState.lastFixTime = Date.now();
|
||||
playerBufferState.numSame = 0;
|
||||
if (isPausePlay) {
|
||||
player.pause();
|
||||
player.play();
|
||||
|
||||
Reference in New Issue
Block a user