您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Reduce Browser's Energy Impact for playing YouTube Video
当前为
- // ==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.09.02.1
- // @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;')(); // real window object
- const hkey_script = 'nzsxclvflluv';
- if (window[hkey_script]) return; // avoid duplicated scripting
- window[hkey_script] = true;
- //if (!document.documentElement) return window.requestAnimationFrame($$); // no document access
- // copies of native functions
- const $$requestAnimationFrame = window.requestAnimationFrame.bind(window); // core looping
- const $$setTimeout = window.setTimeout.bind(window); // for race
- const $$setInterval = window.setInterval.bind(window); // for background execution
- const $busy = Symbol('$busy');
- let mi = 0;
- const sb = {};
- const sFunc = (prop) => {
- return (func, ms, ...args) => {
- mi++; // start at 1
- let handler = args.length > 0 ? func.bind(null, ...args) : func; // original func if no extra argument
- handler[$busy] = handler[$busy] || 0;
- sb[mi] = {
- handler,
- [prop]: ms, // timeout / interval; value can be undefined
- nextAt: Date.now() + (ms > 0 ? ms : 0) // overload for setTimeout(func);
- };
- 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 pf = (
- handler => new Promise(resolve => {
- // try catch is not required - no further execution on the handler
- // For function handler with high energy impact, discard 1st, 2nd, ... (n-1)th calling: (a,b,c,a,b,d,e,f) => (c,a,b,d,e,f)
- // For function handler with low energy impact, discard or not discard depends on system performance
- if (handler[$busy] == 1) handler();
- handler[$busy]--;
- resolve();
- })
- );
- let jf, tf, toResetFuncHandlers = false;
- let bgExecutionAt = 0; // set at 0 to trigger tf in background startup when requestAnimationFrame is not responsive
- tf = () => {
- if (toResetFuncHandlers) {
- toResetFuncHandlers = false;
- for (let jb in sb) sb[jb].handler[$busy] = 0;
- }
- new Promise(resolveApp1 => {
- // microTask #1
- let now = Date.now();
- bgExecutionAt = now + 160; // if requestAnimationFrame is not responsive (e.g. background running)
- let promisesF = [];
- for (let jb in sb) {
- const o = sb[jb];
- let {
- handler,
- // timeout,
- interval,
- nextAt
- } = o;
- if (now < nextAt) continue;
- handler[$busy]++;
- promisesF.push(handler);
- if (interval > 0) { // prevent undefined, zero, negative values
- o.nextAt += +interval; // convertion from string to number if necessary; decimal is acceptable
- } else {
- rm(jb); // remove timeout
- }
- }
- resolveApp1(promisesF);
- }).then(promisesF => {
- // microTask #2
- bgExecutionAt = Date.now() + 160; // if requestAnimationFrame is not responsive (e.g. background running)
- let hidden = document.hidden; // background running would not call requestAnimationFrame
- if (promisesF.length == 0) { // no handler functions
- // requestAnimationFrame when the page is active
- // execution interval is no less than AnimationFrame
- return hidden || jf();
- }
- if (!hidden) {
- let ret2 = new Promise(resolve => $$setTimeout(resolve, 16));
- let promises = promisesF.map(pf); //microTasks
- let ret1 = Promise.all(promises);
- let race = Promise.race([ret1, ret2]);
- // ensure jf must be called after 16ms to maintain visual changes in high fps.
- // >16ms examples: repaint/reflow, change of style/content
- race.then(jf);
- promises.length = 0;
- } else {
- promisesF.forEach(pf);
- }
- promisesF.length = 0;
- })
- };
- (jf = $$requestAnimationFrame.bind(window, tf))();
- $$setInterval(() => {
- // no response of requestAnimationFrame; e.g. running in background
- // toResetFuncHandlers = true;
- if (Date.now() > bgExecutionAt) {
- toResetFuncHandlers = true;
- tf();
- }
- }, 250);
- // i.e. 4 times per second for background execution - to keep YouTube application functional
- window.addEventListener("yt-navigate-finish", () => {
- toResetFuncHandlers = true; // ensure all function handlers can be executed after YouTube navigation.
- }, true); // capturing event - to let it runs before all everything else.
- // Your code here...
- })();