// ==UserScript==
// @name YouTube: Quality Auto Max
// @namespace UserScripts
// @match https://www.youtube.com/*
// @version 0.1.0
// @author CY Fung
// @license MIT
// @description To make Quality Auto Max
// @grant none
// @run-at document-start
// @unwrap
// @inject-into page
// ==/UserScript==
(() => {
const getL0 = (_yt_player) => {
const w = 'L0';
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.getPreferredQuality === 'function' && p.getPreferredQuality.length === 0) q += 200;
if (typeof p.getVideoData === 'function' && p.getVideoData.length === 0) q += 80;
if (typeof p.isPlaying === 'function' && p.isPlaying.length === 0) q += 2;
if (typeof p.getPlayerState === 'function' && p.getPlayerState.length === 0) q += 2;
if (typeof p.getPlayerType === 'function' && p.getPlayerType.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 getZf = (vL0) => {
const w = 'vL0';
let arr = [];
for (const [k, v] of Object.entries(vL0)) {
// console.log(k,v)
const p = v;
if (p) {
let q = 0;
if (typeof p.videoData === 'object' && p.videoData) {
if (Object.keys(p).length === 2) q += 200;
}
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 onRegistryReady = (callback) => {
if (typeof customElements === 'undefined') {
if (!('__CE_registry' in document)) {
// https://github.com/webcomponents/polyfills/
Object.defineProperty(document, '__CE_registry', {
get() {
// return undefined
},
set(nv) {
if (typeof nv == 'object') {
delete this.__CE_registry;
this.__CE_registry = nv;
this.dispatchEvent(new CustomEvent(EVENT_KEY_ON_REGISTRY_READY));
}
return true;
},
enumerable: false,
configurable: true
})
}
let eventHandler = (evt) => {
document.removeEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
const f = callback;
callback = null;
eventHandler = null;
f();
};
document.addEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
} else {
callback();
}
};
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' ? (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;
}
};
const promiseForCustomYtElementsReady = new Promise(onRegistryReady);
cleanContext(window).then(__CONTEXT__ => {
if (!__CONTEXT__) return null;
const { setTimeout } = __CONTEXT__;
const promiseForTamerTimeout = new Promise(resolve => {
promiseForCustomYtElementsReady.then(() => {
customElements.whenDefined('ytd-app').then(() => {
setTimeout(resolve, 1200);
});
});
setTimeout(resolve, 3000);
});
(async () => {
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
}
}
}
const _yt_player = await observablePromise(() => {
return (((window || 0)._yt_player || 0) || 0);
}, promiseForTamerTimeout).obtain();
if (!_yt_player || typeof _yt_player !== 'object') return;
const vmHash = new WeakSet();
const g = _yt_player;
const keyL0 = getL0(_yt_player);
if (keyL0) {
let k = keyL0;
let gk = g[k];
let gkp = g[k].prototype;
let keyZf = null;
gkp.getVideoData31 = gkp.getVideoData;
gkp.setupOnNewVideoData61 = function () {
keyZf = getZf(this);
if (!keyZf) return;
const tZf = this[keyZf];
if (!tZf) return;
let keyJ = Object.keys(tZf).filter(e => e !== 'videoData')[0]
const tZfJ = tZf[keyJ];
const videoData = tZf.videoData;
if (!tZfJ || !videoData || !tZfJ.videoInfos) return;
let videoTypes = tZfJ.videoInfos.map(info => info.video);
if (!videoTypes[0] || !videoTypes[0].quality || !videoTypes[0].height) return;
let highestQuality = videoTypes[0].quality
console.log('highestQuality', highestQuality)
let keyLists = new Set();
let keyLists2 = new Set();
const o = {
[keyZf]: {
videoData: new Proxy(videoData, {
get(obj, key) {
keyLists.add(key);
const v = obj[key];
if (typeof v === 'object') return new Proxy(v, {
get(obj, key) {
keyLists2.add(key);
return obj[key]
}
})
return v
}
})
}
}
this.getPreferredQuality.call(o)
// console.log(keyLists.size, keyLists2.size)
if (keyLists.size !== 2) return;
if (keyLists2.size < 3) return;
/*
* 1080p Premium
g.k.Nj = function(a) {
h_a(this);
this.options[a].element.setAttribute("aria-checked", "true");
this.Yd(this.Dk(a));
this.C = a
}
*/
/*
TP = function(a) {
return SP[a.j || a.B] || "auto"
}
*/
const [keyAy, keyxU] = [...keyLists];
const keyLs = [...keyLists2]
const keyPs = [keyAy, keyxU]
let cz = 0;
function inc() {
for (const pey of keyPs) {
for (const ley of keyLs) {
const val = videoData[pey][ley]
if (typeof val === 'number' && val >= 0 && ~~val === val) {
if (!cz) cz = ley;
if (cz === ley) {
// videoData[pey][ley] = 5120;
// videoData[pey][ley] = videoTypes[0].height;
continue
}
videoData[pey][ley] = videoTypes[0].height;
// videoData[pey][ley]='1080p Premium'
// videoData[pey][ley] = '1080p';
// videoData[pey]['reason']='m'
} else if (typeof val === 'boolean' && val === false) {
videoData[pey][ley] = true;
}
}
}
}
inc();
// console.log(22, this)
// const keyyU=getyU(_yt_player);
// _yt_player[keyyU].prototype.
// (async()=>{
// await customElements.whenDefined('ytd-watch-flexy');
// await customElements.whenDefined('ytd-player');
// await new Promise(r=>setTimeout(r, 800));
// console.log(12321, document.querySelector('ytd-watch-flexy'))
// console.log(12322, document.querySelector('ytd-watch-flexy').polymerController.player )
// document.querySelector('ytd-watch-flexy').polymerController.player.setPlaybackQuality(highestQuality)
// console.log(this.getPreferredQuality())
// })();
// inc();
// console.log(this.getPreferredQuality())
// inc();
// console.log(this.getPreferredQuality())
// console.log(videoData, keyxU)
// console.log(this)
// console.log(1237, keyZf, keyJ, this[keyZf], videoTypes, videoData[keyAy], videoData[keyxU], keyLists2)
}
gkp.getVideoData = function () {
const vd = this.getVideoData31();;
if (!vd || typeof vd !== 'object') return vd;
if (!vmHash.has(vd)) {
vmHash.add(vd);
this.setupOnNewVideoData61();
if (!keyZf) vmHash.delete(vd)
}
return vd;
}
}
})();
});
})();