YouTube JS Engine Tamer

To enhance YouTube performance by modifying YouTube JS Engine

当前为 2023-10-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube JS Engine Tamer
  3. // @namespace UserScripts
  4. // @match https://www.youtube.com/*
  5. // @version 0.5.4
  6. // @license MIT
  7. // @author CY Fung
  8. // @icon https://github.com/cyfung1031/userscript-supports/raw/main/icons/yt-engine.png
  9. // @description To enhance YouTube performance by modifying YouTube JS Engine
  10. // @grant none
  11. // @run-at document-start
  12. // @unwrap
  13. // @inject-into page
  14. // @allFrames true
  15. // ==/UserScript==
  16.  
  17. (() => {
  18.  
  19. const NATIVE_CANVAS_ANIMATION = false; // for #cinematics
  20. const FIX_schedulerInstanceInstance_V1 = false;
  21. const FIX_schedulerInstanceInstance_V2 = true;
  22. const FIX_yt_player = true;
  23. const FIX_Animation_n_timeline = true;
  24. const NO_PRELOAD_GENERATE_204 = false;
  25. const CHANGE_appendChild = true;
  26.  
  27. const FIX_error_many_stack = true; // should be a bug caused by uBlock Origin
  28. // const FIX_error_many_stack_keepAliveDuration = 200; // ms
  29. // const FIX_error_many_stack_keepAliveDuration_check_if_n_larger_than = 8;
  30.  
  31. const FIX_Iframe_NULL_SRC = true;
  32.  
  33. const IGNORE_bindAnimationForCustomEffect = true; // prevent `v.bindAnimationForCustomEffect(this);` being executed
  34.  
  35. const FIX_ytdExpander_childrenChanged = true;
  36. const FIX_paper_ripple_animate = true;
  37.  
  38. const FIX_doIdomRender = true;
  39.  
  40. const FIX_Shady = true;
  41.  
  42. const FIX_ytAction_ = true;
  43. const FIX_onVideoDataChange = true;
  44. // const FIX_onClick = true;
  45. const FIX_onStateChange = true;
  46. const FIX_onLoopRangeChange = true;
  47.  
  48.  
  49. /*
  50. window.addEventListener('edm',()=>{
  51. let p = [...this.onerror.errorTokens][0].token; (()=>{ console.log(p); throw new Error(p);console.log(334,p) })()
  52. });
  53.  
  54. window.addEventListener('edn',()=>{
  55. let p = [...this.onerror.errorTokens][0].token+"X"; (()=>{ console.log(p); throw new Error(p);console.log(334,p) })()
  56. });
  57. window.addEventListener('edr',()=>{
  58. let p = '123'; (()=>{ console.log(p); throw new Error(p);console.log(334,p) })()
  59. });
  60. */
  61.  
  62.  
  63.  
  64. let p59 = 0;
  65.  
  66. const Promise = (async () => { })().constructor;
  67.  
  68. const PromiseExternal = ((resolve_, reject_) => {
  69. const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject };
  70. return class PromiseExternal extends Promise {
  71. constructor(cb = h) {
  72. super(cb);
  73. if (cb === h) {
  74. /** @type {(value: any) => void} */
  75. this.resolve = resolve_;
  76. /** @type {(reason?: any) => void} */
  77. this.reject = reject_;
  78. }
  79. }
  80. };
  81. })();
  82.  
  83.  
  84. let pf31 = new PromiseExternal();
  85.  
  86. // native RAF
  87. let __requestAnimationFrame__ = typeof webkitRequestAnimationFrame === 'function' ? window.webkitRequestAnimationFrame.bind(window) : window.requestAnimationFrame.bind(window);
  88.  
  89. // 1st wrapped RAF
  90. const baseRAF = (callback) => {
  91. return p59 ? __requestAnimationFrame__(callback) : __requestAnimationFrame__((hRes) => {
  92. pf31.then(() => {
  93. callback(hRes);
  94. });
  95. });
  96. };
  97.  
  98. // 2nd wrapped RAF
  99. window.requestAnimationFrame = baseRAF;
  100.  
  101. // requestAnimationFrame is likely to be wrapped into YT Engine's rAf.
  102.  
  103. const pLoad = new Promise(resolve => {
  104. if (document.readyState !== 'loading') {
  105. resolve();
  106. } else {
  107. window.addEventListener("DOMContentLoaded", resolve, false);
  108. }
  109. });
  110. pLoad.then(() => {
  111.  
  112. let nonce = document.querySelector('style[nonce]');
  113. nonce = nonce ? nonce.getAttribute('nonce') : null;
  114. const st = document.createElement('style');
  115. if (typeof nonce === 'string') st.setAttribute('nonce', nonce);
  116. st.textContent = "none-element-k47{order:0}";
  117. st.addEventListener('load', () => {
  118. pf31.resolve();
  119. p59 = 1;
  120. }, false);
  121. document.body.appendChild(st);
  122.  
  123.  
  124. // console.debug('90002', location.pathname)
  125. // console.log(90000, location.pathname)
  126.  
  127. });
  128.  
  129. const prepareLogs = [];
  130.  
  131. const skipAdsDetection = new Set(['/robots.txt', '/live_chat', '/live_chat_replay']);
  132.  
  133. let winError00 = window.onerror;
  134.  
  135. let fix_error_many_stack_state = !FIX_error_many_stack ? 0 : skipAdsDetection.has(location.pathname) ? 2 : 1;
  136.  
  137. if (!JSON || !('parse' in JSON)) fix_error_many_stack_state = 0;
  138.  
  139. ; FIX_Iframe_NULL_SRC && (() => {
  140.  
  141. let emptyBlobUrl = URL.createObjectURL(new Blob([], { type: 'text/html' }));
  142. const lcOpt = { sensitivity: 'base' };
  143. document.createElement24 = document.createElement;
  144. document.createElement = function (t) {
  145. if (typeof t === 'string' && t.length === 6) {
  146. if (t.localeCompare('iframe', undefined, lcOpt) === 0) {
  147. let p = this.createElement24(t);
  148. p.src = emptyBlobUrl; // avoid iframe is appended to DOM without any url
  149. return p;
  150. }
  151. }
  152. return this.createElement24.apply(this, arguments);
  153. };
  154.  
  155. })();
  156.  
  157. ; fix_error_many_stack_state === 1 && (() => {
  158.  
  159.  
  160. let p1 = winError00;
  161.  
  162. let stackNeedleDetails = null;
  163.  
  164. Object.defineProperty(Object.prototype, 'matchAll', {
  165. get() {
  166. stackNeedleDetails = this;
  167. return true;
  168. },
  169. enumerable: true,
  170. configurable: true
  171. });
  172.  
  173. try {
  174. JSON.parse("{}");
  175. } catch (e) {
  176. console.warn(e)
  177. fix_error_many_stack_state = 0;
  178. }
  179.  
  180. delete Object.prototype['matchAll'];
  181.  
  182. let p2 = window.onerror;
  183.  
  184. window.onerror = p1;
  185.  
  186. if (fix_error_many_stack_state === 0) return;
  187.  
  188. if (stackNeedleDetails) {
  189. JSON.parse.stackNeedleDetails = stackNeedleDetails;
  190. stackNeedleDetails.matchAll = true;
  191. }
  192.  
  193. if (p1 === p2) return (fix_error_many_stack_state = 0);
  194.  
  195. // p1!==p2
  196. fix_error_many_stack_state = !stackNeedleDetails ? 4 : 3;
  197.  
  198. })();
  199.  
  200. ; fix_error_many_stack_state === 2 && (() => {
  201.  
  202.  
  203. let p1 = winError00;
  204.  
  205. let objectPrune = null;
  206. let stackNeedleDetails = null;
  207.  
  208. Object.defineProperty(Function.prototype, 'findOwner', {
  209. get() {
  210. objectPrune = this;
  211. return this._findOwner;
  212. },
  213. set(nv) {
  214. this._findOwner = nv;
  215. return true;
  216. },
  217. enumerable: true,
  218. configurable: true
  219. });
  220.  
  221. Object.defineProperty(Object.prototype, 'matchAll', {
  222. get() {
  223. stackNeedleDetails = this;
  224. return true;
  225. },
  226. enumerable: true,
  227. configurable: true
  228. });
  229.  
  230. try {
  231. JSON.parse("{}");
  232. } catch (e) {
  233. console.warn(e)
  234. fix_error_many_stack_state = 0;
  235. }
  236.  
  237. delete Function.prototype['findOwner'];
  238. delete Object.prototype['matchAll'];
  239.  
  240. let p2 = window.onerror;
  241.  
  242. if (p1 !== p2) return (fix_error_many_stack_state = 4); // p1 != p2
  243.  
  244. if (fix_error_many_stack_state == 0) return;
  245.  
  246. // the following will only execute when Brave's scriptlets.js is executed.
  247.  
  248. prepareLogs.push("fix_error_many_stack_state NB")
  249.  
  250. if (stackNeedleDetails) {
  251. stackNeedleDetails.pattern = null;
  252. stackNeedleDetails.re = null;
  253. stackNeedleDetails.expect = null;
  254. stackNeedleDetails.matchAll = true;
  255. }
  256.  
  257. if (objectPrune) {
  258. objectPrune.findOwner = objectPrune.mustProcess = objectPrune.logJson = () => { }
  259. delete objectPrune._findOwner;
  260. }
  261.  
  262. fix_error_many_stack_state = 3;
  263. JSON.parse.stackNeedleDetails = stackNeedleDetails;
  264. JSON.parse.objectPrune = objectPrune;
  265.  
  266. })();
  267.  
  268. ; fix_error_many_stack_state === 3 && (() => {
  269.  
  270.  
  271. let p1 = winError00;
  272.  
  273. try {
  274. JSON.parse("{}");
  275. } catch (e) {
  276. console.warn(e)
  277. fix_error_many_stack_state = 0;
  278. }
  279.  
  280. let p2 = window.onerror;
  281.  
  282. if (p1 === p2) return;
  283.  
  284. window.onerror = p1;
  285.  
  286. if (fix_error_many_stack_state === 0) return;
  287.  
  288. fix_error_many_stack_state = 4; // p1 != p2
  289.  
  290.  
  291. })();
  292.  
  293. fix_error_many_stack_state === 4 && (() => {
  294.  
  295. // the following will only execute when Brave's scriptlets.js is executed.
  296.  
  297. prepareLogs.push("fix_error_many_stack_state AB")
  298.  
  299. JSON.parseProxy = JSON.parse;
  300.  
  301. JSON.parse = ((parse) => {
  302.  
  303. parse = parse.bind(JSON); // get a new instance of the current JSON.parse
  304. return function (text, reviver) {
  305. const onerror = window.onerror;
  306. window.onerror = null;
  307. let r;
  308. try {
  309. r = parse(...arguments);
  310. } catch (e) {
  311. r = e;
  312. }
  313. window.onerror = onerror;
  314. if (r instanceof Error) {
  315. throw r;
  316. }
  317. return r;
  318. }
  319.  
  320. })(JSON.parse);
  321.  
  322.  
  323. })();
  324.  
  325.  
  326.  
  327. // ================================================ 0.4.5 ================================================
  328.  
  329.  
  330. // ; (() => {
  331.  
  332. // if (FIX_error_many_stack && self instanceof Window) {
  333. // // infinite stack due to matchesStackTrace inside objectPrune of AdsBlock
  334.  
  335. // const pdK = Object.getOwnPropertyDescriptor(window, 'onerror');
  336. // if (!pdK || (pdK.get && pdK.configurable)) {
  337.  
  338. // } else {
  339. // return;
  340. // }
  341.  
  342. // let unsupportErrorFix = false;
  343.  
  344. // let firstHook = true;
  345. // let busy33 = false;
  346.  
  347. // let state = 0;
  348.  
  349. // if (pdK) {
  350. // delete window['onerror'];
  351. // }
  352.  
  353. // const pd = {
  354. // get() {
  355. // const stack = (new Error()).stack;
  356. // // targetStack = stack;
  357. // let isGetExceptionToken = stack.indexOf('getExceptionToken') >= 0;
  358. // state = isGetExceptionToken ? 1 : 0;
  359. // delete Window.prototype['onerror'];
  360. // let r = pdK ? pdK.get.call(this) : this.onerror;
  361. // Object.defineProperty(Window.prototype, 'onerror', pd);
  362. // // console.log('onerror get', r)
  363. // return r;
  364. // },
  365. // set(nv) {
  366. // const stack = (new Error()).stack;
  367. // let isGetExceptionToken = stack.indexOf('getExceptionToken') >= 0;
  368. // state = state === 1 && isGetExceptionToken ? 2 : 0;
  369. // /** @type {string?} */
  370. // let sToken = null;
  371. // if (unsupportErrorFix || busy33) {
  372.  
  373. // } else if (typeof nv === 'function' && state === 2) {
  374. // if (firstHook) {
  375. // firstHook = false;
  376. // console.groupCollapsed('Infinite onerror Bug Found');
  377. // console.log(location.href);
  378. // console.log(stack);
  379. // console.log(nv);
  380. // console.groupEnd();
  381. // }
  382. // let _token = null;
  383. // busy33 = true;
  384. // String.prototype.includes76 = String.prototype.includes;
  385. // String.prototype.includes = function (token) {
  386. // _token = token;
  387. // return true;
  388. // }
  389. // nv('token');
  390. // String.prototype.includes = String.prototype.includes76;
  391. // sToken = _token;
  392. // busy33 = false;
  393. // if (typeof sToken !== 'string') {
  394. // unsupportErrorFix = true;
  395. // }
  396. // }
  397. // delete Window.prototype['onerror'];
  398. // if (typeof sToken === 'string' && sToken.length > 1) {
  399. // /** @type {string} */
  400. // const token = sToken;
  401. // /** @type {OnErrorEventHandler & {errorTokens: Set<string>?} } */
  402. // const currentOnerror = pdK ? pdK.get.call(this) : this.onerror;
  403.  
  404. // const now = Date.now();
  405. // const tokenEntry = {
  406. // token,
  407. // expired: now + FIX_error_many_stack_keepAliveDuration
  408. // }
  409. // /** @typedef {typeof tokenEntry} TokenEntry */
  410.  
  411. // /** @type {Set<TokenEntry>} */
  412. // const errorTokens = currentOnerror.errorTokens;
  413.  
  414. // if (errorTokens) {
  415. // if (errorTokens.size > FIX_error_many_stack_keepAliveDuration_check_if_n_larger_than) {
  416. // for (const entry of errorTokens) {
  417. // if (entry.expired < now) {
  418. // errorTokens.delete(entry);
  419. // }
  420. // }
  421. // }
  422. // errorTokens.add(tokenEntry)
  423. // } else {
  424. // /** @type {Set<TokenEntry>} */
  425. // const errorTokens = new Set([tokenEntry]);
  426. // /** @type {OnErrorEventHandler & {errorTokens: Set<string>} } */
  427. // const newOnerror = ((oe) => {
  428. // const r = function (msg, ...args) {
  429. // if (typeof msg === 'string' && errorTokens.size > 0) {
  430. // for (const entry of errorTokens) {
  431. // if (msg.includes(entry.token)) return true;
  432. // }
  433. // }
  434. // if (typeof oe === 'function') {
  435. // return oe.apply(this, arguments);
  436. // }
  437. // };
  438. // r.errorTokens = errorTokens;
  439. // return r;
  440. // })(currentOnerror);
  441.  
  442. // if (pdK && pdK.set) pdK.set.call(this, newOnerror);
  443. // else this.onerror = newOnerror;
  444. // }
  445. // } else {
  446. // if (pdK && pdK.set) pdK.set.call(this, nv);
  447. // else this.onerror = nv;
  448. // }
  449. // Object.defineProperty(Window.prototype, 'onerror', pd);
  450.  
  451. // // console.log('onerror set', nv)
  452. // return true;
  453. // },
  454. // enumerable: true,
  455. // configurable: true
  456. // }
  457.  
  458. // Object.defineProperty(Window.prototype, 'onerror', pd);
  459.  
  460.  
  461. // }
  462.  
  463.  
  464. // })();
  465.  
  466.  
  467.  
  468. // ================================================ 0.4.5 ================================================
  469.  
  470.  
  471. // << if FIX_yt_player >>
  472.  
  473. // credit to @nopeless (https://greasyfork.org/scripts/471489-youtube-player-perf/)
  474. const PERF_471489_ = true;
  475. // PERF_471489_ is not exactly the same to Youtube Player perf v0.7
  476. // This script uses a much gentle way to tamer the JS engine instead.
  477.  
  478. // << end >>
  479.  
  480. const steppingScaleN = 200; // transform: scaleX(k/N); 0<k<N
  481.  
  482.  
  483.  
  484. const nilFn = () => { };
  485.  
  486. let isMainWindow = false;
  487. try {
  488. isMainWindow = window.document === window.top.document
  489. } catch (e) { }
  490.  
  491. let NO_PRELOAD_GENERATE_204_BYPASS = NO_PRELOAD_GENERATE_204 ? false : true;
  492.  
  493. const onRegistryReady = (callback) => {
  494. if (typeof customElements === 'undefined') {
  495. if (!('__CE_registry' in document)) {
  496. // https://github.com/webcomponents/polyfills/
  497. Object.defineProperty(document, '__CE_registry', {
  498. get() {
  499. // return undefined
  500. },
  501. set(nv) {
  502. if (typeof nv == 'object') {
  503. delete this.__CE_registry;
  504. this.__CE_registry = nv;
  505. this.dispatchEvent(new CustomEvent(EVENT_KEY_ON_REGISTRY_READY));
  506. }
  507. return true;
  508. },
  509. enumerable: false,
  510. configurable: true
  511. })
  512. }
  513. let eventHandler = (evt) => {
  514. document.removeEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
  515. const f = callback;
  516. callback = null;
  517. eventHandler = null;
  518. f();
  519. };
  520. document.addEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
  521. } else {
  522. callback();
  523. }
  524. };
  525.  
  526.  
  527. const assertor = (f) => f() || console.assert(false, f + "");
  528.  
  529. const fnIntegrity = (f, d) => {
  530. if (!f || typeof f !== 'function') {
  531. console.warn('f is not a function', f);
  532. return;
  533. }
  534. let p = f + "", s = 0, j = -1, w = 0;
  535. for (let i = 0, l = p.length; i < l; i++) {
  536. const t = p[i];
  537. if (((t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z'))) {
  538. if (j < i - 1) w++;
  539. j = i;
  540. } else {
  541. s++;
  542. }
  543. }
  544. let itz = `${f.length}.${s}.${w}`;
  545. if (!d) {
  546. return itz;
  547. } else {
  548. return itz === d;
  549. }
  550. };
  551.  
  552. const getZq = (_yt_player) => {
  553.  
  554. const w = 'Zq';
  555.  
  556. let arr = [];
  557.  
  558. for (const [k, v] of Object.entries(_yt_player)) {
  559.  
  560. const p = typeof v === 'function' ? v.prototype : 0;
  561. if (p
  562. && typeof p.start === 'function' && p.start.length === 0
  563. && typeof p.isActive === 'function' && p.isActive.length === 0
  564. && typeof p.stop === 'function' && p.stop.length === 0
  565. && !p.isComplete && !p.getStatus && !p.getResponseHeader && !p.getLastError
  566. && !p.send && !p.abort
  567. && !p.sample && !p.initialize && !p.fail && !p.getName
  568. // && !p.dispose && !p.isDisposed
  569.  
  570. ) {
  571. arr = addProtoToArr(_yt_player, k, arr) || arr;
  572.  
  573.  
  574. }
  575.  
  576. }
  577.  
  578. if (arr.length === 0) {
  579.  
  580. console.warn(`Key does not exist. [${w}]`);
  581. } else {
  582.  
  583. console.log(`[${w}]`, arr);
  584. return arr[0];
  585. }
  586.  
  587.  
  588.  
  589.  
  590. }
  591.  
  592.  
  593. const getVG = (_yt_player) => {
  594. const w = 'VG';
  595.  
  596. let arr = [];
  597.  
  598. for (const [k, v] of Object.entries(_yt_player)) {
  599.  
  600. const p = typeof v === 'function' ? v.prototype : 0;
  601. if (p
  602. && typeof p.show === 'function' && p.show.length === 1
  603. && typeof p.hide === 'function' && p.hide.length === 0
  604. && typeof p.stop === 'function' && p.stop.length === 0) {
  605.  
  606. arr = addProtoToArr(_yt_player, k, arr) || arr;
  607.  
  608. }
  609.  
  610. }
  611.  
  612.  
  613. if (arr.length === 0) {
  614.  
  615. console.warn(`Key does not exist. [${w}]`);
  616. } else {
  617.  
  618. console.log(`[${w}]`, arr);
  619. return arr[0];
  620. }
  621.  
  622.  
  623.  
  624. }
  625.  
  626.  
  627. const getzo = (_yt_player) => {
  628. const w = 'zo';
  629.  
  630. let arr = [];
  631.  
  632. for (const [k, v] of Object.entries(_yt_player)) {
  633.  
  634. if (
  635. typeof v === 'function' && v.length === 3 && k.length < 3
  636. && (v + "").includes("a.style[b]=c")
  637. ) {
  638.  
  639. arr.push(k);
  640.  
  641. }
  642.  
  643. }
  644.  
  645.  
  646. if (arr.length === 0) {
  647.  
  648. console.warn(`Key does not exist. [${w}]`);
  649. } else {
  650.  
  651. console.log(`[${w}]`, arr);
  652. return arr[0];
  653. }
  654.  
  655. }
  656.  
  657. const addProtoToArr = (parent, key, arr) => {
  658.  
  659.  
  660. let isChildProto = false;
  661. for (const sr of arr) {
  662. if (parent[key].prototype instanceof parent[sr]) {
  663. isChildProto = true;
  664. break;
  665. }
  666. }
  667.  
  668. if (isChildProto) return;
  669.  
  670. arr = arr.filter(sr => {
  671. if (parent[sr].prototype instanceof parent[key]) {
  672. return false;
  673. }
  674. return true;
  675. });
  676.  
  677. arr.push(key);
  678.  
  679. return arr;
  680.  
  681.  
  682. }
  683.  
  684. const getuG = (_yt_player) => {
  685.  
  686. const w = 'uG';
  687.  
  688. let arr = [];
  689.  
  690. for (const [k, v] of Object.entries(_yt_player)) {
  691.  
  692.  
  693. const p = typeof v === 'function' ? v.prototype : 0;
  694.  
  695. if (p
  696. && typeof p.createElement === 'function' && p.createElement.length === 2
  697. && typeof p.detach === 'function' && p.detach.length === 0
  698. && typeof p.update === 'function' && p.update.length === 1
  699. && typeof p.updateValue === 'function' && p.updateValue.length === 2
  700. ) {
  701.  
  702. arr = addProtoToArr(_yt_player, k, arr) || arr;
  703.  
  704. }
  705.  
  706. }
  707.  
  708.  
  709.  
  710.  
  711.  
  712. if (arr.length === 0) {
  713.  
  714. console.warn(`Key does not exist. [${w}]`);
  715. } else {
  716.  
  717. console.log(`[${w}]`, arr);
  718. return arr[0];
  719. }
  720.  
  721. }
  722.  
  723.  
  724.  
  725. // << if FIX_schedulerInstanceInstance_ >>
  726.  
  727. let idleFrom = Date.now() + 2700;
  728. let slowMode = false;
  729.  
  730. let ytEvented = false;
  731.  
  732.  
  733. const setupEvents = FIX_schedulerInstanceInstance_V1 && !FIX_schedulerInstanceInstance_V2 ? () => {
  734.  
  735. document.addEventListener('yt-navigate', () => {
  736.  
  737. ytEvented = true;
  738. slowMode = false;
  739. idleFrom = Date.now() + 2700;
  740.  
  741. });
  742. document.addEventListener('yt-navigate-start', () => {
  743.  
  744. ytEvented = true;
  745. slowMode = false;
  746. idleFrom = Date.now() + 2700;
  747.  
  748. });
  749.  
  750. document.addEventListener('yt-page-type-changed', () => {
  751.  
  752. ytEvented = true;
  753. slowMode = false;
  754. idleFrom = Date.now() + 1700;
  755.  
  756. });
  757.  
  758.  
  759. document.addEventListener('yt-player-updated', () => {
  760.  
  761. ytEvented = true;
  762. slowMode = false;
  763. idleFrom = Date.now() + 1700;
  764.  
  765. });
  766.  
  767.  
  768. document.addEventListener('yt-page-data-fetched', () => {
  769.  
  770. ytEvented = true;
  771. slowMode = false;
  772. idleFrom = Date.now() + 1700;
  773.  
  774. });
  775.  
  776. document.addEventListener('yt-navigate-finish', () => {
  777.  
  778. ytEvented = true;
  779. slowMode = false;
  780. let t = Date.now() + 700;
  781. if (t > idleFrom) idleFrom = t;
  782.  
  783. });
  784.  
  785. document.addEventListener('yt-page-data-updated', () => {
  786.  
  787. ytEvented = true;
  788. slowMode = false;
  789. let t = Date.now() + 700;
  790. if (t > idleFrom) idleFrom = t;
  791.  
  792. });
  793.  
  794. document.addEventListener('yt-watch-comments-ready', () => {
  795.  
  796. ytEvented = true;
  797. slowMode = false;
  798. let t = Date.now() + 700;
  799. if (t > idleFrom) idleFrom = t;
  800.  
  801. });
  802. } : () => { };
  803.  
  804.  
  805. // << end >>
  806.  
  807. const cleanContext = async (win) => {
  808. const waitFn = requestAnimationFrame; // shall have been binded to window
  809. try {
  810. let mx = 16; // MAX TRIAL
  811. const frameId = 'vanillajs-iframe-v1';
  812. /** @type {HTMLIFrameElement | null} */
  813. let frame = document.getElementById(frameId);
  814. let removeIframeFn = null;
  815. if (!frame) {
  816. frame = document.createElement('iframe');
  817. frame.id = frameId;
  818. const blobURL = typeof webkitCancelAnimationFrame === 'function' ? (frame.src = URL.createObjectURL(new Blob([], { type: 'text/html' }))) : null; // avoid Brave Crash
  819. frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
  820. let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
  821. n.appendChild(frame);
  822. while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
  823. const root = document.documentElement;
  824. root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
  825. if (blobURL) Promise.resolve().then(() => URL.revokeObjectURL(blobURL));
  826.  
  827. removeIframeFn = (setTimeout) => {
  828. const removeIframeOnDocumentReady = (e) => {
  829. e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  830. win = null;
  831. const m = n;
  832. n = null;
  833. setTimeout(() => m.remove(), 200);
  834. }
  835. if (document.readyState !== 'loading') {
  836. removeIframeOnDocumentReady();
  837. } else {
  838. win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  839. }
  840. }
  841. }
  842. while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
  843. const fc = frame.contentWindow;
  844. if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
  845. const { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle } = fc;
  846. const res = { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle };
  847. for (let k in res) res[k] = res[k].bind(win); // necessary
  848. if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
  849. res.animate = fc.HTMLElement.prototype.animate;
  850. return res;
  851. } catch (e) {
  852. console.warn(e);
  853. return null;
  854. }
  855. };
  856.  
  857.  
  858. const promiseForCustomYtElementsReady = new Promise(onRegistryReady);
  859.  
  860. cleanContext(window).then(__CONTEXT__ => {
  861. if (!__CONTEXT__) return null;
  862.  
  863. const { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, animate, requestIdleCallback, getComputedStyle } = __CONTEXT__;
  864.  
  865. __requestAnimationFrame__ = requestAnimationFrame;
  866.  
  867. let rafPromise = null;
  868.  
  869. const getRafPromise = () => rafPromise || (rafPromise = new Promise(resolve => {
  870. requestAnimationFrame(hRes => {
  871. rafPromise = null;
  872. resolve(hRes);
  873. });
  874. }));
  875.  
  876. const getForegroundPromise = () => {
  877. if (document.visibilityState === 'visible') {
  878. return Promise.resolve();
  879. } else {
  880. return getRafPromise();
  881. }
  882. };
  883.  
  884. NO_PRELOAD_GENERATE_204_BYPASS || promiseForCustomYtElementsReady.then(() => {
  885. setTimeout(() => {
  886. NO_PRELOAD_GENERATE_204_BYPASS = true;
  887. }, 1270);
  888. });
  889.  
  890. const promiseForTamerTimeout = new Promise(resolve => {
  891. promiseForCustomYtElementsReady.then(() => {
  892. customElements.whenDefined('ytd-app').then(() => {
  893. setTimeout(resolve, 1200);
  894. });
  895. });
  896. setTimeout(resolve, 3000);
  897. });
  898.  
  899.  
  900. class RAFHub {
  901. constructor() {
  902. /** @type {number} */
  903. this.startAt = 8170;
  904. /** @type {number} */
  905. this.counter = 0;
  906. /** @type {number} */
  907. this.rid = 0;
  908. /** @type {Map<number, FrameRequestCallback>} */
  909. this.funcs = new Map();
  910. const funcs = this.funcs;
  911. /** @type {FrameRequestCallback} */
  912. this.bCallback = this.mCallback.bind(this);
  913. this.pClear = () => funcs.clear();
  914. }
  915. /** @param {DOMHighResTimeStamp} highResTime */
  916. mCallback(highResTime) {
  917. this.rid = 0;
  918. Promise.resolve().then(this.pClear);
  919. this.funcs.forEach(func => Promise.resolve(highResTime).then(func).catch(console.warn));
  920. }
  921. /** @param {FrameRequestCallback} f */
  922. request(f) {
  923. if (this.counter > 1e9) this.counter = 9;
  924. let cid = this.startAt + (++this.counter);
  925. this.funcs.set(cid, f);
  926. if (this.rid === 0) {
  927. console.log(2455)
  928. this.rid = requestAnimationFrame(this.bCallback);
  929. }
  930. return cid;
  931. }
  932. /** @param {number} cid */
  933. cancel(cid) {
  934. cid = +cid;
  935. if (cid > 0) {
  936. if (cid <= this.startAt) {
  937. return cancelAnimationFrame(cid);
  938. }
  939. if (this.rid > 0) {
  940. this.funcs.delete(cid);
  941. if (this.funcs.size === 0) {
  942. cancelAnimationFrame(this.rid);
  943. this.rid = 0;
  944. }
  945. }
  946. }
  947. }
  948. /** @param {number} cid */
  949. /** @param {FrameRequestCallback} f */
  950. replaceFunc(cid, f) {
  951. if (typeof this.funcs.get(cid) === 'function') {
  952. this.funcs.set(cid, f);
  953. return cid;
  954. } else {
  955. let r = this.request(f);
  956. this.cancel(cid);
  957. return r;
  958. }
  959. }
  960. }
  961.  
  962.  
  963.  
  964. NATIVE_CANVAS_ANIMATION && (() => {
  965.  
  966. HTMLCanvasElement.prototype.animate = animate;
  967.  
  968. let cid = setInterval(() => {
  969. HTMLCanvasElement.prototype.animate = animate;
  970. }, 1);
  971.  
  972. promiseForTamerTimeout.then(() => {
  973. clearInterval(cid)
  974. });
  975.  
  976. })();
  977.  
  978. FIX_ytAction_ && (async () => {
  979.  
  980. const ytdApp = await new Promise(resolve => {
  981.  
  982. promiseForCustomYtElementsReady.then(() => {
  983. customElements.whenDefined('ytd-app').then(() => {
  984. const ytdApp = document.querySelector('ytd-app');
  985. if (ytdApp) {
  986. resolve(ytdApp);
  987. return;
  988. }
  989. let mo = new MutationObserver(() => {
  990. const ytdApp = document.querySelector('ytd-app');
  991. if (!ytdApp) return;
  992. if (mo) {
  993. mo.disconnect();
  994. mo.takeRecords();
  995. mo = null;
  996. }
  997. resolve(ytdApp);
  998. });
  999. mo.observe(document, { subtree: true, childList: true });
  1000. });
  1001. });
  1002.  
  1003.  
  1004.  
  1005. });
  1006.  
  1007.  
  1008.  
  1009. if (!ytdApp) return;
  1010. const cProto = (ytdApp.inst || ytdApp).constructor.prototype;
  1011.  
  1012.  
  1013. if (!cProto) return;
  1014. let mbd = 0;
  1015.  
  1016. const fixer = (_ytdApp)=>{
  1017.  
  1018. const ytdApp = _ytdApp ? (_ytdApp.inst || _ytdApp) : null;
  1019.  
  1020. if (ytdApp && typeof ytdApp.onYtActionBoundListener_ === 'function' && !ytdApp.onYtActionBoundListener57_) {
  1021. ytdApp.onYtActionBoundListener57_ = ytdApp.onYtActionBoundListener_;
  1022. ytdApp.onYtActionBoundListener_ = ytdApp.onYtAction_.bind(ytdApp);
  1023. mbd++;
  1024. }
  1025.  
  1026.  
  1027. }
  1028.  
  1029. let cid = setInterval(() => {
  1030.  
  1031.  
  1032. if (typeof cProto.created === 'function' && !cProto.created57) {
  1033. cProto.created57 = cProto.created;
  1034. cProto.created = function (...args) {
  1035. const r = this.created57(...args);
  1036. fixer(this);
  1037. return r;
  1038. };
  1039. mbd++;
  1040. }
  1041.  
  1042.  
  1043. if (typeof cProto.onYtAction_ === 'function' && !cProto.onYtAction57_) {
  1044. cProto.onYtAction57_ = cProto.onYtAction_;
  1045. cProto.onYtAction_ = function (...args) {
  1046. Promise.resolve().then(() => this.onYtAction57_(...args));
  1047. };
  1048. mbd++;
  1049. }
  1050.  
  1051. if(ytdApp) fixer(ytdApp);
  1052.  
  1053. /*
  1054. const actionRouter_ = ytdApp ? ytdApp.actionRouter_ : null;
  1055. if (actionRouter_ && typeof actionRouter_.handleAction === 'function' && !actionRouter_.handleAction57) {
  1056. actionRouter_.handleAction57 = actionRouter_.handleAction;
  1057. actionRouter_.handleAction = function (...args) {
  1058. Promise.resolve().then(() => this.handleAction57(...args));
  1059. }
  1060. mbd++;
  1061. }
  1062. */
  1063.  
  1064. // if(mbd === 3) clearInterval(cid);
  1065. if (mbd >= 3) clearInterval(cid);
  1066.  
  1067. }, 1);
  1068.  
  1069. setTimeout(() => {
  1070.  
  1071. clearInterval(cid);
  1072. }, 1000);
  1073.  
  1074. })();
  1075.  
  1076.  
  1077. const generalEvtHandler = async (_evKey, _fvKey) => {
  1078.  
  1079. const evKey = `${_evKey}`;
  1080. const fvKey = `${_fvKey}`;
  1081.  
  1082.  
  1083. // const rafHub = new RAFHub();
  1084.  
  1085.  
  1086. const _yt_player = await new Promise(resolve => {
  1087.  
  1088. let cid = setInterval(() => {
  1089. let t = (((window || 0)._yt_player || 0) || 0);
  1090. if (t) {
  1091.  
  1092. clearInterval(cid);
  1093. resolve(t);
  1094. }
  1095. }, 1);
  1096.  
  1097. promiseForTamerTimeout.then(() => {
  1098. resolve(null)
  1099. });
  1100.  
  1101. });
  1102.  
  1103.  
  1104. if (!_yt_player || typeof _yt_player !== 'object') return;
  1105.  
  1106.  
  1107. const getArr = (_yt_player) => {
  1108.  
  1109. let arr = [];
  1110.  
  1111. for (const [k, v] of Object.entries(_yt_player)) {
  1112.  
  1113. const p = typeof v === 'function' ? v.prototype : 0;
  1114. if (p
  1115. && typeof p[evKey] === 'function' && p[evKey].length >= 0 && !p[fvKey]
  1116.  
  1117. ) {
  1118. arr = addProtoToArr(_yt_player, k, arr) || arr;
  1119.  
  1120. }
  1121.  
  1122. }
  1123.  
  1124. if (arr.length === 0) {
  1125.  
  1126. console.warn(`Key prop [${evKey}] does not exist.`);
  1127. } else {
  1128.  
  1129. return arr;
  1130. }
  1131.  
  1132. };
  1133.  
  1134. const arr = getArr(_yt_player);
  1135.  
  1136.  
  1137. if (!arr) return;
  1138.  
  1139. // console.log(`FIX_${evKey}`, arr);
  1140.  
  1141. const f = function (...args) {
  1142. Promise.resolve().then(() => this[fvKey](...args));
  1143. };
  1144.  
  1145.  
  1146. for (const k of arr) {
  1147.  
  1148. const g = _yt_player;
  1149. const gk = g[k];
  1150. const gkp = gk.prototype;
  1151.  
  1152. // console.log(237, k, gkp)
  1153.  
  1154. if (typeof gkp[evKey] == 'function' && !gkp[fvKey]) {
  1155. gkp[fvKey] = gkp[evKey];
  1156. gkp[evKey] = f;
  1157. }
  1158. }
  1159.  
  1160.  
  1161.  
  1162.  
  1163. }
  1164.  
  1165. FIX_onVideoDataChange && generalEvtHandler('onVideoDataChange', 'onVideoDataChange57');
  1166. // FIX_onClick && generalEvtHandler('onClick', 'onClick57');
  1167. FIX_onStateChange && generalEvtHandler('onStateChange', 'onStateChange57');
  1168. FIX_onLoopRangeChange && generalEvtHandler('onLoopRangeChange', 'onLoopRangeChange57');
  1169.  
  1170. CHANGE_appendChild && (() => {
  1171.  
  1172. HTMLElement.prototype.appendChild73 = HTMLElement.prototype.appendChild;
  1173. HTMLElement.prototype.appendChild = function (a) {
  1174.  
  1175. if (this instanceof HTMLElement) {
  1176. if (!NO_PRELOAD_GENERATE_204_BYPASS && document.head === this) {
  1177. for (let node = this.firstElementChild; node instanceof HTMLElement; node = node.nextElementSibling) {
  1178. if (node.nodeName === 'LINK' && node.rel === 'preload' && node.as === 'fetch' && !node.__m848__) {
  1179. node.__m848__ = 1;
  1180. node.rel = 'prefetch'; // see https://github.com/GoogleChromeLabs/quicklink
  1181. }
  1182. }
  1183. } else if (this.nodeName.startsWith('YT-')) { // yt-animated-rolling-number, yt-attributed-string
  1184. return this.appendChild73.apply(this, arguments);
  1185. }
  1186. if (a instanceof DocumentFragment) {
  1187. if (a.firstElementChild === null) return a;
  1188. }
  1189. }
  1190.  
  1191. return this.appendChild73.apply(this, arguments)
  1192. }
  1193.  
  1194.  
  1195. })();
  1196.  
  1197. if (FIX_Shady) {
  1198.  
  1199. let cidSL = setInterval(() => {
  1200. const { ShadyDOM, ShadyCSS } = window;
  1201. if (ShadyDOM && ShadyCSS) {
  1202. clearInterval(cidSL);
  1203. cidSL = 0;
  1204. }
  1205. if (ShadyDOM) {
  1206. ShadyDOM.handlesDynamicScoping = false; // 9 of 10
  1207. ShadyDOM.noPatch = true; // 1 of 10
  1208. ShadyDOM.patchOnDemand = false; // 1 of 10
  1209. ShadyDOM.preferPerformance = true; // 1 of 10
  1210. ShadyDOM.querySelectorImplementation = undefined; // 1 of 10
  1211. }
  1212. if (ShadyCSS) {
  1213. ShadyCSS.nativeCss = true; // 1 of 10
  1214. ShadyCSS.nativeShadow = true; // 6 of 10
  1215. ShadyCSS.cssBuild = undefined; // 1 of 10
  1216. ShadyCSS.disableRuntime = true; // 1 of 10
  1217. }
  1218. }, 1);
  1219.  
  1220. }
  1221.  
  1222.  
  1223. FIX_schedulerInstanceInstance_V1 && !FIX_schedulerInstanceInstance_V2 && (async () => {
  1224.  
  1225.  
  1226. const schedulerInstanceInstance_ = await new Promise(resolve => {
  1227.  
  1228. let cid = setInterval(() => {
  1229. let t = (((window || 0).ytglobal || 0).schedulerInstanceInstance_ || 0);
  1230. if (t) {
  1231.  
  1232. clearInterval(cid);
  1233. resolve(t);
  1234. }
  1235. }, 1);
  1236. promiseForTamerTimeout.then(() => {
  1237. resolve(null)
  1238. });
  1239. });
  1240.  
  1241. if (!schedulerInstanceInstance_) return;
  1242.  
  1243.  
  1244. if (!ytEvented) {
  1245. idleFrom = Date.now() + 2700;
  1246. slowMode = false; // integrity
  1247. }
  1248.  
  1249. const checkOK = typeof schedulerInstanceInstance_.start === 'function' && !schedulerInstanceInstance_.start991 && !schedulerInstanceInstance_.stop && !schedulerInstanceInstance_.cancel && !schedulerInstanceInstance_.terminate && !schedulerInstanceInstance_.interupt;
  1250. if (checkOK) {
  1251.  
  1252.  
  1253. schedulerInstanceInstance_.start991 = schedulerInstanceInstance_.start;
  1254.  
  1255. let requestingFn = null;
  1256. let requestingArgs = null;
  1257. let requestingDT = 0;
  1258.  
  1259. // let timerId = null;
  1260. const entries = [];
  1261. const f = function () {
  1262. requestingFn = this.fn;
  1263. requestingArgs = [...arguments];
  1264. requestingDT = Date.now();
  1265. entries.push({
  1266. fn: requestingFn,
  1267. args: requestingArgs,
  1268. t: requestingDT
  1269. });
  1270. // if (Date.now() < idleFrom) {
  1271. // timerId = this.fn.apply(window, arguments);
  1272. // } else {
  1273. // timerId = this.fn.apply(window, arguments);
  1274.  
  1275. // }
  1276. // timerId = 12377;
  1277. return 12377;
  1278. }
  1279.  
  1280.  
  1281. const fakeFns = [
  1282. f.bind({ fn: requestAnimationFrame }),
  1283. f.bind({ fn: setInterval }),
  1284. f.bind({ fn: setTimeout }),
  1285. f.bind({ fn: requestIdleCallback })
  1286. ]
  1287.  
  1288.  
  1289.  
  1290.  
  1291. let timerResolve = null;
  1292. setInterval(() => {
  1293. timerResolve && timerResolve();
  1294. timerResolve = null;
  1295. if (!slowMode && Date.now() > idleFrom) slowMode = true;
  1296. }, 250);
  1297.  
  1298. let mzt = 0;
  1299.  
  1300. let fnSelectorProp = null;
  1301.  
  1302. schedulerInstanceInstance_.start = function () {
  1303.  
  1304. const mk1 = window.requestAnimationFrame
  1305. const mk2 = window.setInterval
  1306. const mk3 = window.setTimeout
  1307. const mk4 = window.requestIdleCallback
  1308.  
  1309. const tThis = this['$$12378$$'] || this;
  1310.  
  1311.  
  1312. window.requestAnimationFrame = fakeFns[0]
  1313. window.setInterval = fakeFns[1]
  1314. window.setTimeout = fakeFns[2]
  1315. window.requestIdleCallback = fakeFns[3]
  1316.  
  1317. fnSelectorProp = null;
  1318.  
  1319.  
  1320. tThis.start991.call(new Proxy(tThis, {
  1321. get(target, prop, receiver) {
  1322. if (prop === '$$12377$$') return true;
  1323. if (prop === '$$12378$$') return target;
  1324.  
  1325. // console.log('get',prop)
  1326. return target[prop]
  1327. },
  1328. set(target, prop, value, receiver) {
  1329. // console.log('set', prop, value)
  1330.  
  1331.  
  1332. if (value >= 1 && value <= 4) fnSelectorProp = prop;
  1333. if (value === 12377 && fnSelectorProp) {
  1334.  
  1335. const originalSelection = target[fnSelectorProp];
  1336. const timerIdProp = prop;
  1337.  
  1338. /*
  1339.  
  1340.  
  1341. case 1:
  1342. var a = this.K;
  1343. this.g = this.I ? window.requestIdleCallback(a, {
  1344. timeout: 3E3
  1345. }) : window.setTimeout(a, ma);
  1346. break;
  1347. case 2:
  1348. this.g = window.setTimeout(this.M, this.N);
  1349. break;
  1350. case 3:
  1351. this.g = window.requestAnimationFrame(this.L);
  1352. break;
  1353. case 4:
  1354. this.g = window.setTimeout(this.J, 0)
  1355. }
  1356.  
  1357. */
  1358.  
  1359. const doForegroundSlowMode = () => {
  1360.  
  1361. const tir = ++mzt;
  1362. const f = requestingArgs[0];
  1363.  
  1364.  
  1365. getForegroundPromise().then(() => {
  1366.  
  1367.  
  1368. new Promise(r => {
  1369. timerResolve = r
  1370. }).then(() => {
  1371. if (target[timerIdProp] === -tir) f();
  1372. });
  1373.  
  1374. })
  1375.  
  1376. target[fnSelectorProp] = 931;
  1377. target[prop] = -tir;
  1378. }
  1379.  
  1380. if (target[fnSelectorProp] === 2 && requestingFn === setTimeout) {
  1381. if (slowMode && !(requestingArgs[1] > 250)) {
  1382.  
  1383. doForegroundSlowMode();
  1384.  
  1385. } else {
  1386. target[prop] = setTimeout.apply(window, requestingArgs);
  1387.  
  1388. }
  1389.  
  1390. } else if (target[fnSelectorProp] === 3 && requestingFn === requestAnimationFrame) {
  1391.  
  1392. if (slowMode) {
  1393.  
  1394. doForegroundSlowMode();
  1395.  
  1396. } else {
  1397. target[prop] = requestAnimationFrame.apply(window, requestingArgs);
  1398. }
  1399.  
  1400.  
  1401. } else if (target[fnSelectorProp] === 4 && requestingFn === setTimeout && !requestingArgs[1]) {
  1402.  
  1403. const f = requestingArgs[0];
  1404. const tir = ++mzt;
  1405. Promise.resolve().then(() => {
  1406. if (target[timerIdProp] === -tir) f();
  1407. });
  1408. target[fnSelectorProp] = 930;
  1409. target[prop] = -tir;
  1410.  
  1411. } else if (target[fnSelectorProp] === 1 && (requestingFn === requestIdleCallback || requestingFn === setTimeout)) {
  1412.  
  1413. doForegroundSlowMode();
  1414.  
  1415. } else {
  1416. // target[prop] = timerId;
  1417. target[fnSelectorProp] = 0;
  1418. target[prop] = 0;
  1419. }
  1420.  
  1421. // *****
  1422. // console.log('[[set]]', slowMode , prop, value, `fnSelectorProp: ${originalSelection} -> ${target[fnSelectorProp]}`)
  1423. } else {
  1424.  
  1425. target[prop] = value;
  1426. }
  1427. // console.log('set',prop,value)
  1428. return true;
  1429. }
  1430. }));
  1431.  
  1432. fnSelectorProp = null;
  1433.  
  1434.  
  1435. window.requestAnimationFrame = mk1;
  1436. window.setInterval = mk2
  1437. window.setTimeout = mk3
  1438. window.requestIdleCallback = mk4;
  1439.  
  1440.  
  1441.  
  1442. }
  1443.  
  1444. schedulerInstanceInstance_.start.toString = function () {
  1445. return schedulerInstanceInstance_.start991.toString();
  1446. }
  1447.  
  1448. // const funcNames = [...(schedulerInstanceInstance_.start + "").matchAll(/[\(,]this\.(\w{1,2})[,\)]/g)].map(e => e[1]).map(prop => ({
  1449. // prop,
  1450. // value: schedulerInstanceInstance_[prop],
  1451. // type: typeof schedulerInstanceInstance_[prop]
  1452.  
  1453. // }));
  1454. // console.log('fcc', funcNames)
  1455.  
  1456.  
  1457.  
  1458.  
  1459. }
  1460. })();
  1461.  
  1462.  
  1463.  
  1464. FIX_schedulerInstanceInstance_V2 && !FIX_schedulerInstanceInstance_V1 && (async () => {
  1465.  
  1466.  
  1467. const schedulerInstanceInstance_ = await new Promise(resolve => {
  1468.  
  1469. let cid = setInterval(() => {
  1470. let t = (((window || 0).ytglobal || 0).schedulerInstanceInstance_ || 0);
  1471. if (t) {
  1472.  
  1473. clearInterval(cid);
  1474. resolve(t);
  1475. }
  1476. }, 1);
  1477. promiseForTamerTimeout.then(() => {
  1478. resolve(null)
  1479. });
  1480. });
  1481.  
  1482. if (!schedulerInstanceInstance_) return;
  1483.  
  1484.  
  1485. const checkOK = typeof schedulerInstanceInstance_.start === 'function' && !schedulerInstanceInstance_.start991 && !schedulerInstanceInstance_.stop && !schedulerInstanceInstance_.cancel && !schedulerInstanceInstance_.terminate && !schedulerInstanceInstance_.interupt;
  1486. if (checkOK) {
  1487.  
  1488. schedulerInstanceInstance_.start991 = schedulerInstanceInstance_.start;
  1489.  
  1490.  
  1491.  
  1492. let busy = false;
  1493.  
  1494. // console.log('1667',schedulerInstanceInstance_.start);
  1495. schedulerInstanceInstance_.start = function () {
  1496.  
  1497. // p59 || console.log(location.pathname, 16400);
  1498.  
  1499. if (busy) {
  1500.  
  1501. return this.start991.call(this);
  1502.  
  1503. }
  1504.  
  1505. busy = true;
  1506.  
  1507. const mk1 = window.requestAnimationFrame
  1508. // const mk2 = window.setInterval
  1509. // const mk3 = window.setTimeout
  1510. // const mk4 = window.requestIdleCallback
  1511.  
  1512. // by pass Youtube Engine's wrapping
  1513. window.requestAnimationFrame = baseRAF;
  1514. // window.setInterval = setInterval
  1515. // window.setTimeout = setTimeout
  1516. // window.requestIdleCallback = requestIdleCallback
  1517.  
  1518.  
  1519. this.start991.call(this);
  1520.  
  1521.  
  1522. window.requestAnimationFrame = mk1;
  1523. // window.setInterval = mk2
  1524. // window.setTimeout = mk3
  1525. // window.requestIdleCallback = mk4;
  1526.  
  1527. busy = false;
  1528.  
  1529.  
  1530.  
  1531. }
  1532.  
  1533. schedulerInstanceInstance_.start.toString = function () {
  1534. return schedulerInstanceInstance_.start991.toString();
  1535. }
  1536.  
  1537. // const funcNames = [...(schedulerInstanceInstance_.start + "").matchAll(/[\(,]this\.(\w{1,2})[,\)]/g)].map(e => e[1]).map(prop => ({
  1538. // prop,
  1539. // value: schedulerInstanceInstance_[prop],
  1540. // type: typeof schedulerInstanceInstance_[prop]
  1541.  
  1542. // }));
  1543. // console.log('fcc', funcNames)
  1544.  
  1545.  
  1546.  
  1547.  
  1548. }
  1549. })();
  1550.  
  1551. FIX_yt_player && (async () => {
  1552.  
  1553.  
  1554.  
  1555. // const rafHub = new RAFHub();
  1556.  
  1557.  
  1558. const _yt_player = await new Promise(resolve => {
  1559.  
  1560. let cid = setInterval(() => {
  1561. let t = (((window || 0)._yt_player || 0) || 0);
  1562. if (t) {
  1563.  
  1564. clearInterval(cid);
  1565. resolve(t);
  1566. }
  1567. }, 1);
  1568.  
  1569. promiseForTamerTimeout.then(() => {
  1570. resolve(null)
  1571. });
  1572.  
  1573. });
  1574.  
  1575.  
  1576.  
  1577. if (!_yt_player || typeof _yt_player !== 'object') return;
  1578.  
  1579.  
  1580.  
  1581. let keyZq = getZq(_yt_player);
  1582. let keyVG = getVG(_yt_player);
  1583. let buildVG = _yt_player[keyVG];
  1584. let u = new buildVG({
  1585. api: {},
  1586. element: document.createElement('noscript'),
  1587. api: {},
  1588. hide: () => { }
  1589. }, 250);
  1590. const timeDelayConstructor = u.delay.constructor; // g.br
  1591. // console.log(keyVG, u)
  1592. // buildVG.prototype.show = function(){}
  1593. // _yt_player[keyZq] = g.k
  1594.  
  1595. if (!keyZq) return;
  1596.  
  1597.  
  1598. const g = _yt_player
  1599. let k = keyZq
  1600.  
  1601. const gk = g[k];
  1602. if (typeof gk !== 'function') return;
  1603. const gkp = gk.prototype;
  1604.  
  1605. let dummyObject = new gk;
  1606. let nilFunc = () => { };
  1607.  
  1608. let nilObj = {};
  1609.  
  1610. // console.log(1111111111)
  1611.  
  1612. let keyBoolD = '';
  1613. let keyWindow = '';
  1614. let keyFuncC = '';
  1615. let keyCidj = '';
  1616.  
  1617. for (const [t, y] of Object.entries(dummyObject)) {
  1618. if (y instanceof Window) keyWindow = t;
  1619. }
  1620.  
  1621. const dummyObjectProxyHandler = {
  1622. get(target, prop) {
  1623. let v = target[prop]
  1624. if (v instanceof Window && !keyWindow) {
  1625. keyWindow = t;
  1626. }
  1627. let y = typeof v === 'function' ? nilFunc : typeof v === 'object' ? nilObj : v;
  1628. if (prop === keyWindow) y = {
  1629. requestAnimationFrame(f) {
  1630. return 3;
  1631. },
  1632. cancelAnimationFrame() {
  1633.  
  1634. }
  1635. }
  1636. if (!keyFuncC && typeof v === 'function' && !(prop in target.constructor.prototype)) {
  1637. keyFuncC = prop;
  1638. }
  1639. // console.log('[get]', prop, typeof target[prop])
  1640.  
  1641.  
  1642. return y;
  1643. },
  1644. set(target, prop, value) {
  1645.  
  1646. if (typeof value === 'boolean' && !keyBoolD) {
  1647. keyBoolD = prop;
  1648. }
  1649. if (typeof value === 'number' && !keyCidj && value >= 2) {
  1650. keyCidj = prop;
  1651. }
  1652.  
  1653. // console.log('[set]', prop, value)
  1654. target[prop] = value
  1655.  
  1656. return true;
  1657. }
  1658. };
  1659.  
  1660. dummyObject.start.call(new Proxy(dummyObject, dummyObjectProxyHandler))
  1661.  
  1662. /*
  1663. console.log({
  1664. keyBoolD,
  1665. keyFuncC,
  1666. keyWindow,
  1667. keyCidj
  1668. })
  1669.  
  1670. console.log( dummyObject[keyFuncC])
  1671.  
  1672.  
  1673. console.log(2222222222)
  1674. */
  1675.  
  1676.  
  1677. // console.log('gkp.start',gkp.start);
  1678. // console.log('gkp.stop',gkp.stop);
  1679. gkp._activation = false;
  1680.  
  1681. gkp.start = function () {
  1682. // p59 || console.log(12100)
  1683. if (!this._activation) {
  1684. this._activation = true;
  1685. getRafPromise().then(() => {
  1686. this._activation = false;
  1687. if (this[keyCidj]) {
  1688. Promise.resolve().then(this[keyFuncC]);
  1689. }
  1690. });
  1691. }
  1692. this[keyCidj] = 1;
  1693. this[keyBoolD] = true;
  1694. }
  1695. ;
  1696. gkp.stop = function () {
  1697. this[keyCidj] = null
  1698. }
  1699.  
  1700.  
  1701. /*
  1702. g[k].start = function() {
  1703. this.stop();
  1704. this.D = true;
  1705. var a = requestAnimationFrame
  1706. , b = cancelAnimationFrame;
  1707. this.j = a.call(this.B, this.C)
  1708. }
  1709. ;
  1710. g[k].stop = function() {
  1711. if (this.isActive()) {
  1712. var a = requestAnimationFrame
  1713. , b = cancelAnimationFrame;
  1714. b.call(this.B, this.j)
  1715. }
  1716. this.j = null
  1717. }
  1718. */
  1719.  
  1720.  
  1721.  
  1722. const keyzo = PERF_471489_ ? getzo(_yt_player) : null;
  1723.  
  1724. if (keyzo) {
  1725.  
  1726. k = keyzo
  1727.  
  1728. const setCSSProp = (() => {
  1729.  
  1730. let animationPropCapable = false;
  1731. try {
  1732. const propName = "--ibxpf"
  1733. const value = 2;
  1734. const keyframes = [{
  1735. [propName]: value
  1736. }];
  1737. window.CSS.registerProperty({
  1738. name: "--ibxpf",
  1739. syntax: "<number>",
  1740. inherits: false,
  1741. initialValue: 1,
  1742. });
  1743. animationPropCapable = '1' === `${getComputedStyle(document.documentElement).getPropertyValue('--ibxpf')}`
  1744. } catch (e) { }
  1745.  
  1746. if (!animationPropCapable) {
  1747. return (element, cssProp, value) => {
  1748.  
  1749.  
  1750. element.style.setProperty(cssProp, value);
  1751.  
  1752. }
  1753. }
  1754.  
  1755. const propMaps = new Map();
  1756.  
  1757. function setCustomCSSProperty(element, propName, value) {
  1758. let wm = propMaps.get(propName);
  1759. if (!wm) {
  1760.  
  1761. try {
  1762. window.CSS.registerProperty({
  1763. name: propName,
  1764. syntax: "*",
  1765. inherits: false
  1766. });
  1767. } catch (e) {
  1768. console.warn(e);
  1769. }
  1770.  
  1771. propMaps.set(propName, (wm = new WeakMap()));
  1772. }
  1773.  
  1774. // Create the animation keyframes with the provided property and value
  1775. const keyframes = [{
  1776. [propName]: value
  1777. }];
  1778.  
  1779. let currentAnimation = wm.get(element);
  1780. if (currentAnimation) {
  1781.  
  1782. currentAnimation.effect.setKeyframes(keyframes);
  1783.  
  1784. } else {
  1785.  
  1786.  
  1787.  
  1788. // Set the animation on the element and immediately pause it
  1789. const animation = animate.call(element, keyframes, {
  1790. duration: 1, // Very short duration as we just want to set the value
  1791. fill: 'forwards',
  1792. iterationStart: 1,
  1793. iterations: 2,
  1794. direction: 'alternate'
  1795. });
  1796.  
  1797.  
  1798. // animation.currentTime = 1;
  1799. animation.pause();
  1800.  
  1801. wm.set(element, animation);
  1802.  
  1803.  
  1804. }
  1805.  
  1806. }
  1807.  
  1808. return setCustomCSSProperty;
  1809.  
  1810.  
  1811. })();
  1812.  
  1813.  
  1814. const attrUpdateFn = g[k];
  1815. g['$$original$$' + k] = attrUpdateFn;
  1816. g[k] = function (a, b, c) {
  1817.  
  1818. // console.log(140000, a, b, c);
  1819.  
  1820. let transformType = '';
  1821. let transformValue = 0;
  1822. let transformUnit = '';
  1823.  
  1824. let byPassDefaultFn = false;
  1825. if (b === "transform" && typeof c === 'string') {
  1826.  
  1827. byPassDefaultFn = true;
  1828.  
  1829. const aStyle = a.style;
  1830.  
  1831. // let beforeMq = aStyle.getPropertyValue('--mq-transform');
  1832. if (!(a instanceof HTMLElement)) return;
  1833. if (c.length === 0) {
  1834.  
  1835. } else if (c.startsWith('scalex(0.') || (c === 'scalex(0)' || c === 'scalex(1)')) {
  1836. let p = c.substring(7, c.length - 1);
  1837. let q = p.length >= 1 ? parseFloat(p) : -1;
  1838. if (q > -1e-5 && q < 1 + 1e-5) {
  1839. transformType = 'scalex'
  1840. transformValue = q;
  1841. transformUnit = '';
  1842. }
  1843.  
  1844.  
  1845. } else if (c.startsWith('translateX(') && c.endsWith('px)')) {
  1846.  
  1847. let p = c.substring(11, c.length - 3);
  1848. let q = p.length >= 1 ? parseFloat(p) : NaN;
  1849.  
  1850. if (typeof q === 'number' && !isNaN(q)) {
  1851. transformType = 'translateX'
  1852. transformValue = q;
  1853. transformUnit = 'px';
  1854. }
  1855.  
  1856.  
  1857. } else if (c.startsWith('scaley(0.') || (c === 'scaley(0)' || c === 'scaley(1)')) {
  1858. let p = c.substring(7, c.length - 1);
  1859. let q = p.length >= 1 ? parseFloat(p) : -1;
  1860. if (q > -1e-5 && q < 1 + 1e-5) {
  1861. transformType = 'scaley'
  1862. transformValue = q;
  1863. transformUnit = '';
  1864. }
  1865.  
  1866.  
  1867. } else if (c.startsWith('translateY(') && c.endsWith('px)')) {
  1868.  
  1869. let p = c.substring(11, c.length - 3);
  1870. let q = p.length >= 1 ? parseFloat(p) : NaN;
  1871.  
  1872. if (typeof q === 'number' && !isNaN(q)) {
  1873. transformType = 'translateY'
  1874. transformValue = q;
  1875. transformUnit = 'px';
  1876. }
  1877.  
  1878.  
  1879. }
  1880.  
  1881. if (transformType) {
  1882.  
  1883. if (transformType === 'scalex' || transformType === 'scaley') {
  1884.  
  1885. const q = transformValue;
  1886.  
  1887.  
  1888. /*
  1889.  
  1890. let vz = Math.round(steppingScaleN * q);
  1891. const customPropName = '--discrete-'+transformType
  1892.  
  1893. const currentValue = aStyle.getPropertyValue(customPropName);
  1894.  
  1895. const transform = (aStyle.transform || '');
  1896. const u = transform.includes(customPropName)
  1897. if (`${currentValue}` === `${vz}`) {
  1898. if (u) return;
  1899. }
  1900.  
  1901.  
  1902. setCSSProp(a,customPropName, vz);
  1903. // aStyle.setProperty(customPropName, vz)
  1904.  
  1905. let ck = '';
  1906.  
  1907. if (c.length === 9) ck = c;
  1908. else if (!u) ck = c.replace(/[.\d]+/, '0.5');
  1909.  
  1910. if (ck && beforeMq !== ck) {
  1911. aStyle.setProperty('--mq-transform', ck);
  1912. }
  1913.  
  1914. if (u) return;
  1915. c = `${transformType}(calc(var(--discrete-${transformType})/${steppingScaleN}))`;
  1916.  
  1917.  
  1918.  
  1919. */
  1920.  
  1921. const vz = +(Math.round(q * steppingScaleN) / steppingScaleN).toFixed(3);
  1922.  
  1923. c = `${transformType === 'scalex' ? 'scaleX' : 'scaleY'}(${vz})`
  1924. const cv = aStyle.transform;
  1925.  
  1926. // console.log(157, cv,c)
  1927.  
  1928. if (c === cv) return;
  1929. // console.log(257, cv,c)
  1930.  
  1931. aStyle.transform = c;
  1932.  
  1933. // return;
  1934.  
  1935. } else if (transformType === 'translateX' || transformType === 'translateY') {
  1936.  
  1937. const q = transformValue;
  1938.  
  1939. /*
  1940.  
  1941. let vz = q.toFixed(1);
  1942. const customPropName = '--discrete-'+transformType
  1943.  
  1944. const aStyle = a.style;
  1945. const currentValue = (aStyle.getPropertyValue(customPropName) || '').replace('px', '');
  1946.  
  1947.  
  1948. const transform = (aStyle.transform || '');
  1949. const u = transform.includes(customPropName)
  1950. if (parseFloat(currentValue).toFixed(1) === vz) {
  1951. if (u) return;
  1952. }
  1953.  
  1954. setCSSProp(a,customPropName, vz + 'px');
  1955. // aStyle.setProperty(customPropName, vz + 'px')
  1956.  
  1957. let ck = '';
  1958. if (c.length === 15) ck = c;
  1959. else if (!u) ck = c.replace(/[.\d]+/, '0.5');
  1960.  
  1961. if (ck && beforeMq !== ck) {
  1962. aStyle.setProperty('--mq-transform', ck);
  1963. }
  1964.  
  1965. if (u) return;
  1966. c = `${transformType}(var(--discrete-${transformType}))`;
  1967.  
  1968. */
  1969.  
  1970.  
  1971. const vz = +q.toFixed(1);
  1972.  
  1973. c = `${transformType}(${vz}${transformUnit})`
  1974. const cv = aStyle.transform;
  1975.  
  1976. // console.log(158, cv,c)
  1977.  
  1978. if (c === cv) return;
  1979. // console.log(258, cv,c)
  1980.  
  1981. aStyle.transform = c;
  1982.  
  1983. // return;
  1984.  
  1985. } else {
  1986. throw new Error();
  1987. }
  1988.  
  1989. } else {
  1990. // if(beforeMq) a.style.setProperty('--mq-transform', '');
  1991. const cv = aStyle.transform
  1992. if (!c && !cv) return;
  1993. else if (c === cv) return;
  1994. aStyle.transform = c;
  1995. // return;
  1996. }
  1997.  
  1998. } else if (b === "display") {
  1999.  
  2000. const cv = a.style.display;
  2001. if (!cv && !c) return;
  2002. if (cv === c) return;
  2003.  
  2004.  
  2005. } else if (b === "width") {
  2006.  
  2007. const cv = a.style.width;
  2008. if (!cv && !c) return;
  2009. if (cv === c) return;
  2010.  
  2011. }
  2012.  
  2013. // console.log(130000, a, b, c);
  2014.  
  2015. if (byPassDefaultFn) return;
  2016. return attrUpdateFn.call(this, a, b, c);
  2017. }
  2018.  
  2019.  
  2020. /*
  2021.  
  2022. g.zo = function(a, b, c) {
  2023. if ("string" === typeof b)
  2024. (b = yo(a, b)) && (a.style[b] = c);
  2025. else
  2026. for (var d in b) {
  2027. c = a;
  2028. var e = b[d]
  2029. , f = yo(c, d);
  2030. f && (c.style[f] = e)
  2031. }
  2032. }
  2033.  
  2034.  
  2035. */
  2036.  
  2037.  
  2038. }
  2039.  
  2040.  
  2041.  
  2042. const keyuG = PERF_471489_ ? getuG(_yt_player) : null;
  2043.  
  2044. if (keyuG) {
  2045.  
  2046. k = keyuG;
  2047.  
  2048. const gk = g[k];
  2049. const gkp = gk.prototype;
  2050.  
  2051.  
  2052. /** @type { Map<string, WeakMap<any, any>> } */
  2053. const ntLogs = new Map();
  2054.  
  2055. if (typeof gkp.updateValue === 'function' && gkp.updateValue.length === 2 && !gkp.updateValue31) {
  2056.  
  2057. gkp.updateValue31 = gkp.updateValue;
  2058. gkp.updateValue = function (a, b) {
  2059. if (typeof a !== 'string') return this.updateValue31(a, b);
  2060.  
  2061. const element = this.element;
  2062. if (!(element instanceof HTMLElement)) return this.updateValue31(a, b);
  2063.  
  2064. let ntLog = ntLogs.get(a);
  2065. if (!ntLog) ntLogs.set(a, (ntLog = new WeakMap()));
  2066.  
  2067. let cache = ntLog.get(element);
  2068. if (cache && cache.value === b) {
  2069. return;
  2070. }
  2071. if (!cache) {
  2072. this.__oldValueByUpdateValue__ = null;
  2073. ntLog.set(element, cache = { value: b });
  2074. } else {
  2075. this.__oldValueByUpdateValue__ = cache.value;
  2076. cache.value = b;
  2077. }
  2078.  
  2079.  
  2080. return this.updateValue31(a, b);
  2081. }
  2082.  
  2083.  
  2084. /*
  2085. g.k.update = function(a) {
  2086. for (var b = g.u(Object.keys(a)), c = b.next(); !c.done; c = b.next())
  2087. c = c.value,
  2088. this.updateValue(c, a[c])
  2089. }
  2090. ;
  2091. g.k.updateValue = function(a, b) {
  2092. (a = this.Td["{{" + a + "}}"]) && wG(this, a[0], a[1], b)
  2093. }
  2094. */
  2095.  
  2096.  
  2097. }
  2098.  
  2099.  
  2100. }
  2101.  
  2102.  
  2103.  
  2104.  
  2105. })();
  2106.  
  2107.  
  2108.  
  2109. FIX_Animation_n_timeline && (async () => {
  2110.  
  2111.  
  2112. const timeline = await new Promise(resolve => {
  2113.  
  2114. let cid = setInterval(() => {
  2115. let t = (((document || 0).timeline || 0) || 0);
  2116. if (t && typeof t._play === 'function') {
  2117.  
  2118. clearInterval(cid);
  2119. resolve(t);
  2120. }
  2121. }, 1);
  2122.  
  2123. promiseForTamerTimeout.then(() => {
  2124. resolve(null)
  2125. });
  2126.  
  2127. });
  2128.  
  2129.  
  2130. const Animation = await new Promise(resolve => {
  2131.  
  2132. let cid = setInterval(() => {
  2133. let t = (((window || 0).Animation || 0) || 0);
  2134. if (t && typeof t === 'function' && t.length === 2 && typeof t.prototype._updatePromises === 'function') {
  2135.  
  2136. clearInterval(cid);
  2137. resolve(t);
  2138. }
  2139. }, 1);
  2140.  
  2141. promiseForTamerTimeout.then(() => {
  2142. resolve(null)
  2143. });
  2144.  
  2145. });
  2146.  
  2147. if (!timeline) return;
  2148. if (!Animation) return;
  2149.  
  2150. const aniProto = Animation.prototype;
  2151. // aniProto.sequenceNumber = 0; // native YouTube engine bug - sequenceNumber is not set
  2152.  
  2153. const getXroto = (x) => {
  2154. try {
  2155. return x.__proto__;
  2156. } catch (e) { }
  2157. return null;
  2158. }
  2159. const timProto = getXroto(timeline);
  2160. if (!timProto) return;
  2161. if (
  2162. (
  2163. typeof timProto.getAnimations === 'function' && typeof timProto.play === 'function' &&
  2164. typeof timProto._discardAnimations === 'function' && typeof timProto._play === 'function' &&
  2165. typeof timProto._updateAnimationsPromises === 'function' && !timProto.nofCQ &&
  2166. typeof aniProto._updatePromises === 'function' && !aniProto.nofYH
  2167. )
  2168.  
  2169. ) {
  2170.  
  2171. timProto.nofCQ = 1;
  2172. aniProto.nofYH = 1;
  2173.  
  2174. const originalAnimationsWithPromises = ((_updateAnimationsPromises) => {
  2175.  
  2176.  
  2177. /*
  2178. v.animationsWithPromises = v.animationsWithPromises.filter(function (c) {
  2179. return c._updatePromises();
  2180. });
  2181. */
  2182.  
  2183. const p = Array.prototype.filter;
  2184.  
  2185. let res = null;
  2186. Array.prototype.filter = function () {
  2187.  
  2188. res = this;
  2189. return this;
  2190.  
  2191. };
  2192.  
  2193. _updateAnimationsPromises.call({});
  2194.  
  2195. Array.prototype.filter = p;
  2196.  
  2197. if (res && typeof res.length === 'number') {
  2198. /** @type {any[]} */
  2199. const _res = res;
  2200. return _res;
  2201. }
  2202.  
  2203.  
  2204. return null;
  2205.  
  2206.  
  2207.  
  2208.  
  2209. })(timProto._updateAnimationsPromises);
  2210.  
  2211. if (!originalAnimationsWithPromises || typeof originalAnimationsWithPromises.length !== 'number') return;
  2212.  
  2213. // console.log('originalAnimationsWithPromises', originalAnimationsWithPromises)
  2214.  
  2215. aniProto._updatePromises31 = aniProto._updatePromises;
  2216.  
  2217. /*
  2218. aniProto._updatePromises = function(){
  2219. console.log('eff',this._oldPlayState, this.playState)
  2220. return this._updatePromises31.apply(this, arguments)
  2221. }
  2222. */
  2223.  
  2224. aniProto._updatePromises = function () {
  2225. var oldPlayState = this._oldPlayState;
  2226. var newPlayState = this.playState;
  2227. // console.log('ett', oldPlayState, newPlayState)
  2228. if (newPlayState !== oldPlayState) {
  2229. this._oldPlayState = newPlayState;
  2230. if (this._readyPromise) {
  2231. if ("idle" == newPlayState) {
  2232. this._rejectReadyPromise();
  2233. this._readyPromise = void 0;
  2234. } else if ("pending" == oldPlayState) {
  2235. this._resolveReadyPromise();
  2236. } else if ("pending" == newPlayState) {
  2237. this._readyPromise = void 0;
  2238. }
  2239. }
  2240. if (this._finishedPromise) {
  2241. if ("idle" == newPlayState) {
  2242. this._rejectFinishedPromise();
  2243. this._finishedPromise = void 0;
  2244. } else if ("finished" == newPlayState) {
  2245. this._resolveFinishedPromise();
  2246. } else if ("finished" == oldPlayState) {
  2247. this._finishedPromise = void 0;
  2248. }
  2249. }
  2250. }
  2251. return this._readyPromise || this._finishedPromise;
  2252. };
  2253.  
  2254.  
  2255. let restartWebAnimationsNextTickFlag = false;
  2256.  
  2257. const looperMethodT = () => {
  2258.  
  2259. const runnerFn = (hRes) => {
  2260. var b = timeline;
  2261. b.currentTime = hRes;
  2262. b._discardAnimations();
  2263. if (0 == b._animations.length) {
  2264. restartWebAnimationsNextTickFlag = false;
  2265. } else {
  2266. getRafPromise().then(runnerFn);
  2267. }
  2268. }
  2269.  
  2270. const restartWebAnimationsNextTick = () => {
  2271. if (!restartWebAnimationsNextTickFlag) {
  2272. restartWebAnimationsNextTickFlag = true;
  2273. getRafPromise().then(runnerFn);
  2274. }
  2275. }
  2276.  
  2277. return { restartWebAnimationsNextTick }
  2278. };
  2279.  
  2280.  
  2281. const looperMethodN = () => {
  2282.  
  2283. const acs = document.createElement('a-f');
  2284. acs.id = 'a-f';
  2285.  
  2286. const style = document.createElement('style');
  2287. style.textContent = `
  2288. @keyFrames aF1 {
  2289. 0% {
  2290. order: 0;
  2291. }
  2292. 100% {
  2293. order: 6;
  2294. }
  2295. }
  2296. #a-f[id] {
  2297. visibility: collapse !important;
  2298. position: fixed !important;
  2299. top: -100px !important;
  2300. left: -100px !important;
  2301. margin:0 !important;
  2302. padding:0 !important;
  2303. outline:0 !important;
  2304. border:0 !important;
  2305. z-index:-1 !important;
  2306. width: 0px !important;
  2307. height: 0px !important;
  2308. contain: strict !important;
  2309. pointer-events: none !important;
  2310. animation: 1ms steps(2) 0ms infinite alternate forwards running aF1 !important;
  2311. }
  2312. `;
  2313. (document.head || document.documentElement).appendChild(style);
  2314.  
  2315. document.documentElement.insertBefore(acs, document.documentElement.firstChild);
  2316.  
  2317. const _onanimationiteration = function (evt) {
  2318. const hRes = evt.timeStamp;
  2319. var b = timeline;
  2320. b.currentTime = hRes;
  2321. b._discardAnimations();
  2322. if (0 == b._animations.length) {
  2323. restartWebAnimationsNextTickFlag = false;
  2324. acs.onanimationiteration = null;
  2325. } else {
  2326. acs.onanimationiteration = _onanimationiteration;
  2327. }
  2328.  
  2329. }
  2330.  
  2331.  
  2332.  
  2333. const restartWebAnimationsNextTick = () => {
  2334. if (!restartWebAnimationsNextTickFlag) {
  2335. restartWebAnimationsNextTickFlag = true;
  2336. acs.onanimationiteration = _onanimationiteration;
  2337.  
  2338. }
  2339. }
  2340.  
  2341. return { restartWebAnimationsNextTick }
  2342. };
  2343.  
  2344.  
  2345.  
  2346. const { restartWebAnimationsNextTick } = ('onanimationiteration' in document.documentElement) ? looperMethodN() : looperMethodT();
  2347.  
  2348.  
  2349. // console.log(571, timProto);
  2350. timProto._play = function (c) {
  2351. c = new Animation(c, this);
  2352. this._animations.push(c);
  2353. restartWebAnimationsNextTick();
  2354. c._updatePromises();
  2355. c._animation.play();
  2356. c._updatePromises();
  2357. return c
  2358. }
  2359.  
  2360. const animationsWithPromisesMap = new Set(originalAnimationsWithPromises);
  2361. originalAnimationsWithPromises.length = 0;
  2362. originalAnimationsWithPromises.push = null;
  2363. originalAnimationsWithPromises.splice = null;
  2364. originalAnimationsWithPromises.slice = null;
  2365. originalAnimationsWithPromises.indexOf = null;
  2366. originalAnimationsWithPromises.unshift = null;
  2367. originalAnimationsWithPromises.shift = null;
  2368. originalAnimationsWithPromises.pop = null;
  2369. originalAnimationsWithPromises.filter = null;
  2370. originalAnimationsWithPromises.forEach = null;
  2371. originalAnimationsWithPromises.map = null;
  2372.  
  2373.  
  2374. const _updateAnimationsPromises = () => {
  2375. animationsWithPromisesMap.forEach(c => {
  2376. if (!c._updatePromises()) animationsWithPromisesMap.delete(c);
  2377. });
  2378. /*
  2379. v.animationsWithPromises = v.animationsWithPromises.filter(function (c) {
  2380. return c._updatePromises();
  2381. });
  2382. */
  2383. }
  2384.  
  2385. timProto._updateAnimationsPromises31 = timProto._updateAnimationsPromises;
  2386.  
  2387. timProto._updateAnimationsPromises = _updateAnimationsPromises;
  2388.  
  2389. delete timProto._updateAnimationsPromises;
  2390. Object.defineProperty(timProto, '_updateAnimationsPromises', {
  2391. get() {
  2392. if (animationsWithPromisesMap.size === 0) return nilFn;
  2393. return _updateAnimationsPromises;
  2394. },
  2395. set(nv) {
  2396. delete this._updateAnimationsPromises;
  2397. this._updateAnimationsPromises = nv;
  2398. },
  2399. enumerable: true,
  2400. configurable: true,
  2401. });
  2402.  
  2403.  
  2404. let pdFinished = Object.getOwnPropertyDescriptor(aniProto, 'finished');
  2405. aniProto.__finished_native_get__ = pdFinished.get;
  2406. if (typeof pdFinished.get === 'function' && !pdFinished.set && pdFinished.configurable === true && pdFinished.enumerable === true) {
  2407.  
  2408.  
  2409. Object.defineProperty(aniProto, 'finished', {
  2410. get() {
  2411. this._finishedPromise || (!animationsWithPromisesMap.has(this) && animationsWithPromisesMap.add(this),
  2412. this._finishedPromise = new Promise((resolve, reject) => {
  2413. this._resolveFinishedPromise = function () {
  2414. resolve(this)
  2415. };
  2416. this._rejectFinishedPromise = function () {
  2417. reject({
  2418. type: DOMException.ABORT_ERR,
  2419. name: "AbortError"
  2420. })
  2421. };
  2422. }),
  2423. "finished" == this.playState && this._resolveFinishedPromise());
  2424. return this._finishedPromise
  2425. },
  2426. set: undefined,
  2427. enumerable: true,
  2428. configurable: true
  2429. });
  2430.  
  2431. }
  2432.  
  2433.  
  2434.  
  2435. let pdReady = Object.getOwnPropertyDescriptor(aniProto, 'ready');
  2436. aniProto.__ready_native_get__ = pdReady.get;
  2437. if (typeof pdReady.get === 'function' && !pdReady.set && pdReady.configurable === true && pdReady.enumerable === true) {
  2438.  
  2439. Object.defineProperty(aniProto, 'ready', {
  2440. get() {
  2441. this._readyPromise || (!animationsWithPromisesMap.has(this) && animationsWithPromisesMap.add(this),
  2442. this._readyPromise = new Promise((resolve, reject) => {
  2443. this._resolveReadyPromise = function () {
  2444. resolve(this)
  2445. };
  2446. this._rejectReadyPromise = function () {
  2447. reject({
  2448. type: DOMException.ABORT_ERR,
  2449. name: "AbortError"
  2450. })
  2451. };
  2452. }),
  2453. "pending" !== this.playState && this._resolveReadyPromise());
  2454. return this._readyPromise
  2455. },
  2456. set: undefined,
  2457. enumerable: true,
  2458. configurable: true
  2459. });
  2460.  
  2461. }
  2462.  
  2463.  
  2464. if (IGNORE_bindAnimationForCustomEffect && typeof aniProto._rebuildUnderlyingAnimation === 'function' && !aniProto._rebuildUnderlyingAnimation21 && aniProto._rebuildUnderlyingAnimation.length === 0) {
  2465.  
  2466. aniProto._rebuildUnderlyingAnimation21 = aniProto._rebuildUnderlyingAnimation;
  2467. const _rebuildUnderlyingAnimation = function () {
  2468. // if (isNaN(this._sequenceNumber)) return; // do not rebuild underlying animation if native animation is used.
  2469. this.effect && this.effect._onsample && (this.effect._onsample = null);
  2470. return this._rebuildUnderlyingAnimation21();
  2471. }
  2472. aniProto._rebuildUnderlyingAnimation = _rebuildUnderlyingAnimation;
  2473. // delete aniProto._rebuildUnderlyingAnimation;
  2474. // Object.defineProperty(aniProto, '_rebuildUnderlyingAnimation', {
  2475. // get() {
  2476. // if (isNaN(this._sequenceNumber)) return nilFn;
  2477. // return this._rebuildUnderlyingAnimation21;
  2478. // },
  2479. // set(nv) {
  2480. // delete this._rebuildUnderlyingAnimation;
  2481. // this._rebuildUnderlyingAnimation = nv;
  2482. // },
  2483. // enumerable: true,
  2484. // configurable: true
  2485. // });
  2486. }
  2487.  
  2488.  
  2489. /*
  2490.  
  2491.  
  2492. function f(c) {
  2493. var b = v.timeline;
  2494. b.currentTime = c;
  2495. b._discardAnimations();
  2496. 0 == b._animations.length ? d = !1 : requestAnimationFrame(f)
  2497. }
  2498. var h = window.requestAnimationFrame;
  2499. window.requestAnimationFrame = function(c) {
  2500. return h(function(b) {
  2501. v.timeline._updateAnimationsPromises();
  2502. c(b);
  2503. v.timeline._updateAnimationsPromises()
  2504. })
  2505. }
  2506. ;
  2507. v.AnimationTimeline = function() {
  2508. this._animations = [];
  2509. this.currentTime = void 0
  2510. }
  2511. ;
  2512. v.AnimationTimeline.prototype = {
  2513. getAnimations: function() {
  2514. this._discardAnimations();
  2515. return this._animations.slice()
  2516. },
  2517. _updateAnimationsPromises: function() {
  2518. v.animationsWithPromises = v.animationsWithPromises.filter(function(c) {
  2519. return c._updatePromises()
  2520. })
  2521. },
  2522. _discardAnimations: function() {
  2523. this._updateAnimationsPromises();
  2524. this._animations = this._animations.filter(function(c) {
  2525. return "finished" != c.playState && "idle" != c.playState
  2526. })
  2527. },
  2528. _play: function(c) {
  2529. c = new v.Animation(c,this);
  2530. this._animations.push(c);
  2531. v.restartWebAnimationsNextTick();
  2532. c._updatePromises();
  2533. c._animation.play();
  2534. c._updatePromises();
  2535. return c
  2536. },
  2537. play: function(c) {
  2538. c && c.remove();
  2539. return this._play(c)
  2540. }
  2541. };
  2542. var d = !1;
  2543. v.restartWebAnimationsNextTick = function() {
  2544. d || (d = !0,
  2545. requestAnimationFrame(f))
  2546. }
  2547. ;
  2548. var a = new v.AnimationTimeline;
  2549. v.timeline = a;
  2550. try {
  2551. Object.defineProperty(window.document, "timeline", {
  2552. configurable: !0,
  2553. get: function() {
  2554. return a
  2555. }
  2556. })
  2557. } catch (c) {}
  2558. try {
  2559. window.document.timeline = a
  2560. } catch (c) {}
  2561.  
  2562. */
  2563.  
  2564.  
  2565.  
  2566. /*
  2567.  
  2568. var g = window.getComputedStyle;
  2569. Object.defineProperty(window, "getComputedStyle", {
  2570. configurable: !0,
  2571. enumerable: !0,
  2572. value: function() {
  2573. v.timeline._updateAnimationsPromises();
  2574. var e = g.apply(this, arguments);
  2575. h() && (e = g.apply(this, arguments));
  2576. v.timeline._updateAnimationsPromises();
  2577. return e
  2578. }
  2579. });
  2580.  
  2581. */
  2582.  
  2583.  
  2584.  
  2585.  
  2586. }
  2587.  
  2588.  
  2589.  
  2590.  
  2591. })();
  2592.  
  2593.  
  2594.  
  2595.  
  2596. promiseForCustomYtElementsReady.then(() => {
  2597.  
  2598. FIX_ytdExpander_childrenChanged && customElements.whenDefined('ytd-expander').then(() => {
  2599.  
  2600.  
  2601.  
  2602. let dummy;
  2603. let cProto;
  2604.  
  2605.  
  2606.  
  2607. dummy = document.createElement('ytd-expander');
  2608. cProto = (dummy.inst || dummy).constructor.prototype;
  2609.  
  2610.  
  2611. if (fnIntegrity(cProto.initChildrenObserver, '0.48.21') && fnIntegrity(cProto.childrenChanged, '0.40.22')) {
  2612.  
  2613.  
  2614. cProto.initChildrenObserver14 = cProto.initChildrenObserver;
  2615. cProto.childrenChanged14 = cProto.childrenChanged;
  2616.  
  2617. cProto.initChildrenObserver = function () {
  2618. var a = this;
  2619. this.observer = new MutationObserver(function () {
  2620. a.childrenChanged()
  2621. }
  2622. );
  2623. this.observer.observe(this.content, {
  2624. subtree: !0,
  2625. childList: !0,
  2626. attributes: !0,
  2627. characterData: !0
  2628. });
  2629. this.childrenChanged()
  2630. }
  2631. ;
  2632. cProto.childrenChanged = function () {
  2633. if (this.alwaysToggleable) {
  2634. this.canToggle = this.alwaysToggleable;
  2635. } else if (!this.canToggleJobId) {
  2636. this.canToggleJobId = 1;
  2637. getRafPromise().then(() => {
  2638. this.canToggleJobId = 0;
  2639. this.calculateCanCollapse()
  2640. })
  2641. }
  2642. }
  2643.  
  2644.  
  2645. // console.log(cProto.initChildrenObserver)
  2646. console.debug('ytd-expander-fix-childrenChanged');
  2647.  
  2648. }
  2649.  
  2650. })
  2651.  
  2652.  
  2653.  
  2654. FIX_paper_ripple_animate && customElements.whenDefined('paper-ripple').then(() => {
  2655.  
  2656.  
  2657.  
  2658. let dummy;
  2659. let cProto;
  2660. dummy = document.createElement('paper-ripple');
  2661. cProto = (dummy.inst || dummy).constructor.prototype;
  2662.  
  2663. if (fnIntegrity(cProto.animate, '0.74.5')) {
  2664.  
  2665.  
  2666. cProto.animate34 = cProto.animate;
  2667. cProto.animate = function () {
  2668. if (this._animating) {
  2669. var a;
  2670. const ripples = this.ripples;
  2671. for (a = 0; a < ripples.length; ++a) {
  2672. var b = ripples[a];
  2673. b.draw();
  2674. this.$.background.style.opacity = b.outerOpacity;
  2675. b.isOpacityFullyDecayed && !b.isRestingAtMaxRadius && this.removeRipple(b)
  2676. }
  2677. if ((this.shouldKeepAnimating || 0) !== ripples.length) {
  2678. if (!this._boundAnimate38) this._boundAnimate38 = this.animate.bind(this);
  2679. getRafPromise().then(this._boundAnimate38);
  2680. } else {
  2681. this.onAnimationComplete()
  2682. }
  2683. }
  2684. }
  2685.  
  2686. console.debug('FIX_paper_ripple_animate')
  2687.  
  2688. // console.log(cProto.animate)
  2689.  
  2690. }
  2691.  
  2692. });
  2693.  
  2694. if (FIX_doIdomRender) {
  2695.  
  2696.  
  2697. const xsetTimeout = function (f, d) {
  2698. if (xsetTimeout.m511 === 1 && !d) {
  2699. xsetTimeout.m511 = 2;
  2700. getRafPromise().then(f);
  2701. } else {
  2702. return setTimeout.apply(window, arguments)
  2703. }
  2704.  
  2705. }
  2706.  
  2707. const xrequestAnimationFrame = function (f) {
  2708. const h = f + "";
  2709. if (h.startsWith("function(){setTimeout(function(){") && h.endsWith("})}")) {
  2710. xsetTimeout.m511 = 1;
  2711. f();
  2712. xsetTimeout.m511 = 0;
  2713. } else if (h.includes("requestAninmationFrameResolver")) {
  2714. getRafPromise().then(f);
  2715. } else {
  2716. return requestAnimationFrame.apply(window, arguments);
  2717. }
  2718. }
  2719.  
  2720. let busy = false;
  2721. const doIdomRender = function () {
  2722. if (busy) {
  2723. return this.doIdomRender13.apply(this, arguments);
  2724. }
  2725. busy = true;
  2726. const { requestAnimationFrame, setTimeout } = window;
  2727. window.requestAnimationFrame = xrequestAnimationFrame;
  2728. window.setTimeout = xsetTimeout;
  2729. let r = this.doIdomRender13.apply(this, arguments);
  2730. window.requestAnimationFrame = requestAnimationFrame;
  2731. window.setTimeout = setTimeout;
  2732. busy = false;
  2733. return r;
  2734. };
  2735. for (const ytTag of ['ytd-lottie-player', 'yt-attributed-string', 'yt-image', 'yt-icon-shape', 'yt-button-shape', 'yt-button-view-model', 'yt-icon-badge-shape']) {
  2736.  
  2737.  
  2738. customElements.whenDefined(ytTag).then(() => {
  2739.  
  2740. let dummy;
  2741. let cProto;
  2742. dummy = document.createElement(ytTag);
  2743. cProto = (dummy.inst || dummy).constructor.prototype;
  2744.  
  2745. cProto.doIdomRender13 = cProto.doIdomRender;
  2746. cProto.doIdomRender = doIdomRender;
  2747.  
  2748. if (cProto.doIdomRender13 === cProto.templatingFn) cProto.templatingFn = doIdomRender;
  2749.  
  2750. console.debug('FIX_doIdomRender', ytTag)
  2751.  
  2752.  
  2753.  
  2754. });
  2755.  
  2756. }
  2757.  
  2758. }
  2759.  
  2760.  
  2761.  
  2762. });
  2763.  
  2764. });
  2765.  
  2766.  
  2767. setupEvents();
  2768.  
  2769.  
  2770.  
  2771. if (isMainWindow) {
  2772.  
  2773. console.groupCollapsed(
  2774. "%cYouTube JS Engine Tamer",
  2775. "background-color: #EDE43B ; color: #000 ; font-weight: bold ; padding: 4px ;"
  2776. );
  2777.  
  2778.  
  2779.  
  2780. console.log("Script is loaded.");
  2781. console.log("This script changes the core mechanisms of the YouTube JS engine.");
  2782.  
  2783. console.log("This script is experimental and subject to further changes.");
  2784.  
  2785. console.log("This might boost your YouTube performance.");
  2786.  
  2787. console.log("CAUTION: This might break your YouTube.");
  2788.  
  2789.  
  2790. if (prepareLogs.length >= 1) {
  2791. console.log(" =========================================================================== ");
  2792.  
  2793. for (const msg of prepareLogs) {
  2794. console.log(msg)
  2795. }
  2796.  
  2797. console.log(" =========================================================================== ");
  2798. }
  2799.  
  2800. console.groupEnd();
  2801.  
  2802. }
  2803.  
  2804.  
  2805.  
  2806.  
  2807.  
  2808.  
  2809. })();