// ==UserScript==
// @name YouTube: Make AutoPlay Next More Than 3 seconds
// @namespace UserScripts
// @match https://www.youtube.com/*
// @version 0.2.9
// @author CY Fung
// @license MIT
// @description To make AutoPlay Next Duration longer
// @grant none
// @run-at document-start
// @unwrap
// @inject-into page
// ==/UserScript==
(() => {
const second_to_play_next_def = 8;
const valuer = {
get second_to_play_next() {
return +localStorage.second_to_play_next || second_to_play_next_def;
}
}
const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);
const Promise = (async () => { })().constructor;
/*
*
, $mb = function(a, b) {
b = void 0 === b ? -1 : b;
a = a.j.Ha("ytp-autonav-endscreen-upnext-header");
g.of(a);
if (0 <= b) {
b = String(b);
var c = "$SECONDS \u79d2\u5f8c\u306b\u6b21\u306e\u52d5\u753b\u3092\u518d\u751f".match(RegExp("\\$SECONDS", "gi"))[0]
, d = "$SECONDS \u79d2\u5f8c\u306b\u6b21\u306e\u52d5\u753b\u3092\u518d\u751f".indexOf(c);
if (0 <= d) {
a.appendChild(g.mf("$SECONDS \u79d2\u5f8c\u306b\u6b21\u306e\u52d5\u753b\u3092\u518d\u751f".slice(0, d)));
var e = g.lf("span");
g.$t(e, "ytp-autonav-endscreen-upnext-header-countdown-number");
g.Bf(e, b);
a.appendChild(e);
a.appendChild(g.mf("$SECONDS \u79d2\u5f8c\u306b\u6b21\u306e\u52d5\u753b\u3092\u518d\u751f".slice(d + c.length)));
return
}
}
g.Bf(a, "\u6b21\u306e\u52d5\u753b")
}
*/
/*
*
if (!a.Qk()) {
a.J.Bf() ? $mb(a, Math.round(anb(a) / 1E3)) : $mb(a);
b = !!a.suggestion && !!a.suggestion.Bs;
var c = a.J.Bf() || !b;
g.fu(a.container.element, "ytp-autonav-endscreen-upnext-alternative-header-only", !c && b);
g.fu(a.container.element, "ytp-autonav-endscreen-upnext-no-alternative-header", c && !b);
g.FG(a.B, a.J.Bf());
g.fu(a.element, "ytp-enable-w2w-color-transitions", bnb(a))
}
*/
/*
*
*
, anb = function(a) {
if (a.J.isFullscreen()) {
var b;
a = null == (b = a.J.getVideoData()) ? void 0 : b.CB;
return -1 === a || void 0 === a ? 8E3 : a
}
return 0 <= a.J.Ss() ? a.J.Ss() : g.WJ(a.J.W().experiments, "autoplay_time") || 1E4
}
*/
// a.J instanceof g.AU
/*
*
*
g.AU {Dp: false, xk: undefined, app: g.b1, state: WQa, playerType: undefined, …}
Dp: false
UK: pYa {Dp: false, xk: Array(6), Ub: {…}, Td: {…}, element: div#ytp-id-18.ytp-popup.ytp-settings-menu, …}
app: g.b1 {Dp: false, xk: Array(21), logger: g.pY, di: false, bA: false, …}
element: null
j: false
playerType: undefined
state: WQa {Dp: false, xk: undefined, D: Set(89), G: {…}, S: {…}, …}
xk: undefined
*/
// a.J.Ss = ƒ (){return this.app.Ss()}
// a.J.app.Ss = ƒ (){return this.getVideoData().aU}
// a.J.app.getVideoData() = ƒ (){return this.Qb.getVideoData()} = object instanceof g.sT
// a.J.app.Qb.getVideoData = ƒ (){return this.videoData}
// g.sT.prototype
/*
*
getAudioTrack
getAvailableAudioTracks
getHeartbeatResponse
getPlayerResponse
getPlaylistSequenceForTime
getStoryboardFormat
hasProgressBarBoundaries
hasSupportedAudio51Tracks
isAd
isDaiEnabled
isLoaded
isOtf
useInnertubeDrmService
*/
const getsT = (_yt_player) => {
const w = 'sT';
let arr = [];
for (const [k, v] of Object.entries(_yt_player)) {
const p = typeof v === 'function' ? v.prototype : 0;
if (p) {
let q = 0;
if (typeof p.isLoaded === 'function' && p.isLoaded.length === 0) q += 200;
if (q < 200) continue; // p.isLoaded is required
if (typeof p.hasSupportedAudio51Tracks === 'function' && p.hasSupportedAudio51Tracks.length === 0) q += 2;
if (typeof p.getStoryboardFormat === 'function' && p.getStoryboardFormat.length === 0) q += 4;
if (typeof p.getPlaylistSequenceForTime === 'function' && p.getPlaylistSequenceForTime.length === 1) q += 4;
if (typeof p.isOtf === 'function' && p.isOtf.length === 0) q += 2;
if (typeof p.getAvailableAudioTracks === 'function' && p.getAvailableAudioTracks.length === 0) q += 4;
if (typeof p.getAudioTrack === 'function' && p.getAudioTrack.length === 0) q += 4;
if (typeof p.getPlayerResponse === 'function' && p.getPlayerResponse.length === 0) q += 2;
if (typeof p.getHeartbeatResponse === 'function' && p.getHeartbeatResponse.length === 0) q += 2;
if (typeof p.isAd === 'function' && p.isAd.length === 0) q += 2;
if (typeof p.isDaiEnabled === 'function' && p.isDaiEnabled.length === 1) q += 2;
if (typeof p.useInnertubeDrmService === 'function' && p.useInnertubeDrmService.length === 0) q++;
if (typeof p.hasProgressBarBoundaries === 'function' && p.hasProgressBarBoundaries.length === 0) q += 2;
if (q > 0) arr.push([k, q]);
}
}
if (arr.length === 0) {
console.warn(`Key does not exist. [${w}]`);
} else {
if (arr.length > 1) arr.sort((a, b) => b[1] - a[1]);
console.log(`[${w}]`, arr);
return arr[0][0];
}
}
// const addProtoToArr = (parent, key, arr) => {
// let isChildProto = false;
// for (const sr of arr) {
// if (parent[key].prototype instanceof parent[sr]) {
// isChildProto = true;
// break;
// }
// }
// if (isChildProto) return;
// arr = arr.filter(sr => {
// if (parent[sr].prototype instanceof parent[key]) {
// return false;
// }
// return true;
// });
// arr.push(key);
// return arr;
// }
// const getAU = (_yt_player) => {
// const w = 'VG';
// let arr = [];
// for (const [k, v] of Object.entries(_yt_player)) {
// const p = typeof v === 'function' ? v.prototype : 0;
// if (p
// && typeof p.show === 'function' && p.show.length === 1
// && typeof p.hide === 'function' && p.hide.length === 0
// && typeof p.stop === 'function' && p.stop.length === 0) {
// arr = addProtoToArr(_yt_player, k, arr) || arr;
// }
// }
// if (arr.length === 0) {
// console.warn(`Key does not exist. [${w}]`);
// } else {
// console.log(`[${w}]`, arr);
// return arr[0];
// }
// }
const cleanContext = async (win) => {
const waitFn = requestAnimationFrame; // shall have been binded to window
try {
let mx = 16; // MAX TRIAL
const frameId = 'vanillajs-iframe-v1';
/** @type {HTMLIFrameElement | null} */
let frame = document.getElementById(frameId);
let removeIframeFn = null;
if (!frame) {
frame = document.createElement('iframe');
frame.id = frameId;
const blobURL = typeof webkitCancelAnimationFrame === 'function' && typeof kagi === 'undefined' ? (frame.src = URL.createObjectURL(new Blob([], { type: 'text/html' }))) : null; // avoid Brave Crash
frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
n.appendChild(frame);
while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
const root = document.documentElement;
root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
if (blobURL) Promise.resolve().then(() => URL.revokeObjectURL(blobURL));
removeIframeFn = (setTimeout) => {
const removeIframeOnDocumentReady = (e) => {
e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
win = null;
const m = n;
n = null;
setTimeout(() => m.remove(), 200);
}
if (document.readyState !== 'loading') {
removeIframeOnDocumentReady();
} else {
win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
}
}
}
while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
const fc = frame.contentWindow;
if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
const { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle } = fc;
const res = { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle };
for (let k in res) res[k] = res[k].bind(win); // necessary
if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
res.animate = fc.HTMLElement.prototype.animate;
return res;
} catch (e) {
console.warn(e);
return null;
}
};
cleanContext(window).then(__CONTEXT__ => {
if (!__CONTEXT__) return null;
const { setTimeout } = __CONTEXT__;
const isUrlInEmbed = location.href.includes('.youtube.com/embed/');
const isAbortSignalSupported = typeof AbortSignal !== "undefined";
const promiseForTamerTimeout = new Promise(resolve => {
!isUrlInEmbed && isAbortSignalSupported && document.addEventListener('yt-action', function () {
setTimeout(resolve, 480);
}, { capture: true, passive: true, once: true });
!isUrlInEmbed && isAbortSignalSupported && typeof customElements === "object" && customElements.whenDefined('ytd-app').then(() => {
setTimeout(resolve, 1200);
});
setTimeout(resolve, 3000);
});
const PromiseExternal = ((resolve_, reject_) => {
const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject };
return class PromiseExternal extends Promise {
constructor(cb = h) {
super(cb);
if (cb === h) {
/** @type {(value: any) => void} */
this.resolve = resolve_;
/** @type {(reason?: any) => void} */
this.reject = reject_;
}
}
};
})();
(async () => {
const autoplayRendererP = new PromiseExternal();
const instReadyP = new PromiseExternal();
let bSetupDone = false;
const observablePromise = (proc, timeoutPromise) => {
let promise = null;
return {
obtain() {
if (!promise) {
promise = new Promise(resolve => {
let mo = null;
const f = () => {
let t = proc();
if (t) {
mo.disconnect();
mo.takeRecords();
mo = null;
resolve(t);
}
}
mo = new MutationObserver(f);
mo.observe(document, { subtree: true, childList: true })
f();
timeoutPromise && timeoutPromise.then(() => {
resolve(null)
});
});
}
return promise
}
}
}
if (isAbortSignalSupported && !isUrlInEmbed) {
await customElements.whenDefined('ytd-player');
}
const _yt_player = await observablePromise(() => {
return (((window || 0)._yt_player || 0) || 0);
}, promiseForTamerTimeout).obtain();
if (!_yt_player || typeof _yt_player !== 'object') return;
// store and control variables
const pm = new Proxy({}, {
set(target, prop, value, receiver) {
let old = target[prop];
if (old !== value) {
target[prop] = old = value;
if (prop === 'instsT') value && objProceed(true);
}
}
});
window.set_second_to_play_next = function (sec) {
if (!arguments.length) return +localStorage.second_to_play_next;
localStorage.second_to_play_next = `${sec}`;
assignDurationToOAR(pm.playerOAR);
console.log('The value will be applied to the next video');
}
const objProceed = (toResolveInstSetPromise) => {
const resAssigned = obtainOAR();
if (resAssigned) bSetupDone ? assignDurationToOAR(pm.playerOAR) : autoplayRendererP.resolve();
if (toResolveInstSetPromise) instReadyP.resolve();
}
const assignDurationToOAR = (playerOAR) => {
if (!playerOAR) return;
const t4 = playerOAR.countDownSecsForFullscreen;
const t5 = playerOAR.countDownSecs;
let b = t4 === t5
const second_to_play_next = valuer.second_to_play_next;
playerOAR.countDownSecsForFullscreen = second_to_play_next;
if (b) playerOAR.countDownSecs = second_to_play_next;
return b;
}
Promise.all([autoplayRendererP, instReadyP]).then(() => {
const inst = pm.instsT;
const playerOAR = pm.playerOAR;
if (!inst || !playerOAR) return;
let entriesA = Object.entries(inst).filter(e => typeof e[1] === 'number');
let m = new Map();
for (const entry of entriesA) {
m.set(entry[0], entry[1]);
}
const second_to_play_next = valuer.second_to_play_next;
let b = assignDurationToOAR(playerOAR);
bSetupDone = true;
setTimeout(() => {
const entriesB = Object.entries(inst).filter(e => typeof e[1] === 'number' && m.get(e[0]) !== e[1]);
m = null;
const filtered = entriesB.filter(e => Math.abs(e[1] - second_to_play_next * 1E3) < 1e-8);
if (filtered.length >= 1) {
pm.targetKeys = filtered.map(e => e[0]);
} else {
pm.targetKeys = null;
}
console.log(`sT(${b ? 'T' : 'F'}):`, filtered, filtered.length)
}, 80);
});
const obtainOAR = () => {
let playerOAR = null;
let pageDataMgr = document.querySelector('ytd-page-manager#page-manager');
let pageData = pageDataMgr ? insp(pageDataMgr).data : null;
if (pageData) {
try {
playerOAR = pageData.response.playerOverlays.playerOverlayRenderer.autoplay.playerOverlayAutoplayRenderer;
} catch (e) { }
}
if (playerOAR && typeof playerOAR.countDownSecsForFullscreen === 'number' && playerOAR.countDownSecsForFullscreen < 15) {
pm.playerOAR = playerOAR;
return true;
}
};
document.addEventListener('yt-navigate-finish', function () {
objProceed(false);
}, false);
const g = _yt_player;
const keysT = getsT(_yt_player);
if (keysT) {
let k = keysT;
let gk = g[k];
let gkp = g[k].prototype;
/*
*
if (typeof p.hasSupportedAudio51Tracks === 'function' && p.hasSupportedAudio51Tracks.length === 0) q += 2;
if (typeof p.getStoryboardFormat === 'function' && p.getStoryboardFormat.length === 0) q += 4;
if (typeof p.getPlaylistSequenceForTime === 'function' && p.getPlaylistSequenceForTime.length === 1) q += 4;
if (typeof p.isLoaded === 'function' && p.isLoaded.length === 0) q += 2;
if (typeof p.isOtf === 'function' && p.isOtf.length === 0) q += 2;
if (typeof p.getAvailableAudioTracks === 'function' && p.getAvailableAudioTracks.length === 0) q += 4;
if (typeof p.getAudioTrack === 'function' && p.getAudioTrack.length === 0) q += 4;
if (typeof p.getPlayerResponse === 'function' && p.getPlayerResponse.length === 0) q += 2;
if (typeof p.getHeartbeatResponse === 'function' && p.getHeartbeatResponse.length === 0) q += 2;
if (typeof p.isAd === 'function' && p.isAd.length === 0) q += 2;
if (typeof p.isDaiEnabled === 'function' && p.isDaiEnabled.length === 1) q += 2;
if (typeof p.useInnertubeDrmService === 'function' && p.useInnertubeDrmService.length === 0) q++;
if (typeof p.hasProgressBarBoundaries === 'function' && p.hasProgressBarBoundaries.length === 0) q += 2;
*/
// for(const pk of ['hasSupportedAudio51Tracks','getStoryboardFormat','getPlaylistSequenceForTime','isLoaded',
// 'isOtf',//'getAvailableAudioTracks','getAudioTrack',
// 'getPlayerResponse','getHeartbeatResponse',
// // 'isAd',
// 'isDaiEnabled','useInnertubeDrmService',
// //'hasProgressBarBoundaries'
// ]){
// }
if (!gkp.isLoaded75 && typeof gkp.isLoaded === 'function') {
gkp.isLoaded75 = gkp.isLoaded;
gkp.isLoaded = function () {
pm.instsT = this;
return arguments.length === 0 ? this.isLoaded75() : this.isLoaded75(...arguments);
}
}
}
})();
});
})();