您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
To make Quality Auto Max
当前为
// ==UserScript== // @name YouTube: Quality Auto Max // @namespace UserScripts // @match https://www.youtube.com/* // @version 0.2.0 // @author CY Fung // @license MIT // @description To make Quality Auto Max // @grant none // @run-at document-start // @unwrap // @inject-into page // ==/UserScript== (() => { const Promise = (async () => { })().constructor; 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_; } } }; })(); const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0); 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 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 getuU = (_yt_player) => { const w = 'uU'; 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.setPlaybackQualityRange === 'function' && p.setPlaybackQualityRange.length === 3) q += 200; if (typeof p.updateVideoData === 'function' && p.updateVideoData.length === 2) q += 80; if (p.getVideoAspectRatio) q += 20; if (p.getStreamTimeOffset) q += 20; // if (typeof p.updatePlaylist ==='function' && p.updatePlaylist.length===1)q += 80; if (q > 0) arr = addProtoToArr(_yt_player, k, arr) || arr; } } 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]; } } 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); }); let resultantQualities = null; let resultantQuality = null; let byPass = false; (async () => { let pm2 = new PromiseExternal(); const fn = async (evt) => { try { const target = (evt || 0).target if (!(target instanceof HTMLVideoElement)) return; pm2.resolve(); pm2 = new PromiseExternal(); const ytdPlayerElm = await observablePromise(() => { return target.closest('ytd-player#ytd-player') }, pm2.then()).obtain(); if (!ytdPlayerElm) return; let player_ for (let i = 10; --i;) { player_ = await ((insp(ytdPlayerElm) || 0).player_ || 0); if (player_) break; await new Promise(r => setTimeout(r)); } if (!player_) return; for (let i = 10; --i;) { if (player_.setPlaybackQualityRange) break; await new Promise(r => setTimeout(r)); } if (!player_.setPlaybackQualityRange) return; if (resultantQualities) { let resultantQuality; let qualityThreshold = +localStorage.qualityThreshold || 0; if (!(qualityThreshold > 60)) qualityThreshold = 0; for (const entry of resultantQualities) { if (entry.height > 60 && entry.quality && typeof entry.quality === 'string') { if (qualityThreshold === 0 || (qualityThreshold > 60 && entry.height <= qualityThreshold)) { resultantQuality = entry.quality; break; } } } if (resultantQuality) { byPass = true; player_.setPlaybackQualityRange(resultantQuality, resultantQuality) byPass = false; } } } catch (e) { console.warn(e) } }; // document.addEventListener('loadstart', fn, true) document.addEventListener('durationchange', fn, true) })(); (async () => { try { 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 keyuU = getuU(_yt_player); const keyL0 = getL0(_yt_player); if (keyuU) { let k = keyuU; let gk = g[k]; let gkp = g[k].prototype; gkp.setPlaybackQualityRange132 = gkp.setPlaybackQualityRange; gkp.setPlaybackQualityRange = function (...args) { if (!byPass && resultantQualities && document.visibilityState === 'visible') { if (args[0] === args[1] && typeof args[0] === 'string' && args[0]) { const selectionHeight = (resultantQualities.filter(e => e.quality === args[0])[0] || 0).height; if (selectionHeight > 60) { localStorage.qualityThreshold = selectionHeight; } } } return this.setPlaybackQualityRange132(...args) } } 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; } } } } // console.log(22, this) // const keyyU=getyU(_yt_player); // _yt_player[keyyU].prototype. resultantQualities = videoTypes; resultantQuality = highestQuality; // 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; } } } catch (e) { console.warn(e) } })(); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址