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

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

目前為 2025-04-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name         YouTube CPU Tamer – Hybrid Edition (Improved)
// @name:ja      YouTube CPU負荷軽減スクリプト – ハイブリッド方式(改良版)
// @name:en      YouTube CPU Tamer – Hybrid Edition (Improved)
// @name:zh-CN   YouTube CPU减负脚本 – 混合策略(改进版)
// @name:zh-TW   YouTube CPU負載減輕工具 – 混合策略(改良版)
// @name:ko      YouTube CPU 부하 감소 스크립트 – 하이브리드 방식(개선판)
// @name:fr      Réducteur de charge CPU YouTube – Édition Hybride (Améliorée)
// @name:es      Reductor de carga de CPU para YouTube – Edición Híbrida (Mejorada)
// @name:de      YouTube CPU-Last-Reduzierer – Hybrid-Edition (Verbessert)
// @name:pt-BR   Redutor de uso da CPU no YouTube – Edição Híbrida (Aprimorada)
// @name:ru      Снижение нагрузки на CPU в YouTube – Гибридная версия (Улучшенная)
// @version      3.80
// @description         Reduce CPU load on YouTube using hybrid DOMMutation + AnimationFrame strategy with dynamic switching and delay correction
// @description:ja      DOM変化とrequestAnimationFrameを組み合わせたハイブリッド戦略でYouTubeのCPU負荷を大幅軽減!遅延補正&動的切替も搭載。
// @description:en      Reduce CPU load on YouTube using hybrid DOMMutation + AnimationFrame strategy with dynamic switching and delay correction
// @description:zh-CN   使用混合DOMMutation和requestAnimationFrame策略动态切换并校正延迟,降低YouTube的CPU负载
// @description:zh-TW   採用混合DOMMutation與requestAnimationFrame策略,動態切換並修正延遲,降低YouTube的CPU負載
// @description:ko      DOM 변화 감지 + 애니메이션 프레임 전략으로 YouTube CPU 부하 감소, 지연 보정 및 동적 전환 포함
// @description:fr      Réduisez la charge CPU de YouTube avec une stratégie hybride DOMMutation + AnimationFrame, avec commutation dynamique et correction du délai
// @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
// @description:de      Reduzieren Sie die CPU-Last von YouTube mit einer hybriden DOMMutation + AnimationFrame-Strategie mit dynamischem Wechsel und Verzögerungskorrektur
// @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
// @description:ru      Снижение нагрузки на CPU в YouTube с помощью гибридной стратегии DOMMutation + requestAnimationFrame с динамическим переключением и коррекцией задержки
// @namespace    https://github.com/koyasi777/youtube-cpu-tamer-hybrid
// @author       koyasi777
// @match        https://www.youtube.com/*
// @match        https://www.youtube.com/embed/*
// @match        https://www.youtube-nocookie.com/embed/*
// @match        https://music.youtube.com/*
// @run-at       document-start
// @grant        none
// @inject-into  page
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @homepageURL  https://github.com/koyasi777/youtube-cpu-tamer-hybrid
// @supportURL   https://github.com/koyasi777/youtube-cpu-tamer-hybrid/issues
// ==/UserScript==

(async () => {
  'use strict';

  const key = '__yt_cpu_tamer_hybrid_running__';
  if (window[key]) return;
  window[key] = true;

  const waitForDocumentReady = async () => {
    while (!document.documentElement || !document.head) {
      await new Promise(r => requestAnimationFrame(r));
    }
  };

  const PromiseExt = (() => {
    let _resolve, _reject;
    const h = (res, rej) => { _resolve = res; _reject = rej; };
    return class extends Promise {
      constructor(cb = h) {
        super(cb);
        if (cb === h) {
          this.resolve = _resolve;
          this.reject = _reject;
        }
      }
    };
  })();

  const cleanContext = async () => {
    await waitForDocumentReady();
    const frameId = 'yt-cpu-tamer-frame';
    let iframe = document.getElementById(frameId);
    if (!iframe) {
      iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      iframe.id = frameId;
      iframe.sandbox = 'allow-same-origin';
      document.documentElement.appendChild(iframe);
    }
    while (!iframe.contentWindow) await new Promise(r => requestAnimationFrame(r));
    const { requestAnimationFrame, setTimeout, setInterval, clearTimeout, clearTimeout: ci } = iframe.contentWindow;
    return { requestAnimationFrame, setTimeout, setInterval, clearTimeout, clearInterval: ci };
  };

  const ctx = await cleanContext();
  const { requestAnimationFrame, setTimeout, setInterval, clearTimeout, clearInterval } = ctx;

  const dummyDiv = document.createElement('div');
  dummyDiv.style.display = 'none';
  document.documentElement.appendChild(dummyDiv);

  let currentTrigger;

  const createHybridTriggerBase = () => {
    if (document.visibilityState === 'visible') {
      return (callback) => {
        const p = new PromiseExt();
        requestAnimationFrame(() => p.resolve());
        return p.then(callback);
      };
    } else {
      return (callback) => {
        const attr = 'data-yt-cpu-tamer';
        dummyDiv.setAttribute(attr, Math.random().toString(36));
        const p = new PromiseExt();
        const obs = new MutationObserver(() => {
          obs.disconnect();
          p.resolve();
        });
        obs.observe(dummyDiv, { attributes: true });
        return p.then(callback);
      };
    }
  };

  currentTrigger = createHybridTriggerBase();
  document.addEventListener('visibilitychange', () => {
    currentTrigger = createHybridTriggerBase();
  });

  const overrideTimer = (timerFn, clearFn, label, registry) => {
    return (fn, delay = 0, ...args) => {
      if (typeof fn !== 'function') return timerFn(fn, delay, ...args);
      const id = Symbol(label);
      let isActive = true;
      const handler = () => {
        const start = performance.now();
        currentTrigger(() => {
          const elapsed = performance.now() - start;
          if (!isActive) return;
          if (elapsed >= delay) {
            fn(...args);
          } else {
            setTimeout(() => {
              if (isActive) fn(...args);
            }, delay - elapsed);
          }
        });
      };
      const nativeId = timerFn(handler, delay);
      registry.set(id, () => {
        isActive = false;
        clearFn(nativeId);
      });
      return id;
    };
  };

  const overrideClear = (registry, nativeFn) => {
    return (id) => {
      const stop = registry.get(id);
      if (stop) {
        stop();
        registry.delete(id);
      } else {
        nativeFn(id);
      }
    };
  };

  const activeTimeouts = new Map();
  const activeIntervals = new Map();

  window.setTimeout = overrideTimer(setTimeout, clearTimeout, 'timeout', activeTimeouts);
  window.setInterval = overrideTimer(setInterval, clearInterval, 'interval', activeIntervals);

  window.clearTimeout = overrideClear(activeTimeouts, clearTimeout);
  window.clearInterval = overrideClear(activeIntervals, clearInterval);

  const patchToString = (target, source) => {
    try {
      target.toString = source.toString.bind(source);
    } catch {}
  };

  patchToString(window.setTimeout, setTimeout);
  patchToString(window.setInterval, setInterval);
  patchToString(window.clearTimeout, clearTimeout);
  patchToString(window.clearInterval, clearInterval);

  console.log('[YouTube CPU Tamer – Hybrid Edition] Fully loaded and optimized.');
})();