Greasy Fork

YouTube CPU Tamer by AnimationFrame

减少YouTube影片所致的能源消耗

目前为 2021-08-29 提交的版本。查看 最新版本

// ==UserScript==
// @name         YouTube CPU Tamer by AnimationFrame
// @name:en      YouTube CPU Tamer by AnimationFrame
// @name:jp      YouTube CPU Tamer by AnimationFrame
// @name:zh-tw   YouTube CPU Tamer by AnimationFrame
// @name:zh-cn   YouTube CPU Tamer by AnimationFrame
// @namespace    http://tampermonkey.net/
// @version     2021.08.29.2
// @license     MIT License
// @description     Reduce Browser's Energy Impact for playing YouTube Video
// @description:en  Reduce Browser's Energy Impact for playing YouTube Video
// @description:jp  YouTubeビデオのエネルギーインパクトを減らす
// @description:zh-tw  減少YouTube影片所致的能源消耗
// @description:zh-cn  减少YouTube影片所致的能源消耗
// @author       CY Fung
// @include     https://www.youtube.com/*
// @include     https://www.youtube.com/embed/*
// @include     https://www.youtube-nocookie.com/embed/*
// @include     https://www.youtube.com/live_chat*
// @include     https://www.youtube.com/live_chat_replay*
// @icon         https://www.google.com/s2/favicons?domain=youtube.com
// @run-at      document-start
// @grant       none
// ==/UserScript==
(function $$() {
    'use strict';

    const window = new Function('return window;')();

    const hkey_script = 'nzsxclvflluv';
    if (window[hkey_script]) return;
    window[hkey_script] = true;

    //if (!document.documentElement) return window.requestAnimationFrame($$);

    const $$requestAnimationFrame = window.requestAnimationFrame.bind(window);
    const $$setTimeout = window.setTimeout.bind(window);
    const $$setInterval = window.setInterval.bind(window);

    let mi = 0;
    const sb = {};
    const sFunc = (prop) => {
        return (func, ms, ...args) => {
            mi++;
            sb[mi] = {
                handler: args.length > 0 ? func.bind(null, ...args) : func,
                [prop]: ms,
                nextAt: Date.now() + (ms > 0 ? ms : 0)
            };
            return mi;
        }
    }
    const rm = (jd) => {
        let o = sb[jd];
        if (typeof o != 'object') return;
        for (let k in o) o[k] = null;
        o = null;
        sb[jd] = null;
        delete sb[jd];
    }
    window.setTimeout = sFunc('timeout');
    window.setInterval = sFunc('interval');
    window.clearInterval = window.clearTimeout = rm;

    const $busy = Symbol('$busy');

    const pf = (handler =>
        new Promise(resolve => {
            if (!($busy in handler)) {
                handler[$busy] = true;
                handler();
                delete handler[$busy];
            }
            resolve();
        })
    );

    let jf,tf;
    let bgExecutionAt = 0;
    tf = () => {
        let now = Date.now();
        // ======= MarcoTask [requestAnimationFrame] =======
        let promises = [];
        for (let mi in sb) {
            const o = sb[mi];
            let {
                handler,
                timeout,
                interval,
                nextAt,
                isNative
            } = o;
            if (now < nextAt) continue;
            promises.push(pf(handler));
            if (interval > 0) {
                o.nextAt += interval;
            } else {
                rm(mi);
            }
        }
        // ======= MarcoTask [requestAnimationFrame] =======
        if (!document.hidden){
            if (promises.length > 0) {
                let ret1 = Promise.all(promises);
                let ret2 = new Promise(resolve => $$setTimeout(resolve, 16));
                let race = Promise.race([ret1, ret2]);
                race.then(jf);
            } else {
                jf();
            }
        }
        bgExecutionAt = now + 160; // lower task execution rate for background playing
    };
    (jf = $$requestAnimationFrame.bind(window, tf))();

    $$setInterval(()=>{
        // no response of requestAnimationFrame in background
        if(Date.now()>bgExecutionAt) tf();
    },250);

    // Your code here...
})();