YouTube JS Engine Tamer

To enhance YouTube performance by modifying YouTube JS Engine

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

  1. // ==UserScript==
  2. // @name YouTube JS Engine Tamer
  3. // @namespace UserScripts
  4. // @match https://www.youtube.com/*
  5. // @version 0.4.1
  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 = true; // for #cinematics
  20. const FIX_schedulerInstanceInstance_ = true;
  21. const FIX_yt_player = true;
  22. const FIX_Animation_n_timeline = true;
  23.  
  24. // << if FIX_yt_player >>
  25.  
  26. // credit to @nopeless (https://greasyfork.org/scripts/471489-youtube-player-perf/)
  27. const PERF_471489_ = true;
  28. // PERF_471489_ is not exactly the same to Youtube Player perf v0.7
  29. // This script uses a much gentle way to tamer the JS engine instead.
  30.  
  31. // << end >>
  32.  
  33. const steppingScaleN = 200; // transform: scaleX(k/N); 0<k<N
  34.  
  35. const Promise = (async () => { })().constructor;
  36.  
  37. let isMainWindow = false;
  38. try {
  39. isMainWindow = window.document === window.top.document
  40. } catch (e) { }
  41.  
  42. const onRegistryReady = (callback) => {
  43. if (typeof customElements === 'undefined') {
  44. if (!('__CE_registry' in document)) {
  45. // https://github.com/webcomponents/polyfills/
  46. Object.defineProperty(document, '__CE_registry', {
  47. get() {
  48. // return undefined
  49. },
  50. set(nv) {
  51. if (typeof nv == 'object') {
  52. delete this.__CE_registry;
  53. this.__CE_registry = nv;
  54. this.dispatchEvent(new CustomEvent(EVENT_KEY_ON_REGISTRY_READY));
  55. }
  56. return true;
  57. },
  58. enumerable: false,
  59. configurable: true
  60. })
  61. }
  62. let eventHandler = (evt) => {
  63. document.removeEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
  64. const f = callback;
  65. callback = null;
  66. eventHandler = null;
  67. f();
  68. };
  69. document.addEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false);
  70. } else {
  71. callback();
  72. }
  73. };
  74.  
  75. const getZq = (_yt_player) => {
  76.  
  77. const w = 'Zq';
  78.  
  79. let arr = [];
  80.  
  81. for (const [k, v] of Object.entries(_yt_player)) {
  82.  
  83. const p = typeof v === 'function' ? v.prototype : 0;
  84. if (p
  85. && typeof p.start === 'function' && p.start.length === 0
  86. && typeof p.isActive === 'function' && p.isActive.length === 0
  87. && typeof p.stop === 'function' && p.stop.length === 0
  88. && !p.isComplete && !p.getStatus && !p.getResponseHeader && !p.getLastError
  89. && !p.send && !p.abort
  90. && !p.sample && !p.initialize && !p.fail && !p.getName
  91. // && !p.dispose && !p.isDisposed
  92.  
  93. ) {
  94. arr = addProtoToArr(_yt_player, k, arr) || arr;
  95.  
  96.  
  97. }
  98.  
  99. }
  100.  
  101. if (arr.length === 0) {
  102.  
  103. console.warn(`Key does not exist. [${w}]`);
  104. } else {
  105.  
  106. console.log(`[${w}]`, arr);
  107. return arr[0];
  108. }
  109.  
  110.  
  111.  
  112.  
  113. }
  114.  
  115.  
  116. const getVG = (_yt_player) => {
  117. const w = 'VG';
  118.  
  119. let arr = [];
  120.  
  121. for (const [k, v] of Object.entries(_yt_player)) {
  122.  
  123. const p = typeof v === 'function' ? v.prototype : 0;
  124. if (p
  125. && typeof p.show === 'function' && p.show.length === 1
  126. && typeof p.hide === 'function' && p.hide.length === 0
  127. && typeof p.stop === 'function' && p.stop.length === 0) {
  128.  
  129. arr = addProtoToArr(_yt_player, k, arr) || arr;
  130.  
  131. }
  132.  
  133. }
  134.  
  135.  
  136. if (arr.length === 0) {
  137.  
  138. console.warn(`Key does not exist. [${w}]`);
  139. } else {
  140.  
  141. console.log(`[${w}]`, arr);
  142. return arr[0];
  143. }
  144.  
  145.  
  146.  
  147. }
  148.  
  149.  
  150. const getzo = (_yt_player) => {
  151. const w = 'zo';
  152.  
  153. let arr = [];
  154.  
  155. for (const [k, v] of Object.entries(_yt_player)) {
  156.  
  157. if (
  158. typeof v === 'function' && v.length === 3 && k.length < 3
  159. && (v + "").includes("a.style[b]=c")
  160. ) {
  161.  
  162. arr.push(k);
  163.  
  164. }
  165.  
  166. }
  167.  
  168.  
  169. if (arr.length === 0) {
  170.  
  171. console.warn(`Key does not exist. [${w}]`);
  172. } else {
  173.  
  174. console.log(`[${w}]`, arr);
  175. return arr[0];
  176. }
  177.  
  178. }
  179.  
  180. const addProtoToArr = (parent, key, arr) => {
  181.  
  182.  
  183. let isChildProto = false;
  184. for (const sr of arr) {
  185. if (parent[key].prototype instanceof parent[sr]) {
  186. isChildProto = true;
  187. break;
  188. }
  189. }
  190.  
  191. if (isChildProto) return;
  192.  
  193. arr = arr.filter(sr => {
  194. if (parent[sr].prototype instanceof parent[key]) {
  195. return false;
  196. }
  197. return true;
  198. });
  199.  
  200. arr.push(key);
  201.  
  202. return arr;
  203.  
  204.  
  205. }
  206.  
  207. const getuG = (_yt_player) => {
  208.  
  209. const w = 'uG';
  210.  
  211. let arr = [];
  212.  
  213. for (const [k, v] of Object.entries(_yt_player)) {
  214.  
  215.  
  216. const p = typeof v === 'function' ? v.prototype : 0;
  217.  
  218. if (p
  219. && typeof p.createElement === 'function' && p.createElement.length === 2
  220. && typeof p.detach === 'function' && p.detach.length === 0
  221. && typeof p.update === 'function' && p.update.length === 1
  222. && typeof p.updateValue === 'function' && p.updateValue.length === 2
  223. ) {
  224.  
  225. arr = addProtoToArr(_yt_player, k, arr) || arr;
  226.  
  227. }
  228.  
  229. }
  230.  
  231.  
  232.  
  233.  
  234.  
  235. if (arr.length === 0) {
  236.  
  237. console.warn(`Key does not exist. [${w}]`);
  238. } else {
  239.  
  240. console.log(`[${w}]`, arr);
  241. return arr[0];
  242. }
  243.  
  244. }
  245.  
  246.  
  247. let foregroundPromise = null;
  248.  
  249. const getForegroundPromise = () => {
  250. if (document.visibilityState === 'visible') return Promise.resolve();
  251. else {
  252. return foregroundPromise = foregroundPromise || new Promise(resolve => {
  253. requestAnimationFrame(() => {
  254. foregroundPromise = null;
  255. resolve();
  256. });
  257. });
  258. }
  259. }
  260.  
  261. // << if FIX_schedulerInstanceInstance_ >>
  262.  
  263. let idleFrom = Date.now() + 2700;
  264. let slowMode = false;
  265.  
  266. let ytEvented = false;
  267.  
  268.  
  269. function setupEvents() {
  270.  
  271. document.addEventListener('yt-navigate', () => {
  272.  
  273. ytEvented = true;
  274. slowMode = false;
  275. idleFrom = Date.now() + 2700;
  276.  
  277. });
  278. document.addEventListener('yt-navigate-start', () => {
  279.  
  280. ytEvented = true;
  281. slowMode = false;
  282. idleFrom = Date.now() + 2700;
  283.  
  284. });
  285.  
  286. document.addEventListener('yt-page-type-changed', () => {
  287.  
  288. ytEvented = true;
  289. slowMode = false;
  290. idleFrom = Date.now() + 1700;
  291.  
  292. });
  293.  
  294.  
  295. document.addEventListener('yt-player-updated', () => {
  296.  
  297. ytEvented = true;
  298. slowMode = false;
  299. idleFrom = Date.now() + 1700;
  300.  
  301. });
  302.  
  303.  
  304. document.addEventListener('yt-page-data-fetched', () => {
  305.  
  306. ytEvented = true;
  307. slowMode = false;
  308. idleFrom = Date.now() + 1700;
  309.  
  310. });
  311.  
  312. document.addEventListener('yt-navigate-finish', () => {
  313.  
  314. ytEvented = true;
  315. slowMode = false;
  316. let t = Date.now() + 700;
  317. if (t > idleFrom) idleFrom = t;
  318.  
  319. });
  320.  
  321. document.addEventListener('yt-page-data-updated', () => {
  322.  
  323. ytEvented = true;
  324. slowMode = false;
  325. let t = Date.now() + 700;
  326. if (t > idleFrom) idleFrom = t;
  327.  
  328. });
  329.  
  330. document.addEventListener('yt-watch-comments-ready', () => {
  331.  
  332. ytEvented = true;
  333. slowMode = false;
  334. let t = Date.now() + 700;
  335. if (t > idleFrom) idleFrom = t;
  336.  
  337. });
  338. }
  339.  
  340.  
  341. // << end >>
  342.  
  343. const cleanContext = async (win) => {
  344. const waitFn = requestAnimationFrame; // shall have been binded to window
  345. try {
  346. let mx = 16; // MAX TRIAL
  347. const frameId = 'vanillajs-iframe-v1';
  348. /** @type {HTMLIFrameElement | null} */
  349. let frame = document.getElementById(frameId);
  350. let removeIframeFn = null;
  351. if (!frame) {
  352. frame = document.createElement('iframe');
  353. frame.id = 'vanillajs-iframe-v1';
  354. frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
  355. let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
  356. n.appendChild(frame);
  357. while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
  358. const root = document.documentElement;
  359. root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
  360. removeIframeFn = (setTimeout) => {
  361. const removeIframeOnDocumentReady = (e) => {
  362. e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  363. win = null;
  364. setTimeout(() => {
  365. n.remove();
  366. n = null;
  367. }, 200);
  368. }
  369. if (document.readyState !== 'loading') {
  370. removeIframeOnDocumentReady();
  371. } else {
  372. win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  373. }
  374. }
  375. }
  376. while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
  377. const fc = frame.contentWindow;
  378. if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
  379. const { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle } = fc;
  380. const res = { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, requestIdleCallback, getComputedStyle };
  381. for (let k in res) res[k] = res[k].bind(win); // necessary
  382. if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
  383. res.animate = fc.HTMLElement.prototype.animate;
  384. return res;
  385. } catch (e) {
  386. console.warn(e);
  387. return null;
  388. }
  389. };
  390.  
  391.  
  392. const promiseForCustomYtElementsReady = new Promise(onRegistryReady);
  393.  
  394. cleanContext(window).then(__CONTEXT__ => {
  395. if (!__CONTEXT__) return null;
  396.  
  397. const { requestAnimationFrame, setTimeout, cancelAnimationFrame, setInterval, clearInterval, animate, requestIdleCallback, getComputedStyle } = __CONTEXT__;
  398.  
  399.  
  400.  
  401. let rafPromiseForTickers = null;
  402. const getRafPromiseForTickers = () => (rafPromiseForTickers = (rafPromiseForTickers || new Promise(resolve => {
  403. requestAnimationFrame((hRes) => {
  404. rafPromiseForTickers = null;
  405. resolve(hRes);
  406. });
  407. })));
  408.  
  409. const promiseForTamerTimeout = new Promise(resolve => {
  410. promiseForCustomYtElementsReady.then(() => {
  411. customElements.whenDefined('ytd-app').then(() => {
  412. setTimeout(resolve, 1200);
  413. });
  414. });
  415. setTimeout(resolve, 3000);
  416. });
  417.  
  418.  
  419. class RAFHub {
  420. constructor() {
  421. /** @type {number} */
  422. this.startAt = 8170;
  423. /** @type {number} */
  424. this.counter = 0;
  425. /** @type {number} */
  426. this.rid = 0;
  427. /** @type {Map<number, FrameRequestCallback>} */
  428. this.funcs = new Map();
  429. const funcs = this.funcs;
  430. /** @type {FrameRequestCallback} */
  431. this.bCallback = this.mCallback.bind(this);
  432. this.pClear = () => funcs.clear();
  433. }
  434. /** @param {DOMHighResTimeStamp} highResTime */
  435. mCallback(highResTime) {
  436. this.rid = 0;
  437. Promise.resolve().then(this.pClear);
  438. this.funcs.forEach(func => Promise.resolve(highResTime).then(func).catch(console.warn));
  439. }
  440. /** @param {FrameRequestCallback} f */
  441. request(f) {
  442. if (this.counter > 1e9) this.counter = 9;
  443. let cid = this.startAt + (++this.counter);
  444. this.funcs.set(cid, f);
  445. if (this.rid === 0) this.rid = requestAnimationFrame(this.bCallback);
  446. return cid;
  447. }
  448. /** @param {number} cid */
  449. cancel(cid) {
  450. cid = +cid;
  451. if (cid > 0) {
  452. if (cid <= this.startAt) {
  453. return cancelAnimationFrame(cid);
  454. }
  455. if (this.rid > 0) {
  456. this.funcs.delete(cid);
  457. if (this.funcs.size === 0) {
  458. cancelAnimationFrame(this.rid);
  459. this.rid = 0;
  460. }
  461. }
  462. }
  463. }
  464. }
  465.  
  466.  
  467.  
  468. NATIVE_CANVAS_ANIMATION && (() => {
  469.  
  470. HTMLCanvasElement.prototype.animate = animate;
  471.  
  472. let cid = setInterval(() => {
  473. HTMLCanvasElement.prototype.animate = animate;
  474. }, 1);
  475.  
  476. promiseForTamerTimeout.then(() => {
  477. clearInterval(cid)
  478. });
  479.  
  480. })();
  481.  
  482.  
  483. FIX_schedulerInstanceInstance_ && (async () => {
  484.  
  485.  
  486. const schedulerInstanceInstance_ = await new Promise(resolve => {
  487.  
  488. let cid = setInterval(() => {
  489. let t = (((window || 0).ytglobal || 0).schedulerInstanceInstance_ || 0);
  490. if (t) {
  491.  
  492. clearInterval(cid);
  493. resolve(t);
  494. }
  495. }, 1);
  496. promiseForTamerTimeout.then(() => {
  497. resolve(null)
  498. });
  499. });
  500.  
  501. if (!schedulerInstanceInstance_) return;
  502.  
  503.  
  504. if (!ytEvented) {
  505. idleFrom = Date.now() + 2700;
  506. slowMode = false; // integrity
  507. }
  508.  
  509. const checkOK = typeof schedulerInstanceInstance_.start === 'function' && !schedulerInstanceInstance_.start991 && !schedulerInstanceInstance_.stop && !schedulerInstanceInstance_.cancel && !schedulerInstanceInstance_.terminate && !schedulerInstanceInstance_.interupt;
  510. if (checkOK) {
  511.  
  512. schedulerInstanceInstance_.start991 = schedulerInstanceInstance_.start;
  513.  
  514. let requestingFn = null;
  515. let requestingArgs = null;
  516. let requestingDT = 0;
  517.  
  518. // let timerId = null;
  519. const entries = [];
  520. const f = function () {
  521. requestingFn = this.fn;
  522. requestingArgs = [...arguments];
  523. requestingDT = Date.now();
  524. entries.push({
  525. fn: requestingFn,
  526. args: requestingArgs,
  527. t: requestingDT
  528. });
  529. // if (Date.now() < idleFrom) {
  530. // timerId = this.fn.apply(window, arguments);
  531. // } else {
  532. // timerId = this.fn.apply(window, arguments);
  533.  
  534. // }
  535. // timerId = 12377;
  536. return 12377;
  537. }
  538.  
  539.  
  540. const fakeFns = [
  541. f.bind({ fn: requestAnimationFrame }),
  542. f.bind({ fn: setInterval }),
  543. f.bind({ fn: setTimeout }),
  544. f.bind({ fn: requestIdleCallback })
  545. ]
  546.  
  547.  
  548.  
  549.  
  550. let timerResolve = null;
  551. setInterval(() => {
  552. timerResolve && timerResolve();
  553. timerResolve = null;
  554. if (!slowMode && Date.now() > idleFrom) slowMode = true;
  555. }, 250);
  556.  
  557. let mzt = 0;
  558.  
  559. let fnSelectorProp = null;
  560.  
  561. schedulerInstanceInstance_.start = function () {
  562.  
  563. const mk1 = window.requestAnimationFrame
  564. const mk2 = window.setInterval
  565. const mk3 = window.setTimeout
  566. const mk4 = window.requestIdleCallback
  567.  
  568. const tThis = this['$$12378$$'] || this;
  569.  
  570.  
  571. window.requestAnimationFrame = fakeFns[0]
  572. window.setInterval = fakeFns[1]
  573. window.setTimeout = fakeFns[2]
  574. window.requestIdleCallback = fakeFns[3]
  575.  
  576. fnSelectorProp = null;
  577.  
  578.  
  579. tThis.start991.call(new Proxy(tThis, {
  580. get(target, prop, receiver) {
  581. if (prop === '$$12377$$') return true;
  582. if (prop === '$$12378$$') return target;
  583.  
  584. // console.log('get',prop)
  585. return target[prop]
  586. },
  587. set(target, prop, value, receiver) {
  588. // console.log('set', prop, value)
  589.  
  590.  
  591. if (value >= 1 && value <= 4) fnSelectorProp = prop;
  592. if (value === 12377 && fnSelectorProp) {
  593.  
  594. const originalSelection = target[fnSelectorProp];
  595. const timerIdProp = prop;
  596.  
  597. /*
  598.  
  599.  
  600. case 1:
  601. var a = this.K;
  602. this.g = this.I ? window.requestIdleCallback(a, {
  603. timeout: 3E3
  604. }) : window.setTimeout(a, ma);
  605. break;
  606. case 2:
  607. this.g = window.setTimeout(this.M, this.N);
  608. break;
  609. case 3:
  610. this.g = window.requestAnimationFrame(this.L);
  611. break;
  612. case 4:
  613. this.g = window.setTimeout(this.J, 0)
  614. }
  615.  
  616. */
  617.  
  618. const doForegroundSlowMode = () => {
  619.  
  620. const tir = ++mzt;
  621. const f = requestingArgs[0];
  622.  
  623.  
  624. getForegroundPromise().then(() => {
  625.  
  626.  
  627. new Promise(r => {
  628. timerResolve = r
  629. }).then(() => {
  630. if (target[timerIdProp] === -tir) f();
  631. });
  632.  
  633. })
  634.  
  635. target[fnSelectorProp] = 931;
  636. target[prop] = -tir;
  637. }
  638.  
  639. if (target[fnSelectorProp] === 2 && requestingFn === setTimeout) {
  640. if (slowMode && !(requestingArgs[1] > 250)) {
  641.  
  642. doForegroundSlowMode();
  643.  
  644. } else {
  645. target[prop] = setTimeout.apply(window, requestingArgs);
  646.  
  647. }
  648.  
  649. } else if (target[fnSelectorProp] === 3 && requestingFn === requestAnimationFrame) {
  650.  
  651. if (slowMode) {
  652.  
  653. doForegroundSlowMode();
  654.  
  655. } else {
  656. target[prop] = requestAnimationFrame.apply(window, requestingArgs);
  657. }
  658.  
  659.  
  660. } else if (target[fnSelectorProp] === 4 && requestingFn === setTimeout && !requestingArgs[1]) {
  661.  
  662. const f = requestingArgs[0];
  663. const tir = ++mzt;
  664. Promise.resolve().then(() => {
  665. if (target[timerIdProp] === -tir) f();
  666. });
  667. target[fnSelectorProp] = 930;
  668. target[prop] = -tir;
  669.  
  670. } else if (target[fnSelectorProp] === 1 && (requestingFn === requestIdleCallback || requestingFn === setTimeout)) {
  671.  
  672. doForegroundSlowMode();
  673.  
  674. } else {
  675. // target[prop] = timerId;
  676. target[fnSelectorProp] = 0;
  677. target[prop] = 0;
  678. }
  679.  
  680. // *****
  681. // console.log('[[set]]', slowMode , prop, value, `fnSelectorProp: ${originalSelection} -> ${target[fnSelectorProp]}`)
  682. } else {
  683.  
  684. target[prop] = value;
  685. }
  686. // console.log('set',prop,value)
  687. return true;
  688. }
  689. }));
  690.  
  691. fnSelectorProp = null;
  692.  
  693.  
  694. window.requestAnimationFrame = mk1;
  695. window.setInterval = mk2
  696. window.setTimeout = mk3
  697. window.requestIdleCallback = mk4;
  698.  
  699.  
  700.  
  701. }
  702.  
  703. schedulerInstanceInstance_.start.toString = function () {
  704. return schedulerInstanceInstance_.start991.toString();
  705. }
  706.  
  707. // const funcNames = [...(schedulerInstanceInstance_.start + "").matchAll(/[\(,]this\.(\w{1,2})[,\)]/g)].map(e => e[1]).map(prop => ({
  708. // prop,
  709. // value: schedulerInstanceInstance_[prop],
  710. // type: typeof schedulerInstanceInstance_[prop]
  711.  
  712. // }));
  713. // console.log('fcc', funcNames)
  714.  
  715.  
  716.  
  717.  
  718. }
  719. })();
  720.  
  721.  
  722. FIX_yt_player && (async () => {
  723.  
  724.  
  725.  
  726. const rafHub = new RAFHub();
  727.  
  728.  
  729. const _yt_player = await new Promise(resolve => {
  730.  
  731. let cid = setInterval(() => {
  732. let t = (((window || 0)._yt_player || 0) || 0);
  733. if (t) {
  734.  
  735. clearInterval(cid);
  736. resolve(t);
  737. }
  738. }, 1);
  739.  
  740. promiseForTamerTimeout.then(() => {
  741. resolve(null)
  742. });
  743.  
  744. });
  745.  
  746.  
  747.  
  748. if (!_yt_player || typeof _yt_player !== 'object') return;
  749.  
  750.  
  751.  
  752. let keyZq = getZq(_yt_player);
  753. let keyVG = getVG(_yt_player);
  754. let buildVG = _yt_player[keyVG];
  755. let u = new buildVG({
  756. api: {},
  757. element: document.createElement('noscript'),
  758. api: {},
  759. hide: () => { }
  760. }, 250);
  761. // console.log(keyVG, u)
  762. // buildVG.prototype.show = function(){}
  763. // _yt_player[keyZq] = g.k
  764.  
  765. if (!keyZq) return;
  766.  
  767.  
  768. const g = _yt_player
  769. let k = keyZq
  770.  
  771. const gk = g[k];
  772. if (typeof gk !== 'function') return;
  773.  
  774. let dummyObject = new gk;
  775. let nilFunc = () => { };
  776.  
  777. let nilObj = {};
  778.  
  779. // console.log(1111111111)
  780.  
  781. let keyBoolD = '';
  782. let keyWindow = '';
  783. let keyFuncC = '';
  784. let keyCidj = '';
  785.  
  786. for (const [t, y] of Object.entries(dummyObject)) {
  787. if (y instanceof Window) keyWindow = t;
  788. }
  789.  
  790. const dummyObjectProxyHandler = {
  791. get(target, prop) {
  792. let v = target[prop]
  793. if (v instanceof Window && !keyWindow) {
  794. keyWindow = t;
  795. }
  796. let y = typeof v === 'function' ? nilFunc : typeof v === 'object' ? nilObj : v;
  797. if (prop === keyWindow) y = {
  798. requestAnimationFrame(f) {
  799. return 3;
  800. },
  801. cancelAnimationFrame() {
  802.  
  803. }
  804. }
  805. if (!keyFuncC && typeof v === 'function' && !(prop in target.constructor.prototype)) {
  806. keyFuncC = prop;
  807. }
  808. // console.log('[get]', prop, typeof target[prop])
  809.  
  810.  
  811. return y;
  812. },
  813. set(target, prop, value) {
  814.  
  815. if (typeof value === 'boolean' && !keyBoolD) {
  816. keyBoolD = prop;
  817. }
  818. if (typeof value === 'number' && !keyCidj && value >= 2) {
  819. keyCidj = prop;
  820. }
  821.  
  822. // console.log('[set]', prop, value)
  823. target[prop] = value
  824.  
  825. return true;
  826. }
  827. };
  828.  
  829. dummyObject.start.call(new Proxy(dummyObject, dummyObjectProxyHandler))
  830.  
  831. /*
  832. console.log({
  833. keyBoolD,
  834. keyFuncC,
  835. keyWindow,
  836. keyCidj
  837. })
  838.  
  839. console.log( dummyObject[keyFuncC])
  840.  
  841.  
  842. console.log(2222222222)
  843. */
  844.  
  845.  
  846.  
  847.  
  848. g[k].prototype.start = function () {
  849. this.stop();
  850. this[keyBoolD] = true;
  851. this[keyCidj] = rafHub.request(this[keyFuncC]);
  852. }
  853. ;
  854. g[k].prototype.stop = function () {
  855. if (this.isActive() && this[keyCidj]) {
  856. rafHub.cancel(this[keyCidj]);
  857. }
  858. this[keyCidj] = null
  859. }
  860.  
  861.  
  862. /*
  863. g[k].start = function() {
  864. this.stop();
  865. this.D = true;
  866. var a = requestAnimationFrame
  867. , b = cancelAnimationFrame;
  868. this.j = a.call(this.B, this.C)
  869. }
  870. ;
  871. g[k].stop = function() {
  872. if (this.isActive()) {
  873. var a = requestAnimationFrame
  874. , b = cancelAnimationFrame;
  875. b.call(this.B, this.j)
  876. }
  877. this.j = null
  878. }
  879. */
  880.  
  881.  
  882.  
  883. const keyzo = PERF_471489_ ? getzo(_yt_player) : null;
  884.  
  885. if (keyzo) {
  886.  
  887. k = keyzo
  888.  
  889. const attrUpdateFn = g[k];
  890. g['$$original$$' + k] = attrUpdateFn;
  891. g[k] = function (a, b, c) {
  892.  
  893. // console.log(140000, a, b, c);
  894.  
  895. if (b === "transform") {
  896. if (!(a instanceof HTMLElement)) return;
  897. if (c.length === 0) {
  898.  
  899. if (!a.style.transform) return;
  900.  
  901. } else if (c.startsWith('scalex(0.') || (c === 'scalex(0)' || c === 'scalex(1)')) {
  902. let p = c.substring(7, c.length - 1);
  903. let q = p.length >= 1 ? parseFloat(p) : -1;
  904. if (q > -1e-5 && q < 1 + 1e-5) {
  905. let vz = Math.round(steppingScaleN * q);
  906.  
  907. const aStyle = a.style;
  908. const currentValue = aStyle.getPropertyValue('--stepped-scalex');
  909.  
  910. const transform = (aStyle.transform || '');
  911. const u = transform.includes('--stepped-scalex')
  912. if (`${currentValue}` === `${vz}`) {
  913. if (u) return;
  914. }
  915.  
  916. aStyle.setProperty('--stepped-scalex', vz)
  917.  
  918.  
  919. if (u) return;
  920. c = `scalex(calc(var(--stepped-scalex)/${steppingScaleN}))`;
  921.  
  922.  
  923.  
  924.  
  925. }
  926.  
  927.  
  928. } else if (c.startsWith('translateX(') && c.endsWith('px)')) {
  929.  
  930. let p = c.substring(11, c.length - 3);
  931. let q = p.length >= 1 ? parseFloat(p) : NaN;
  932.  
  933. if (q !== NaN && typeof q === 'number') {
  934.  
  935.  
  936. let vz = q.toFixed(1);
  937.  
  938. const aStyle = a.style;
  939. const currentValue = (aStyle.getPropertyValue('--stepped-translateX') || '').replace('px', '');
  940.  
  941.  
  942. const transform = (aStyle.transform || '');
  943. const u = transform.includes('--stepped-translateX')
  944. if (parseFloat(currentValue).toFixed(1) === vz) {
  945. if (u) return;
  946. }
  947.  
  948. aStyle.setProperty('--stepped-translateX', vz + 'px')
  949.  
  950.  
  951. if (u) return;
  952. c = `translateX(var(--stepped-translateX))`;
  953.  
  954. }
  955.  
  956.  
  957. } else if (c.startsWith('scaley(0.') || (c === 'scaley(0)' || c === 'scaley(1)')) {
  958. let p = c.substring(7, c.length - 1);
  959. let q = p.length >= 1 ? parseFloat(p) : -1;
  960. if (q > -1e-5 && q < 1 + 1e-5) {
  961. let vz = Math.round(steppingScaleN * q);
  962.  
  963. const aStyle = a.style;
  964. const currentValue = aStyle.getPropertyValue('--stepped-scaley');
  965.  
  966. const transform = (aStyle.transform || '');
  967. const u = transform.includes('--stepped-scaley')
  968. if (currentValue === `${vz}`) {
  969. if (u) return;
  970. }
  971.  
  972. aStyle.setProperty('--stepped-scaley', vz)
  973.  
  974.  
  975. if (u) return;
  976. c = `scaley(calc(var(--stepped-scaley)/${steppingScaleN}))`;
  977.  
  978.  
  979.  
  980.  
  981. }
  982.  
  983.  
  984. } else if (c.startsWith('translateY(') && c.endsWith('px)')) {
  985.  
  986. let p = c.substring(11, c.length - 3);
  987. let q = p.length >= 1 ? parseFloat(p) : NaN;
  988.  
  989. if (q !== NaN && typeof q === 'number') {
  990.  
  991.  
  992. let vz = q.toFixed(1);
  993.  
  994. const aStyle = a.style;
  995. const currentValue = aStyle.getPropertyValue('--stepped-translateY');
  996.  
  997. const transform = (aStyle.transform || '');
  998. const u = transform.includes('--stepped-translateY')
  999. if (currentValue === `${vz}`) {
  1000. if (u) return;
  1001. }
  1002.  
  1003. aStyle.setProperty('--stepped-translateY', vz + 'px')
  1004.  
  1005.  
  1006. if (u) return;
  1007. c = `translateY(var(--stepped-translateY))`;
  1008.  
  1009. }
  1010.  
  1011.  
  1012. }
  1013. } else if (b === "display") {
  1014.  
  1015. const cv = a.style.display;
  1016. if (!cv && !c) return;
  1017. if (cv === c) return;
  1018.  
  1019. } else if (b === "width") {
  1020.  
  1021. const cv = a.style.width;
  1022. if (!cv && !c) return;
  1023. if (cv === c) return;
  1024.  
  1025. }
  1026.  
  1027. // console.log(130000, a, b, c);
  1028.  
  1029. return attrUpdateFn.call(this, a, b, c);
  1030. }
  1031.  
  1032.  
  1033. /*
  1034.  
  1035. g.zo = function(a, b, c) {
  1036. if ("string" === typeof b)
  1037. (b = yo(a, b)) && (a.style[b] = c);
  1038. else
  1039. for (var d in b) {
  1040. c = a;
  1041. var e = b[d]
  1042. , f = yo(c, d);
  1043. f && (c.style[f] = e)
  1044. }
  1045. }
  1046.  
  1047.  
  1048. */
  1049.  
  1050.  
  1051. }
  1052.  
  1053.  
  1054.  
  1055. const keyuG = PERF_471489_ ? getuG(_yt_player) : null;
  1056.  
  1057. if (keyuG) {
  1058.  
  1059. k = keyuG;
  1060.  
  1061. const gk = g[k];
  1062. const gkp = gk.prototype;
  1063.  
  1064.  
  1065. /** @type { Map<string, WeakMap<any, any>> } */
  1066. const ntLogs = new Map();
  1067.  
  1068. if (typeof gkp.updateValue === 'function' && gkp.updateValue.length === 2 && !gkp.updateValue31) {
  1069.  
  1070. gkp.updateValue31 = gkp.updateValue;
  1071. gkp.updateValue = function (a, b) {
  1072. if (typeof a !== 'string') return this.updateValue31(a, b);
  1073.  
  1074. const element = this.element;
  1075. if (!(element instanceof HTMLElement)) return this.updateValue31(a, b);
  1076.  
  1077. let ntLog = ntLogs.get(a);
  1078. if (!ntLog) ntLogs.set(a, (ntLog = new WeakMap()));
  1079.  
  1080. let cache = ntLog.get(element);
  1081. if (cache && cache.value === b) {
  1082. return;
  1083. }
  1084. if (!cache) {
  1085. ntLog.set(element, cache = { value: b });
  1086. } else {
  1087. cache.value = b;
  1088. }
  1089.  
  1090.  
  1091. return this.updateValue31(a, b);
  1092. }
  1093.  
  1094.  
  1095. /*
  1096. g.k.update = function(a) {
  1097. for (var b = g.u(Object.keys(a)), c = b.next(); !c.done; c = b.next())
  1098. c = c.value,
  1099. this.updateValue(c, a[c])
  1100. }
  1101. ;
  1102. g.k.updateValue = function(a, b) {
  1103. (a = this.Td["{{" + a + "}}"]) && wG(this, a[0], a[1], b)
  1104. }
  1105. */
  1106.  
  1107.  
  1108. }
  1109.  
  1110.  
  1111. }
  1112.  
  1113.  
  1114.  
  1115.  
  1116. })();
  1117.  
  1118.  
  1119.  
  1120. FIX_Animation_n_timeline && (async () => {
  1121.  
  1122.  
  1123. const timeline = await new Promise(resolve => {
  1124.  
  1125. let cid = setInterval(() => {
  1126. let t = (((document || 0).timeline || 0) || 0);
  1127. if (t && typeof t._play === 'function') {
  1128.  
  1129. clearInterval(cid);
  1130. resolve(t);
  1131. }
  1132. }, 1);
  1133.  
  1134. promiseForTamerTimeout.then(() => {
  1135. resolve(null)
  1136. });
  1137.  
  1138. });
  1139.  
  1140.  
  1141. const Animation = await new Promise(resolve => {
  1142.  
  1143. let cid = setInterval(() => {
  1144. let t = (((window || 0).Animation || 0) || 0);
  1145. if (t && typeof t === 'function' && t.length === 2 && typeof t.prototype._updatePromises === 'function') {
  1146.  
  1147. clearInterval(cid);
  1148. resolve(t);
  1149. }
  1150. }, 1);
  1151.  
  1152. promiseForTamerTimeout.then(() => {
  1153. resolve(null)
  1154. });
  1155.  
  1156. });
  1157.  
  1158. if (!timeline) return;
  1159. if (!Animation) return;
  1160.  
  1161. const aniProto = Animation.prototype;
  1162.  
  1163. const getXroto = (x) => {
  1164. try {
  1165. return x.__proto__;
  1166. } catch (e) { }
  1167. return null;
  1168. }
  1169. const timProto = getXroto(timeline);
  1170. if (!timProto) return;
  1171. if (
  1172. (
  1173. typeof timProto.getAnimations === 'function' && typeof timProto.play === 'function' &&
  1174. typeof timProto._discardAnimations === 'function' && typeof timProto._play === 'function' &&
  1175. typeof timProto._updateAnimationsPromises === 'function' && !timProto.nofCQ &&
  1176. typeof aniProto._updatePromises === 'function' && !aniProto.nofYH
  1177. )
  1178.  
  1179. ) {
  1180.  
  1181. timProto.nofCQ = 1;
  1182. aniProto.nofYH = 1;
  1183.  
  1184. const originalAnimationsWithPromises = ((_updateAnimationsPromises) => {
  1185.  
  1186.  
  1187. /*
  1188. v.animationsWithPromises = v.animationsWithPromises.filter(function (c) {
  1189. return c._updatePromises();
  1190. });
  1191. */
  1192.  
  1193. const p = Array.prototype.filter;
  1194.  
  1195. let res = null;
  1196. Array.prototype.filter = function () {
  1197.  
  1198. res = this;
  1199. return this;
  1200.  
  1201. };
  1202.  
  1203. _updateAnimationsPromises.call({});
  1204.  
  1205. Array.prototype.filter = p;
  1206.  
  1207. if (res && typeof res.length === 'number') {
  1208. /** @type {any[]} */
  1209. const _res = res;
  1210. return _res;
  1211. }
  1212.  
  1213.  
  1214. return null;
  1215.  
  1216.  
  1217.  
  1218.  
  1219. })(timProto._updateAnimationsPromises);
  1220.  
  1221. if (!originalAnimationsWithPromises || typeof originalAnimationsWithPromises.length !== 'number') return;
  1222.  
  1223. // console.log('originalAnimationsWithPromises', originalAnimationsWithPromises)
  1224.  
  1225. aniProto._updatePromises31 = aniProto._updatePromises;
  1226.  
  1227. /*
  1228. aniProto._updatePromises = function(){
  1229. console.log('eff',this._oldPlayState, this.playState)
  1230. return this._updatePromises31.apply(this, arguments)
  1231. }
  1232. */
  1233.  
  1234. aniProto._updatePromises = function () {
  1235. var oldPlayState = this._oldPlayState;
  1236. var newPlayState = this.playState;
  1237. // console.log('ett', oldPlayState, newPlayState)
  1238. if (newPlayState !== oldPlayState) {
  1239. this._oldPlayState = newPlayState;
  1240. if (this._readyPromise) {
  1241. if ("idle" == newPlayState) {
  1242. this._rejectReadyPromise();
  1243. this._readyPromise = void 0;
  1244. } else if ("pending" == oldPlayState) {
  1245. this._resolveReadyPromise();
  1246. } else if ("pending" == newPlayState) {
  1247. this._readyPromise = void 0;
  1248. }
  1249. }
  1250. if (this._finishedPromise) {
  1251. if ("idle" == newPlayState) {
  1252. this._rejectFinishedPromise();
  1253. this._finishedPromise = void 0;
  1254. } else if ("finished" == newPlayState) {
  1255. this._resolveFinishedPromise();
  1256. } else if ("finished" == oldPlayState) {
  1257. this._finishedPromise = void 0;
  1258. }
  1259. }
  1260. }
  1261. return this._readyPromise || this._finishedPromise;
  1262. };
  1263.  
  1264.  
  1265. let restartWebAnimationsNextTickFlag = false;
  1266.  
  1267. const looperMethodT = () => {
  1268.  
  1269. const runnerFn = (hRes) => {
  1270. var b = timeline;
  1271. b.currentTime = hRes;
  1272. b._discardAnimations();
  1273. if (0 == b._animations.length) {
  1274. restartWebAnimationsNextTickFlag = false;
  1275. } else {
  1276. getForegroundPromise().then(runnerFn);
  1277. }
  1278. }
  1279.  
  1280. const restartWebAnimationsNextTick = () => {
  1281. if (!restartWebAnimationsNextTickFlag) {
  1282. restartWebAnimationsNextTickFlag = true;
  1283. getRafPromiseForTickers().then(runnerFn);
  1284. }
  1285. }
  1286.  
  1287. return { restartWebAnimationsNextTick }
  1288. };
  1289.  
  1290.  
  1291. const looperMethodN = () => {
  1292.  
  1293. const acs = document.createElement('a-f');
  1294. acs.id = 'a-f';
  1295.  
  1296. const style = document.createElement('style');
  1297. style.textContent = `
  1298. @keyFrames aF1 {
  1299. 0% {
  1300. order: 0;
  1301. }
  1302. 100% {
  1303. order: 6;
  1304. }
  1305. }
  1306. #a-f[id] {
  1307. visibility: collapse !important;
  1308. position: fixed !important;
  1309. top: -100px !important;
  1310. left: -100px !important;
  1311. margin:0 !important;
  1312. padding:0 !important;
  1313. outline:0 !important;
  1314. border:0 !important;
  1315. z-index:-1 !important;
  1316. width: 0px !important;
  1317. height: 0px !important;
  1318. contain: strict !important;
  1319. pointer-events: none !important;
  1320. animation: 1ms steps(2) 0ms infinite alternate forwards running aF1 !important;
  1321. }
  1322. `;
  1323. (document.head || document.documentElement).appendChild(style);
  1324.  
  1325. document.documentElement.insertBefore(acs, document.documentElement.firstChild);
  1326.  
  1327. const _onanimationiteration = function (evt) {
  1328. const hRes = evt.timeStamp;
  1329. var b = timeline;
  1330. b.currentTime = hRes;
  1331. b._discardAnimations();
  1332. if (0 == b._animations.length) {
  1333. restartWebAnimationsNextTickFlag = false;
  1334. acs.onanimationiteration = null;
  1335. } else {
  1336. acs.onanimationiteration = _onanimationiteration;
  1337. }
  1338.  
  1339. }
  1340.  
  1341.  
  1342.  
  1343. const restartWebAnimationsNextTick = () => {
  1344. if (!restartWebAnimationsNextTickFlag) {
  1345. restartWebAnimationsNextTickFlag = true;
  1346. acs.onanimationiteration = _onanimationiteration;
  1347.  
  1348. }
  1349. }
  1350.  
  1351. return { restartWebAnimationsNextTick }
  1352. };
  1353.  
  1354.  
  1355.  
  1356. const { restartWebAnimationsNextTick } = ('onanimationiteration' in document.documentElement) ? looperMethodN() : looperMethodT();
  1357.  
  1358. timProto._play = function (c) {
  1359. c = new Animation(c, this);
  1360. this._animations.push(c);
  1361. restartWebAnimationsNextTick();
  1362. c._updatePromises();
  1363. c._animation.play();
  1364. c._updatePromises();
  1365. return c
  1366. }
  1367.  
  1368. const animationsWithPromisesMap = new Set(originalAnimationsWithPromises);
  1369. originalAnimationsWithPromises.length = 0;
  1370. originalAnimationsWithPromises.push = null;
  1371. originalAnimationsWithPromises.splice = null;
  1372. originalAnimationsWithPromises.slice = null;
  1373. originalAnimationsWithPromises.indexOf = null;
  1374. originalAnimationsWithPromises.unshift = null;
  1375. originalAnimationsWithPromises.shift = null;
  1376. originalAnimationsWithPromises.pop = null;
  1377. originalAnimationsWithPromises.filter = null;
  1378. originalAnimationsWithPromises.forEach = null;
  1379. originalAnimationsWithPromises.map = null;
  1380.  
  1381.  
  1382.  
  1383. const _updateAnimationsPromises = () => {
  1384. animationsWithPromisesMap.forEach(c => {
  1385. if (!c._updatePromises()) animationsWithPromisesMap.delete(c);
  1386. })
  1387. /*
  1388. v.animationsWithPromises = v.animationsWithPromises.filter(function (c) {
  1389. return c._updatePromises();
  1390. });
  1391. */
  1392. }
  1393.  
  1394. timProto._updateAnimationsPromises31 = timProto._updateAnimationsPromises;
  1395.  
  1396. timProto._updateAnimationsPromises = _updateAnimationsPromises;
  1397.  
  1398.  
  1399. let pdFinished = Object.getOwnPropertyDescriptor(aniProto, 'finished');
  1400. aniProto.__finished_native_get__ = pdFinished.get;
  1401. if (typeof pdFinished.get === 'function' && !pdFinished.set && pdFinished.configurable === true && pdFinished.enumerable === true) {
  1402.  
  1403.  
  1404. Object.defineProperty(aniProto, 'finished', {
  1405. get() {
  1406. this._finishedPromise || (!animationsWithPromisesMap.has(this) && animationsWithPromisesMap.add(this),
  1407. this._finishedPromise = new Promise((resolve, reject) => {
  1408. this._resolveFinishedPromise = function () {
  1409. resolve(this)
  1410. };
  1411. this._rejectFinishedPromise = function () {
  1412. reject({
  1413. type: DOMException.ABORT_ERR,
  1414. name: "AbortError"
  1415. })
  1416. };
  1417. }),
  1418. "finished" == this.playState && this._resolveFinishedPromise());
  1419. return this._finishedPromise
  1420. },
  1421. set: undefined,
  1422. enumerable: true,
  1423. configurable: true
  1424. });
  1425.  
  1426. }
  1427.  
  1428.  
  1429.  
  1430. let pdReady = Object.getOwnPropertyDescriptor(aniProto, 'ready');
  1431. aniProto.__ready_native_get__ = pdReady.get;
  1432. if (typeof pdReady.get === 'function' && !pdReady.set && pdReady.configurable === true && pdReady.enumerable === true) {
  1433.  
  1434. Object.defineProperty(aniProto, 'ready', {
  1435. get() {
  1436. this._readyPromise || (!animationsWithPromisesMap.has(this) && animationsWithPromisesMap.add(this),
  1437. this._readyPromise = new Promise((resolve, reject) => {
  1438. this._resolveReadyPromise = function () {
  1439. resolve(this)
  1440. };
  1441. this._rejectReadyPromise = function () {
  1442. reject({
  1443. type: DOMException.ABORT_ERR,
  1444. name: "AbortError"
  1445. })
  1446. };
  1447. }),
  1448. "pending" !== this.playState && this._resolveReadyPromise());
  1449. return this._readyPromise
  1450. },
  1451. set: undefined,
  1452. enumerable: true,
  1453. configurable: true
  1454. });
  1455.  
  1456. }
  1457.  
  1458.  
  1459.  
  1460.  
  1461. /*
  1462. function f(c) {
  1463. var b = v.timeline;
  1464. b.currentTime = c;
  1465. b._discardAnimations();
  1466. 0 == b._animations.length ? d = !1 : requestAnimationFrame(f)
  1467. }
  1468. var h = window.requestAnimationFrame;
  1469. window.requestAnimationFrame = function(c) {
  1470. return h(function(b) {
  1471. v.timeline._updateAnimationsPromises();
  1472. c(b);
  1473. v.timeline._updateAnimationsPromises()
  1474. })
  1475. }
  1476. ;
  1477. v.AnimationTimeline = function() {
  1478. this._animations = [];
  1479. this.currentTime = void 0
  1480. }
  1481. ;
  1482. v.AnimationTimeline.prototype = {
  1483. getAnimations: function() {
  1484. this._discardAnimations();
  1485. return this._animations.slice()
  1486. },
  1487. _updateAnimationsPromises: function() {
  1488. v.animationsWithPromises = v.animationsWithPromises.filter(function(c) {
  1489. return c._updatePromises()
  1490. })
  1491. },
  1492. _discardAnimations: function() {
  1493. this._updateAnimationsPromises();
  1494. this._animations = this._animations.filter(function(c) {
  1495. return "finished" != c.playState && "idle" != c.playState
  1496. })
  1497. },
  1498. _play: function(c) {
  1499. c = new v.Animation(c,this);
  1500. this._animations.push(c);
  1501. v.restartWebAnimationsNextTick();
  1502. c._updatePromises();
  1503. c._animation.play();
  1504. c._updatePromises();
  1505. return c
  1506. },
  1507. play: function(c) {
  1508. c && c.remove();
  1509. return this._play(c)
  1510. }
  1511. };
  1512. var d = !1;
  1513. v.restartWebAnimationsNextTick = function() {
  1514. d || (d = !0,
  1515. requestAnimationFrame(f))
  1516. }
  1517. ;
  1518. var a = new v.AnimationTimeline;
  1519. v.timeline = a;
  1520. try {
  1521. Object.defineProperty(window.document, "timeline", {
  1522. configurable: !0,
  1523. get: function() {
  1524. return a
  1525. }
  1526. })
  1527. } catch (c) {}
  1528. try {
  1529. window.document.timeline = a
  1530. } catch (c) {}
  1531. */
  1532.  
  1533.  
  1534.  
  1535. /*
  1536.  
  1537. var g = window.getComputedStyle;
  1538. Object.defineProperty(window, "getComputedStyle", {
  1539. configurable: !0,
  1540. enumerable: !0,
  1541. value: function() {
  1542. v.timeline._updateAnimationsPromises();
  1543. var e = g.apply(this, arguments);
  1544. h() && (e = g.apply(this, arguments));
  1545. v.timeline._updateAnimationsPromises();
  1546. return e
  1547. }
  1548. });
  1549.  
  1550. */
  1551.  
  1552.  
  1553.  
  1554.  
  1555. }
  1556.  
  1557.  
  1558.  
  1559.  
  1560. })();
  1561.  
  1562.  
  1563.  
  1564. });
  1565.  
  1566.  
  1567. setupEvents();
  1568.  
  1569.  
  1570.  
  1571. if (isMainWindow) {
  1572.  
  1573. console.groupCollapsed(
  1574. "%cYouTube JS Engine Tamer",
  1575. "background-color: #EDE43B ; color: #000 ; font-weight: bold ; padding: 4px ;"
  1576. );
  1577.  
  1578. console.log("Script is loaded.");
  1579. console.log("This script changes the core mechanisms of the YouTube JS engine.");
  1580.  
  1581. console.log("This script is experimental and subject to further changes.");
  1582.  
  1583. console.log("This might boost your YouTube performance.");
  1584.  
  1585. console.log("CAUTION: This might break your YouTube.");
  1586.  
  1587. console.groupEnd();
  1588.  
  1589. }
  1590.  
  1591.  
  1592. })();