YouTube CPU-Tamer Upgrade

Optimize CPU and GPU usage while watching YouTube videos

  1. // ==UserScript==
  2. // @name YouTube CPU-Tamer Upgrade
  3. // @version 0.3
  4. // @description Optimize CPU and GPU usage while watching YouTube videos
  5. // @author AstralRift
  6. // @namespace https://gf.qytechs.cn/users/1300060
  7. // @match *://*.youtube.com/*
  8. // @match *://*.youtube-nocookie.com/embed/*
  9. // @match *://music.youtube.com/*
  10. // @exclude *://*.youtube.com/*/*.{txt,png,jpg,jpeg,gif,xml,svg,manifest,log,ini}
  11. // @run-at document-start
  12. // @grant none
  13. // @license MIT
  14. // ==/UserScript==
  15.  
  16. (function () {
  17. 'use strict';
  18.  
  19. const win = this instanceof Window ? this : window;
  20.  
  21. const scriptKey = 'YTB_CPUTamer_AstralRift';
  22. if (win[scriptKey]) throw new Error('Duplicated Userscript Calling');
  23. win[scriptKey] = true;
  24.  
  25. const PromiseConstructor = function (executor) {
  26. return new Promise(executor);
  27. };
  28.  
  29. const ExternalPromise = (function () {
  30. let resolve_, reject_;
  31. const handler = function (resolve, reject) {
  32. resolve_ = resolve;
  33. reject_ = reject;
  34. };
  35. const PromiseExternal = function (cb) {
  36. cb = cb || handler;
  37. const promise = new PromiseConstructor(cb);
  38. if (cb === handler) {
  39. promise.resolve = resolve_;
  40. promise.reject = reject_;
  41. }
  42. return promise;
  43. };
  44. return PromiseExternal;
  45. })();
  46.  
  47. const checkGPUAcceleration = (function () {
  48. try {
  49. const canvas = document.createElement('canvas');
  50. return !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
  51. } catch (e) {
  52. return false;
  53. }
  54. })();
  55.  
  56. if (!checkGPUAcceleration) {
  57. throw new Error('Your browser does not support GPU Acceleration. YouTube CPU-Tamer is skipped.');
  58. }
  59.  
  60. const getTimeUpdate = (function () {
  61. window.lastTimeUpdate = 1;
  62. document.addEventListener('timeupdate', function () {
  63. window.lastTimeUpdate = Date.now();
  64. }, true);
  65. let topLastTimeUpdate = -1;
  66. try {
  67. topLastTimeUpdate = top.lastTimeUpdate;
  68. } catch (e) { }
  69. return topLastTimeUpdate >= 1 ? function () { return top.lastTimeUpdate; } : function () { return window.lastTimeUpdate; };
  70. })();
  71.  
  72. const initializeContext = function (win) {
  73. return new PromiseConstructor(function (resolve) {
  74. const waitForFrame = requestAnimationFrame;
  75. let maxRetries = 16;
  76. const frameId = 'vanillajs-iframe-v1';
  77. let frame = document.getElementById(frameId);
  78. let removeFrame = null;
  79. if (!frame) {
  80. frame = document.createElement('iframe');
  81. frame.id = frameId;
  82. const blobURL = typeof webkitCancelAnimationFrame === 'function' && typeof kagi === 'undefined' ? (frame.src = URL.createObjectURL(new Blob([], { type: 'text/html' }))) : null;
  83. frame.sandbox = 'allow-same-origin';
  84. let noscriptElement = document.createElement('noscript');
  85. noscriptElement.appendChild(frame);
  86. (function waitForDocument() {
  87. if (!document.documentElement && maxRetries-- > 0) {
  88. return new PromiseConstructor(waitForFrame).then(waitForDocument);
  89. }
  90. const root = document.documentElement;
  91. root.appendChild(noscriptElement);
  92. if (blobURL) PromiseConstructor.resolve().then(function () { URL.revokeObjectURL(blobURL); });
  93.  
  94. removeFrame = function (setTimeout) {
  95. const removeFrameWhenReady = function (e) {
  96. if (e) win.removeEventListener("DOMContentLoaded", removeFrameWhenReady, false);
  97. e = noscriptElement;
  98. noscriptElement = win = removeFrame = 0;
  99. if (setTimeout) {
  100. setTimeout(function () { e.remove(); }, 200);
  101. } else {
  102. e.remove();
  103. }
  104. };
  105. if (!setTimeout || document.readyState !== 'loading') {
  106. removeFrameWhenReady();
  107. } else {
  108. win.addEventListener("DOMContentLoaded", removeFrameWhenReady, false);
  109. }
  110. };
  111. })();
  112. }
  113. (function waitForFrameContext() {
  114. if (!frame.contentWindow && maxRetries-- > 0) {
  115. return new PromiseConstructor(waitForFrame).then(waitForFrameContext);
  116. }
  117. const frameContext = frame.contentWindow;
  118. if (!frameContext) throw new Error('window is not found.');
  119. try {
  120. const { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout } = frameContext;
  121. const boundFunctions = { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout };
  122. for (let key in boundFunctions) boundFunctions[key] = boundFunctions[key].bind(win);
  123. if (removeFrame) PromiseConstructor.resolve(boundFunctions.setTimeout).then(removeFrame);
  124. resolve(boundFunctions);
  125. } catch (e) {
  126. if (removeFrame) removeFrame();
  127. resolve(null);
  128. }
  129. })();
  130. });
  131. };
  132.  
  133. initializeContext(win).then(function (context) {
  134. if (!context) return null;
  135.  
  136. const { requestAnimationFrame, setTimeout, setInterval, clearTimeout, clearInterval } = context;
  137.  
  138. let animationFrameInterrupter = null;
  139.  
  140. const createRAFHelper = function () {
  141. const animationElement = document.createElement('a-f');
  142. if (!('onanimationiteration' in animationElement)) {
  143. return function (resolve) {
  144. animationFrameInterrupter = resolve;
  145. requestAnimationFrame(resolve);
  146. };
  147. }
  148. animationElement.id = 'a-f';
  149. let animationQueue = null;
  150. animationElement.onanimationiteration = function () {
  151. if (animationQueue !== null) {
  152. animationQueue();
  153. animationQueue = null;
  154. }
  155. };
  156. if (!document.getElementById('afscript')) {
  157. const style = document.createElement('style');
  158. style.id = 'afscript';
  159. style.textContent = `
  160. @keyFrames aF1 {
  161. 0% { order: 0; }
  162. 100% { order: 1; }
  163. }
  164. #a-f[id] {
  165. visibility: collapse !important;
  166. position: fixed !important;
  167. display: block !important;
  168. top: -100px !important;
  169. left: -100px !important;
  170. margin: 0 !important;
  171. padding: 0 !important;
  172. outline: 0 !important;
  173. border: 0 !important;
  174. z-index: -1 !important;
  175. width: 0px !important;
  176. height: 0px !important;
  177. contain: strict !important;
  178. pointer-events: none !important;
  179. animation: 1ms steps(2, jump-none) 0ms infinite alternate forwards running aF1 !important;
  180. }
  181. `;
  182. (document.head || document.documentElement).appendChild(style);
  183. }
  184. document.documentElement.insertBefore(animationElement, document.documentElement.firstChild);
  185. return function (resolve) {
  186. animationQueue = resolve;
  187. animationFrameInterrupter = resolve;
  188. };
  189. };
  190.  
  191. const rafHelper = createRAFHelper();
  192.  
  193. (function () {
  194. let afPromisePrimary, afPromiseSecondary;
  195. afPromisePrimary = afPromiseSecondary = { resolved: true };
  196. let afIndex = 0;
  197. const resolveRAF = function (rafPromise) {
  198. return new PromiseConstructor(function (resolve) {
  199. rafHelper(resolve);
  200. }).then(function () {
  201. rafPromise.resolved = true;
  202. const time = ++afIndex;
  203. if (time > 9e9) afIndex = 9;
  204. rafPromise.resolve(time);
  205. return time;
  206. });
  207. };
  208. const executeRAF = function () {
  209. return new PromiseConstructor(function (resolve) {
  210. const pendingPrimary = !afPromisePrimary.resolved ? afPromisePrimary : null;
  211. const pendingSecondary = !afPromiseSecondary.resolved ? afPromiseSecondary : null;
  212. let time = 0;
  213. if (pendingPrimary && pendingSecondary) {
  214. resolve(PromiseConstructor.all([pendingPrimary, pendingSecondary]).then(function (times) {
  215. const t1 = times[0];
  216. const t2 = times[1];
  217. time = t1 > t2 && t1 - t2 < 8e9 ? t1 : t2;
  218. return time;
  219. }));
  220. } else {
  221. const newPrimary = !pendingPrimary ? (afPromisePrimary = new ExternalPromise()) : null;
  222. const newSecondary = !pendingSecondary ? (afPromiseSecondary = new ExternalPromise()) : null;
  223. const executeSecondary = function () {
  224. if (newPrimary) {
  225. resolveRAF(newPrimary).then(function (t) {
  226. time = t;
  227. if (newSecondary) {
  228. resolveRAF(newSecondary).then(function (t2) {
  229. time = t2;
  230. resolve(time);
  231. });
  232. } else {
  233. resolve(time);
  234. }
  235. });
  236. } else if (newSecondary) {
  237. resolveRAF(newSecondary).then(function (t) {
  238. time = t;
  239. resolve(time);
  240. });
  241. } else {
  242. resolve(time);
  243. }
  244. };
  245. if (pendingSecondary) {
  246. pendingSecondary.then(function () {
  247. executeSecondary();
  248. });
  249. } else if (pendingPrimary) {
  250. pendingPrimary.then(function () {
  251. executeSecondary();
  252. });
  253. } else {
  254. executeSecondary();
  255. }
  256. }
  257. });
  258. };
  259. const executingTasks = new Set();
  260. const wrapFunction = function (handler, store) {
  261. return function () {
  262. const currentTime = Date.now();
  263. if (currentTime - getTimeUpdate() < 800 && currentTime - store.lastTime < 800) {
  264. const id = store.id;
  265. executingTasks.add(id);
  266. executeRAF().then(function (time) {
  267. const isNotRemoved = executingTasks.delete(id);
  268. if (!isNotRemoved || time === store.lastExecution) return;
  269. store.lastExecution = time;
  270. store.lastTime = currentTime;
  271. handler();
  272. });
  273. } else {
  274. store.lastTime = currentTime;
  275. handler();
  276. }
  277. };
  278. };
  279. const createFunctionWrapper = function (originalFunction) {
  280. return function (func, ms) {
  281. if (ms === undefined) ms = 0;
  282. if (typeof func === 'function') {
  283. const store = { lastTime: Date.now() };
  284. const wrappedFunc = wrapFunction(func, store);
  285. store.id = originalFunction(wrappedFunc, ms);
  286. return store.id;
  287. } else {
  288. return originalFunction(func, ms);
  289. }
  290. };
  291. };
  292. win.setTimeout = createFunctionWrapper(setTimeout);
  293. win.setInterval = createFunctionWrapper(setInterval);
  294.  
  295. const clearFunctionWrapper = function (originalFunction) {
  296. return function (id) {
  297. if (id) executingTasks.delete(id) || originalFunction(id);
  298. };
  299. };
  300.  
  301. win.clearTimeout = clearFunctionWrapper(clearTimeout);
  302. win.clearInterval = clearFunctionWrapper(clearInterval);
  303.  
  304. try {
  305. win.setTimeout.toString = setTimeout.toString.bind(setTimeout);
  306. win.setInterval.toString = setInterval.toString.bind(setInterval);
  307. win.clearTimeout.toString = clearTimeout.toString.bind(clearTimeout);
  308. win.clearInterval.toString = clearInterval.toString.bind(clearInterval);
  309. } catch (e) { console.warn(e); }
  310. })();
  311.  
  312. let intervalInterrupter = null;
  313. setInterval(function () {
  314. if (intervalInterrupter === animationFrameInterrupter) {
  315. if (intervalInterrupter !== null) {
  316. animationFrameInterrupter();
  317. intervalInterrupter = null;
  318. }
  319. } else {
  320. intervalInterrupter = animationFrameInterrupter;
  321. }
  322. }, 125);
  323. });
  324. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址