YouTube: Quality Auto Max

To make Quality Auto Max

当前为 2023-12-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube: Quality Auto Max
  3. // @namespace UserScripts
  4. // @match https://www.youtube.com/*
  5. // @version 0.1.0
  6. // @author CY Fung
  7. // @license MIT
  8. // @description To make Quality Auto Max
  9. // @grant none
  10. // @run-at document-start
  11. // @unwrap
  12. // @inject-into page
  13. // ==/UserScript==
  14.  
  15. (() => {
  16.  
  17.  
  18.  
  19.  
  20. const getL0 = (_yt_player) => {
  21. const w = 'L0';
  22.  
  23. let arr = [];
  24.  
  25. for (const [k, v] of Object.entries(_yt_player)) {
  26.  
  27. const p = typeof v === 'function' ? v.prototype : 0;
  28. if (p) {
  29. let q = 0;
  30. if (typeof p.getPreferredQuality === 'function' && p.getPreferredQuality.length === 0) q += 200;
  31. if (typeof p.getVideoData === 'function' && p.getVideoData.length === 0) q += 80;
  32. if (typeof p.isPlaying === 'function' && p.isPlaying.length === 0) q += 2;
  33.  
  34. if (typeof p.getPlayerState === 'function' && p.getPlayerState.length === 0) q += 2;
  35.  
  36. if (typeof p.getPlayerType === 'function' && p.getPlayerType.length === 0) q += 2;
  37.  
  38.  
  39. if (q > 0) arr.push([k, q])
  40.  
  41. }
  42.  
  43. }
  44.  
  45. if (arr.length === 0) {
  46.  
  47. console.warn(`Key does not exist. [${w}]`);
  48. } else {
  49.  
  50. if (arr.length > 1) arr.sort((a, b) => b[1] - a[1]);
  51.  
  52.  
  53. console.log(`[${w}]`, arr);
  54. return arr[0][0];
  55. }
  56.  
  57.  
  58.  
  59. }
  60.  
  61.  
  62. const getZf = (vL0) => {
  63. const w = 'vL0';
  64.  
  65. let arr = [];
  66.  
  67. for (const [k, v] of Object.entries(vL0)) {
  68.  
  69. // console.log(k,v)
  70.  
  71. const p = v;
  72. if (p) {
  73. let q = 0;
  74. if (typeof p.videoData === 'object' && p.videoData) {
  75.  
  76. if (Object.keys(p).length === 2) q += 200;
  77.  
  78. }
  79.  
  80.  
  81. if (q > 0) arr.push([k, q])
  82.  
  83. }
  84.  
  85. }
  86.  
  87. if (arr.length === 0) {
  88.  
  89. // console.warn(`Key does not exist. [${w}]`);
  90. } else {
  91.  
  92. if (arr.length > 1) arr.sort((a, b) => b[1] - a[1]);
  93.  
  94.  
  95. console.log(`[${w}]`, arr);
  96. return arr[0][0];
  97. }
  98.  
  99.  
  100.  
  101. }
  102.  
  103.  
  104.  
  105.  
  106. const onRegistryReady = (callback) => {
  107. if (typeof customElements === 'undefined') {
  108. if (!('__CE_registry' in document)) {
  109. // https://github.com/webcomponents/polyfills/
  110. Object.defineProperty(document, '__CE_registry', {
  111. get() {
  112. // return undefined
  113. },
  114. set(nv) {
  115. if (typeof nv == 'object') {
  116. delete this.__CE_registry;
  117. this.__CE_registry = nv;
  118. this.dispatchEvent(new CustomEvent(EVENT_KEY_ON_REGISTRY_READY));
  119. }
  120. return true;
  121. },
  122. enumerable: false,
  123. configurable: true
  124. })
  125. }
  126. let eventHandler = (evt) => {
  127. document.removeEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
  128. const f = callback;
  129. callback = null;
  130. eventHandler = null;
  131. f();
  132. };
  133. document.addEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
  134. } else {
  135. callback();
  136. }
  137. };
  138.  
  139. const cleanContext = async (win) => {
  140. const waitFn = requestAnimationFrame; // shall have been binded to window
  141. try {
  142. let mx = 16; // MAX TRIAL
  143. const frameId = 'vanillajs-iframe-v1';
  144. /** @type {HTMLIFrameElement | null} */
  145. let frame = document.getElementById(frameId);
  146. let removeIframeFn = null;
  147. if (!frame) {
  148. frame = document.createElement('iframe');
  149. frame.id = frameId;
  150. const blobURL = typeof webkitCancelAnimationFrame === 'function' ? (frame.src = URL.createObjectURL(new Blob([], { type: 'text/html' }))) : null; // avoid Brave Crash
  151. frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
  152. let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
  153. n.appendChild(frame);
  154. while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
  155. const root = document.documentElement;
  156. root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
  157. if (blobURL) Promise.resolve().then(() => URL.revokeObjectURL(blobURL));
  158.  
  159. removeIframeFn = (setTimeout) => {
  160. const removeIframeOnDocumentReady = (e) => {
  161. e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  162. win = null;
  163. const m = n;
  164. n = null;
  165. setTimeout(() => m.remove(), 200);
  166. }
  167. if (document.readyState !== 'loading') {
  168. removeIframeOnDocumentReady();
  169. } else {
  170. win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  171. }
  172. }
  173. }
  174. while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
  175. const fc = frame.contentWindow;
  176. if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
  177. const { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle } = fc;
  178. const res = { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle };
  179. for (let k in res) res[k] = res[k].bind(win); // necessary
  180. if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
  181. res.animate = fc.HTMLElement.prototype.animate;
  182. return res;
  183. } catch (e) {
  184. console.warn(e);
  185. return null;
  186. }
  187. };
  188.  
  189.  
  190. const promiseForCustomYtElementsReady = new Promise(onRegistryReady);
  191.  
  192. cleanContext(window).then(__CONTEXT__ => {
  193. if (!__CONTEXT__) return null;
  194.  
  195. const { setTimeout } = __CONTEXT__;
  196.  
  197.  
  198.  
  199.  
  200. const promiseForTamerTimeout = new Promise(resolve => {
  201. promiseForCustomYtElementsReady.then(() => {
  202. customElements.whenDefined('ytd-app').then(() => {
  203. setTimeout(resolve, 1200);
  204. });
  205. });
  206. setTimeout(resolve, 3000);
  207. });
  208.  
  209.  
  210.  
  211. (async () => {
  212.  
  213.  
  214. const observablePromise = (proc, timeoutPromise) => {
  215. let promise = null;
  216. return {
  217. obtain() {
  218. if (!promise) {
  219. promise = new Promise(resolve => {
  220. let mo = null;
  221. const f = () => {
  222. let t = proc();
  223. if (t) {
  224. mo.disconnect();
  225. mo.takeRecords();
  226. mo = null;
  227. resolve(t);
  228. }
  229. }
  230. mo = new MutationObserver(f);
  231. mo.observe(document, { subtree: true, childList: true })
  232. f();
  233. timeoutPromise && timeoutPromise.then(() => {
  234. resolve(null)
  235. });
  236. });
  237. }
  238. return promise
  239. }
  240. }
  241. }
  242.  
  243.  
  244. const _yt_player = await observablePromise(() => {
  245. return (((window || 0)._yt_player || 0) || 0);
  246. }, promiseForTamerTimeout).obtain();
  247.  
  248. if (!_yt_player || typeof _yt_player !== 'object') return;
  249.  
  250. const vmHash = new WeakSet();
  251.  
  252. const g = _yt_player;
  253. const keyL0 = getL0(_yt_player);
  254.  
  255.  
  256. if (keyL0) {
  257. let k = keyL0;
  258. let gk = g[k];
  259. let gkp = g[k].prototype;
  260.  
  261. let keyZf = null;
  262.  
  263. gkp.getVideoData31 = gkp.getVideoData;
  264. gkp.setupOnNewVideoData61 = function () {
  265.  
  266. keyZf = getZf(this);
  267. if (!keyZf) return;
  268.  
  269. const tZf = this[keyZf];
  270.  
  271. if (!tZf) return;
  272.  
  273. let keyJ = Object.keys(tZf).filter(e => e !== 'videoData')[0]
  274.  
  275. const tZfJ = tZf[keyJ];
  276. const videoData = tZf.videoData;
  277. if (!tZfJ || !videoData || !tZfJ.videoInfos) return;
  278.  
  279.  
  280. let videoTypes = tZfJ.videoInfos.map(info => info.video);
  281.  
  282.  
  283. if (!videoTypes[0] || !videoTypes[0].quality || !videoTypes[0].height) return;
  284.  
  285. let highestQuality = videoTypes[0].quality
  286.  
  287. console.log('highestQuality', highestQuality)
  288.  
  289. let keyLists = new Set();
  290. let keyLists2 = new Set();
  291. const o = {
  292. [keyZf]: {
  293. videoData: new Proxy(videoData, {
  294. get(obj, key) {
  295. keyLists.add(key);
  296. const v = obj[key];
  297. if (typeof v === 'object') return new Proxy(v, {
  298. get(obj, key) {
  299. keyLists2.add(key);
  300. return obj[key]
  301. }
  302. })
  303. return v
  304. }
  305. })
  306. }
  307. }
  308.  
  309. this.getPreferredQuality.call(o)
  310. // console.log(keyLists.size, keyLists2.size)
  311. if (keyLists.size !== 2) return;
  312. if (keyLists2.size < 3) return;
  313.  
  314.  
  315.  
  316. /*
  317. * 1080p Premium
  318.  
  319. g.k.Nj = function(a) {
  320. h_a(this);
  321. this.options[a].element.setAttribute("aria-checked", "true");
  322. this.Yd(this.Dk(a));
  323. this.C = a
  324. }
  325.  
  326. */
  327.  
  328. /*
  329. TP = function(a) {
  330. return SP[a.j || a.B] || "auto"
  331. }
  332. */
  333.  
  334. const [keyAy, keyxU] = [...keyLists];
  335. const keyLs = [...keyLists2]
  336. const keyPs = [keyAy, keyxU]
  337.  
  338. let cz = 0;
  339. function inc() {
  340. for (const pey of keyPs) {
  341.  
  342. for (const ley of keyLs) {
  343. const val = videoData[pey][ley]
  344. if (typeof val === 'number' && val >= 0 && ~~val === val) {
  345. if (!cz) cz = ley;
  346. if (cz === ley) {
  347. // videoData[pey][ley] = 5120;
  348. // videoData[pey][ley] = videoTypes[0].height;
  349. continue
  350. }
  351. videoData[pey][ley] = videoTypes[0].height;
  352. // videoData[pey][ley]='1080p Premium'
  353. // videoData[pey][ley] = '1080p';
  354. // videoData[pey]['reason']='m'
  355. } else if (typeof val === 'boolean' && val === false) {
  356. videoData[pey][ley] = true;
  357. }
  358. }
  359.  
  360. }
  361. }
  362.  
  363. inc();
  364. // console.log(22, this)
  365.  
  366. // const keyyU=getyU(_yt_player);
  367. // _yt_player[keyyU].prototype.
  368.  
  369.  
  370. // (async()=>{
  371. // await customElements.whenDefined('ytd-watch-flexy');
  372. // await customElements.whenDefined('ytd-player');
  373.  
  374. // await new Promise(r=>setTimeout(r, 800));
  375. // console.log(12321, document.querySelector('ytd-watch-flexy'))
  376. // console.log(12322, document.querySelector('ytd-watch-flexy').polymerController.player )
  377.  
  378. // document.querySelector('ytd-watch-flexy').polymerController.player.setPlaybackQuality(highestQuality)
  379. // console.log(this.getPreferredQuality())
  380. // })();
  381.  
  382.  
  383. // inc();
  384. // console.log(this.getPreferredQuality())
  385. // inc();
  386. // console.log(this.getPreferredQuality())
  387. // console.log(videoData, keyxU)
  388.  
  389. // console.log(this)
  390. // console.log(1237, keyZf, keyJ, this[keyZf], videoTypes, videoData[keyAy], videoData[keyxU], keyLists2)
  391.  
  392. }
  393. gkp.getVideoData = function () {
  394. const vd = this.getVideoData31();;
  395. if (!vd || typeof vd !== 'object') return vd;
  396. if (!vmHash.has(vd)) {
  397. vmHash.add(vd);
  398. this.setupOnNewVideoData61();
  399. if (!keyZf) vmHash.delete(vd)
  400. }
  401. return vd;
  402. }
  403.  
  404.  
  405. }
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413. })();
  414.  
  415. });
  416.  
  417. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址