// ==UserScript== // @name 全网页强制倍速 // @namespace https://workbuddy.cn // @version 1.0 // @description 在所有网页强制锁定视频倍速(电脑/手机通用):原生接口劫持+高频守护+动态视频+iframe 支持 // @match *://*/* // @grant none // @run-at document-idle // @author WorkBuddy // ==/UserScript== (function(){ function initForceSpeed(){ if (window.__forceSpeedReady) return; window.__forceSpeedReady = true; var currentRate = 1; var forceAutoRate = false; var NATIVE_RATE_GET = null, NATIVE_RATE_SET = null; try { var nd = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate'); if (nd && nd.get && nd.set) { NATIVE_RATE_GET = nd.get; NATIVE_RATE_SET = nd.set; } } catch (e) {} function getAllVideos(){ var vids = []; (function collect(doc, depth){ if (depth > 6) return; try { var vs = doc.querySelectorAll('video'); for (var i = 0; i < vs.length; i++) vids.push(vs[i]); var fs = doc.querySelectorAll('iframe'); for (var j = 0; j < fs.length; j++) { try { if (fs[j].contentDocument) collect(fs[j].contentDocument, depth + 1); } catch (e) {} } } catch (e) {} })(document, 0); return vids; } function forceVideoRate(v, rate){ try { if (NATIVE_RATE_SET) { try { NATIVE_RATE_SET.call(v, rate); } catch (e) {} } else { v.playbackRate = rate; } try { if (v.defaultPlaybackRate !== rate) v.defaultPlaybackRate = rate; } catch (e) {} if (v.__lockRate !== rate) { v.__lockRate = rate; try { Object.defineProperty(v, 'playbackRate', { configurable: true, enumerable: true, get: function(){ try { return NATIVE_RATE_GET ? NATIVE_RATE_GET.call(v) : rate; } catch (e) { return rate; } }, set: function(){} }); } catch (e) {} } if (Math.abs(v.playbackRate - rate) > 0.01 && NATIVE_RATE_SET) { try { NATIVE_RATE_SET.call(v, rate); } catch (e) {} } } catch (e) {} } function applyRateLocal(rate){ try { var vs = document.querySelectorAll('video'); for (var i = 0; i < vs.length; i++) forceVideoRate(vs[i], rate); } catch (e) {} } function broadcastRate(rate){ (function send(win){ try { win.postMessage({ __forceSpeedRate: rate }, '*'); } catch (e) {} var frames, len = 0; try { frames = win.frames; len = frames.length; } catch (e) { return; } for (var i = 0; i < len; i++) { try { send(frames[i]); } catch (e) {} } })(window); } function unlockVideos(){ var vs = getAllVideos(); for (var i = 0; i < vs.length; i++) { try { delete vs[i].playbackRate; } catch (e) {} try { delete vs[i].__lockRate; } catch (e) {} } } function setRate(rate){ currentRate = rate; applyRateLocal(rate); var vs = getAllVideos(); for (var i = 0; i < vs.length; i++) forceVideoRate(vs[i], rate); broadcastRate(rate); } var frameRate = 0; try { window.addEventListener('message', function(e){ if (e.source === window) return; var d = e.data; if (!d || typeof d !== 'object' || typeof d.__forceSpeedRate !== 'number') return; if (d.__forceSpeedRate < 0) { frameRate = 0; unlockVideos(); applyRateLocal(0); return; } frameRate = d.__forceSpeedRate; applyRateLocal(d.__forceSpeedRate); }); } catch (e) {} try { document.addEventListener('ratechange', function(e){ var v = e.target; if (!v || v.tagName !== 'VIDEO') return; var target = forceAutoRate ? currentRate : frameRate; if (!target) return; if (Math.abs(v.playbackRate - target) > 0.01) { setTimeout(function(){ forceVideoRate(v, target); }, 50); } }, true); } catch (e) {} var rateTimer = null; function startRateKeeper(){ if (rateTimer) return; rateTimer = setInterval(function(){ if (!forceAutoRate) return; applyRateLocal(currentRate); var vs = getAllVideos(); for (var i = 0; i < vs.length; i++) forceVideoRate(vs[i], currentRate); broadcastRate(currentRate); }, 500); } function stopRateKeeper(){ if (rateTimer) { clearInterval(rateTimer); rateTimer = null; } } try { var mo = new MutationObserver(function(){ if (!forceAutoRate) return; applyRateLocal(currentRate); var vs = getAllVideos(); for (var i = 0; i < vs.length; i++) forceVideoRate(vs[i], currentRate); }); mo.observe(document, { childList: true, subtree: true }); } catch (e) {} function setSpeed(r){ forceAutoRate = true; currentRate = r; setRate(r); startRateKeeper(); } var speeds = [0.5, 1, 1.25, 1.5, 1.75, 2, 3, 4, 8, 16]; if (document.getElementById('force-speed-root')) return; var root = document.createElement('div'); root.id = 'force-speed-root'; root.style.cssText = 'position:fixed;top:80px;right:12px;z-index:2147483647;'; document.documentElement.appendChild(root); var shadow = root.attachShadow({ mode: 'open' }); shadow.innerHTML = '' + '