YouTube CPU負載減輕工具 – 混合策略(改良版)

採用混合DOMMutation與requestAnimationFrame策略,動態切換並修正延遲,降低YouTube的CPU負載

  1. // ==UserScript==
  2. // @name YouTube CPU Tamer – Hybrid Edition (Improved)
  3. // @name:ja YouTube CPU負荷軽減スクリプト – ハイブリッド方式(改良版)
  4. // @name:en YouTube CPU Tamer – Hybrid Edition (Improved)
  5. // @name:zh-CN YouTube CPU减负脚本 – 混合策略(改进版)
  6. // @name:zh-TW YouTube CPU負載減輕工具 – 混合策略(改良版)
  7. // @name:ko YouTube CPU 부하 감소 스크립트 – 하이브리드 방식(개선판)
  8. // @name:fr Réducteur de charge CPU YouTube – Édition Hybride (Améliorée)
  9. // @name:es Reductor de carga de CPU para YouTube – Edición Híbrida (Mejorada)
  10. // @name:de YouTube CPU-Last-Reduzierer – Hybrid-Edition (Verbessert)
  11. // @name:pt-BR Redutor de uso da CPU no YouTube – Edição Híbrida (Aprimorada)
  12. // @name:ru Снижение нагрузки на CPU в YouTube – Гибридная версия (Улучшенная)
  13. // @version 4.50
  14. // @description Reduce CPU load on YouTube using hybrid DOMMutation + AnimationFrame strategy with dynamic switching and delay correction
  15. // @description:ja DOM変化とrequestAnimationFrameを組み合わせたハイブリッド戦略でYouTubeのCPU負荷を大幅軽減!遅延補正&動的切替も搭載。
  16. // @description:en Reduce CPU load on YouTube using hybrid DOMMutation + AnimationFrame strategy with dynamic switching and delay correction
  17. // @description:zh-CN 使用混合DOMMutation和requestAnimationFrame策略动态切换并校正延迟,降低YouTube的CPU负载
  18. // @description:zh-TW 採用混合DOMMutation與requestAnimationFrame策略,動態切換並修正延遲,降低YouTube的CPU負載
  19. // @description:ko DOM 변화 감지 + 애니메이션 프레임 전략으로 YouTube CPU 부하 감소, 지연 보정 및 동적 전환 포함
  20. // @description:fr Réduisez la charge CPU de YouTube avec une stratégie hybride DOMMutation + AnimationFrame, avec commutation dynamique et correction du délai
  21. // @description:es Reduce la carga de CPU en YouTube mediante una estrategia híbrida de DOMMutation y AnimationFrame, con conmutación dinámica y corrección de retrasos
  22. // @description:de Reduzieren Sie die CPU-Last von YouTube mit einer hybriden DOMMutation + AnimationFrame-Strategie mit dynamischem Wechsel und Verzögerungskorrektur
  23. // @description:pt-BR Reduza o uso da CPU no YouTube com uma estratégia híbrida DOMMutation + AnimationFrame com troca dinâmica e correção de atraso
  24. // @description:ru Снижение нагрузки на CPU в YouTube с помощью гибридной стратегии DOMMutation + requestAnimationFrame с динамическим переключением и коррекцией задержки
  25. // @namespace https://github.com/koyasi777/youtube-cpu-tamer-hybrid
  26. // @author koyasi777
  27. // @match https://www.youtube.com/*
  28. // @match https://www.youtube.com/embed/*
  29. // @match https://www.youtube-nocookie.com/embed/*
  30. // @match https://music.youtube.com/*
  31. // @run-at document-start
  32. // @grant none
  33. // @inject-into page
  34. // @license MIT
  35. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  36. // @homepageURL https://github.com/koyasi777/youtube-cpu-tamer-hybrid
  37. // @supportURL https://github.com/koyasi777/youtube-cpu-tamer-hybrid/issues
  38. // ==/UserScript==
  39.  
  40. (() => {
  41. "use strict";
  42.  
  43. const FLAG = "__yt_cpu_tamer_hybrid_running__";
  44. if (window[FLAG]) return;
  45. window[FLAG] = true;
  46.  
  47. const nextAnimationFrame = () => new Promise(r => requestAnimationFrame(r));
  48. const waitForDocReady = async () => {
  49. while (!document.documentElement || !document.head) {
  50. await nextAnimationFrame();
  51. }
  52. };
  53.  
  54. const PromiseExt = (() => {
  55. let _res, _rej;
  56. const shim = (r, j) => { _res = r; _rej = j; };
  57. return class extends Promise {
  58. constructor(cb = shim) {
  59. super(cb);
  60. if (cb === shim) {
  61. // @ts-ignore
  62. this.resolve = _res;
  63. // @ts-ignore
  64. this.reject = _rej;
  65. }
  66. }
  67. };
  68. })();
  69.  
  70. const setup = async () => {
  71. await waitForDocReady();
  72.  
  73. const FRAME_ID = "yt-cpu-tamer-timer-frame";
  74. let frame = document.getElementById(FRAME_ID);
  75. if (frame && (!frame.contentWindow || !frame.contentWindow.setTimeout)) {
  76. frame.remove();
  77. frame = null;
  78. }
  79. if (!frame) {
  80. frame = document.createElement("iframe");
  81. frame.id = FRAME_ID;
  82. frame.style.display = "none";
  83. frame.sandbox = "allow-same-origin allow-scripts";
  84. frame.srcdoc = "<!doctype html><title>yt-cpu-tamer</title>";
  85. document.documentElement.appendChild(frame);
  86. }
  87. while (!frame.contentWindow) {
  88. await nextAnimationFrame();
  89. }
  90.  
  91. const {
  92. requestAnimationFrame: frameRAF,
  93. setTimeout: frameSetTimeout,
  94. setInterval: frameSetInterval,
  95. clearTimeout: frameClearTimeout,
  96. clearInterval: frameClearInterval
  97. } = frame.contentWindow;
  98.  
  99. const DUMMY_ID = "yt-cpu-tamer-trigger-node";
  100. let dummy = document.getElementById(DUMMY_ID);
  101. if (!dummy) {
  102. dummy = document.createElement("div");
  103. dummy.id = DUMMY_ID;
  104. dummy.style.display = "none";
  105. document.documentElement.appendChild(dummy);
  106. }
  107.  
  108. let timersAreNative = document.visibilityState !== "visible";
  109.  
  110. const makeHybridTrigger = () => {
  111. if (document.visibilityState === "visible") {
  112. return cb => {
  113. const p = new PromiseExt();
  114. requestAnimationFrame(p.resolve);
  115. return p.then(cb);
  116. };
  117. } else {
  118. return cb => {
  119. const p = new PromiseExt();
  120. const MO = new MutationObserver(() => {
  121. MO.disconnect();
  122. p.resolve();
  123. });
  124. MO.observe(dummy, { attributes: true });
  125. dummy.setAttribute("data-yt-cpu-tamer", Math.random().toString(36));
  126. return p.then(cb);
  127. };
  128. }
  129. };
  130.  
  131. /** @type {(cb: () => void) => Promise<void>} */
  132. let currentTrigger = makeHybridTrigger();
  133.  
  134. const VC_LISTENER_FLAG = "__yt_cpu_tamer_visibility_listener__";
  135. if (!window[VC_LISTENER_FLAG]) {
  136. document.addEventListener("visibilitychange", () => {
  137. timersAreNative = document.visibilityState !== "visible";
  138. currentTrigger = makeHybridTrigger();
  139. });
  140. window[VC_LISTENER_FLAG] = true;
  141. }
  142.  
  143. const activeTimeouts = new Set();
  144. const activeIntervals = new Set();
  145.  
  146. const makeTimer = (nativeTimer, pool) => {
  147. return function patchedTimer(fn, delay = 0, ...args) {
  148. if (typeof fn !== "function" || timersAreNative) {
  149. return nativeTimer(fn, delay, ...args);
  150. }
  151. const id = nativeTimer(() => {
  152. currentTrigger(() => fn.apply(window, args));
  153. }, delay);
  154. pool.add(id);
  155. return id;
  156. };
  157. };
  158.  
  159. const makeClear = (nativeClear, pool) => id => {
  160. if (pool.has(id)) pool.delete(id);
  161. nativeClear(id);
  162. };
  163.  
  164. const patchTimers = () => {
  165. const alreadyPatched = "__yt_cpu_tamer_timers_patched__";
  166. if (window[alreadyPatched]) return;
  167. window[alreadyPatched] = true;
  168.  
  169. window.setTimeout = makeTimer(frameSetTimeout, activeTimeouts);
  170. window.setInterval = makeTimer(frameSetInterval, activeIntervals);
  171. window.clearTimeout = makeClear(frameClearTimeout, activeTimeouts);
  172. window.clearInterval = makeClear(frameClearInterval, activeIntervals);
  173.  
  174. const mirrorToString = (patched, native) => {
  175. try {
  176. patched.toString = native.toString.bind(native);
  177. } catch {/* ignore */}
  178. };
  179. mirrorToString(window.setTimeout, frameSetTimeout);
  180. mirrorToString(window.setInterval, frameSetInterval);
  181. mirrorToString(window.clearTimeout, frameClearTimeout);
  182. mirrorToString(window.clearInterval, frameClearInterval);
  183.  
  184. console.log("[YouTube CPU Tamer – Hybrid Edition] Timers patched");
  185. };
  186.  
  187. if (document.readyState === "loading") {
  188. document.addEventListener("DOMContentLoaded", patchTimers, { once: true });
  189. } else {
  190. patchTimers();
  191. }
  192.  
  193. window.addEventListener("yt-navigate-finish", () => {
  194. console.log("[YouTube CPU Tamer] yt-navigate-finish – re‑applying patch");
  195. timersAreNative = true;
  196. setTimeout(() => {
  197. timersAreNative = document.visibilityState !== "visible";
  198. currentTrigger = makeHybridTrigger();
  199. }, 5000); // Safety delay before re-enabling hybrid mode
  200. patchTimers();
  201. });
  202. };
  203.  
  204. setup().catch(err => console.error("[YouTube CPU Tamer] setup failed", err));
  205. })();