Greasy Fork

Greasy Fork is available in English.

YouTube CPU-Tamer Upgrade (Firefox Optimized)

Optimize CPU and GPU usage while watching YouTube videos on Firefox

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                YouTube CPU-Tamer Upgrade (Firefox Optimized)
// @version             0.4.2
// @description         Optimize CPU and GPU usage while watching YouTube videos on Firefox
// @author              AstralRift
// @namespace           http://greasyfork.icu/users/1300060
// @match               *://*.youtube.com/*
// @match               *://*.youtube-nocookie.com/embed/*
// @match               *://music.youtube.com/*
// @exclude             *://*.youtube.com/*/*.{txt,png,jpg,jpeg,gif,xml,svg,manifest,log,ini}
// @run-at              document-start
// @grant               none
// @license             MIT
// ==/UserScript==

(function () {
  'use strict';

  const win = typeof window !== 'undefined' ? window : this;

  const scriptKey = 'YTB_CPUTamer_AstralRift';
  if (win[scriptKey]) return;
  win[scriptKey] = true;

  const PromiseConstructor = Promise;

  const ExternalPromise = (function () {
    return function (cb) {
      let res, rej;
      const promise = new PromiseConstructor((resolve, reject) => {
        res = resolve;
        rej = reject;
      });
      if (!cb) {
        promise.resolve = res;
        promise.reject = rej;
      } else {
        cb(res, rej);
      }
      return promise;
    };
  })();

  const checkGPUAcceleration = (function () {
    try {
      const canvas = document.createElement('canvas');
      return !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
    } catch (e) {
      return false;
    }
  })();

  if (!checkGPUAcceleration) return;

  const getTimeUpdate = (function () {
    // FIX: 统一用 win,避免 iframe/非顶层环境写错对象
    win.lastTimeUpdate = 1;

    document.addEventListener('timeupdate', () => {
      win.lastTimeUpdate = Date.now();
    }, { capture: true, passive: true });

    return function () {
      try {
        if (win.top && win.top !== win && win.top.lastTimeUpdate >= 1) {
          return win.top.lastTimeUpdate;
        }
      } catch (e) { }
      return win.lastTimeUpdate;
    };
  })();

  const initializeContext = function (win) {
    return new PromiseConstructor((resolve) => {
      const waitForFrame = requestAnimationFrame;
      let maxRetries = 20;
      const frameId = 'vanillajs-iframe-v1';

      let container = null;
      let frame = document.getElementById(frameId);

      const tryInject = () => {
        if (!document.documentElement) return false;
        if (!frame) {
          frame = document.createElement('iframe');
          frame.id = frameId;
          frame.sandbox = 'allow-same-origin';
          frame.style.display = 'none';

          container = document.createElement('noscript');
          container.appendChild(frame);
          document.documentElement.appendChild(container);
        }
        return true;
      };

      const cleanup = (later) => {
        const c = container;
        container = null;
        if (!c) return;
        const rm = () => { try { c.remove(); } catch (e) { } };
        if (later) setTimeout(rm, 200);
        else rm();
      };

      (function pollContext() {
        if (!tryInject()) {
          if (maxRetries-- > 0) return waitForFrame(pollContext);
          return resolve(null);
        }

        if (!frame.contentWindow && maxRetries-- > 0) {
          return waitForFrame(pollContext);
        }

        const ctx = frame.contentWindow;
        if (!ctx) {
          cleanup(false);
          return resolve(null);
        }

        try {
          const { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout } = ctx;
          const bound = {
            requestAnimationFrame: requestAnimationFrame.bind(win),
            setInterval: setInterval.bind(win),
            setTimeout: setTimeout.bind(win),
            clearInterval: clearInterval.bind(win),
            clearTimeout: clearTimeout.bind(win)
          };
          cleanup(true);
          resolve(bound);
        } catch (e) {
          cleanup(false);
          resolve(null);
        }
      })();
    });
  };

  initializeContext(win).then((context) => {
    if (!context) return;

    const { requestAnimationFrame, setTimeout, setInterval, clearTimeout, clearInterval } = context;
    let animationFrameInterrupter = null;

    const createRAFHelper = function () {
      const afEl = document.createElement('a-f');
      afEl.id = 'a-f';

      if (!('onanimationiteration' in afEl)) {
        return (res) => {
          animationFrameInterrupter = res;
          requestAnimationFrame(res);
        };
      }

      let queue = null;
      afEl.onanimationiteration = () => {
        const fn = queue;
        if (fn) {
          queue = null;
          fn();
        }
      };

      if (!document.getElementById('af-style')) {
        const style = document.createElement('style');
        style.id = 'af-style';
        style.textContent = `
          @keyframes aF1 { from { opacity: 0; } to { opacity: 1; } }
          #a-f {
            position: fixed; top: -1px; left: -1px; width: 1px; height: 1px;
            pointer-events: none; visibility: hidden;
            animation: 1ms linear infinite alternate aF1;
          }
        `;
        (document.head || document.documentElement).appendChild(style);
      }

      if (document.documentElement) {
        document.documentElement.insertBefore(afEl, document.documentElement.firstChild);
      }

      return (res) => {
        queue = res;
        animationFrameInterrupter = res;
      };
    };

    const rafHelper = createRAFHelper();

    (function () {
      let afP1 = { resolved: true };
      let afP2 = { resolved: true };
      let afIdx = 0;

      const resolveRAF = (p) => {
        return new PromiseConstructor((res) => {
          rafHelper(res);
        }).then(() => {
          p.resolved = true;
          // FIX: 回绕逻辑与原脚本一致(9e9量级,回到9)
          let t = ++afIdx;
          if (t > 9000000000) afIdx = t = 9;
          p.resolve(t);
          return t;
        });
      };

      const executeRAF = () => {
        const p1 = !afP1.resolved ? afP1 : null;
        const p2 = !afP2.resolved ? afP2 : null;

        // FIX: 与原脚本一致:同时 pending 时取更合理的时间戳(含溢出区间判断)
        if (p1 && p2) {
          return PromiseConstructor.all([p1, p2]).then(v => {
            const t1 = v[0], t2 = v[1];
            return (t1 > t2 && (t1 - t2) < 8000000000) ? t1 : t2;
          });
        }

        const n1 = !p1 ? (afP1 = new ExternalPromise()) : null;
        const n2 = !p2 ? (afP2 = new ExternalPromise()) : null;

        return new PromiseConstructor((res) => {
          const run = () => {
            if (n1) {
              resolveRAF(n1).then(t => {
                if (n2) resolveRAF(n2).then(res);
                else res(t);
              });
            } else if (n2) {
              resolveRAF(n2).then(res);
            } else {
              res(0);
            }
          };
          if (p2) p2.then(run);
          else if (p1) p1.then(run);
          else run();
        });
      };

      const tasks = new Set();

      const wrap = (fn, store) => {
        return function () {
          const now = Date.now();
          if (now - getTimeUpdate() < 800 && now - store.last < 800) {
            const tid = store.id;
            tasks.add(tid);
            executeRAF().then((t) => {
              // FIX: 先删除,避免 tid 因为 t==store.exec 等原因长期滞留
              const ok = tasks.delete(tid);
              if (!ok || t === store.exec) return;
              store.exec = t;
              store.last = now;
              fn();
            });
          } else {
            store.last = now;
            fn();
          }
        };
      };

      const makeWrapper = (orig) => {
        return function (f, ms = 0) {
          if (typeof f === 'function') {
            // micro-opt: 预置字段,稳定对象形状
            const store = { last: Date.now(), exec: 0, id: 0 };
            const w = wrap(f, store);
            store.id = orig(w, ms);
            return store.id;
          }
          return orig(f, ms);
        };
      };

      win.setTimeout = makeWrapper(setTimeout);
      win.setInterval = makeWrapper(setInterval);

      const makeClear = (orig) => {
        return (id) => {
          if (id) {
            tasks.delete(id);
            orig(id);
          }
        };
      };

      win.clearTimeout = makeClear(clearTimeout);
      win.clearInterval = makeClear(clearInterval);

      try {
        const s = Function.prototype.toString;
        win.setTimeout.toString = s.bind(setTimeout);
        win.setInterval.toString = s.bind(setInterval);
        win.clearTimeout.toString = s.bind(clearTimeout);
        win.clearInterval.toString = s.bind(clearInterval);
      } catch (e) { }
    })();

    let interrupter = null;
    setInterval(() => {
      if (interrupter === animationFrameInterrupter && interrupter !== null) {
        animationFrameInterrupter();
        interrupter = null;
      } else {
        interrupter = animationFrameInterrupter;
      }
    }, 125);
  });
})();