// ==UserScript== // @name 视频加速 // @namespace scriptcat // @version 1.02 // @description Control video playback speed on any website, including live streams // @author Your Name // @match *://*.douyin.com/* // @match *://*.bilibili.com/* // @match *://*.qq.com/* // @match *://*.youku.com/* // @match *://*.iqiyi.com/* // @match *://v.kuaishou.com/* // @match *://*.mgtv.com/* // @match *://*.youtube.com/* // @grant window.onurlchange // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @run-at document-start // ==/UserScript== const css = ` #speedController { position: fixed; top: 20px; right: 20px; background: rgba(0, 0, 0, 0.8); color: white; padding: 10px; border-radius: 5px; z-index: 9999999; font-family: Arial, sans-serif; user-select: none; display: flex; flex-direction: column; gap: 5px; } #speedController button { background: #4CAF50; border: none; color: white; padding: 5px 10px; border-radius: 3px; cursor: pointer; margin: 2px; min-width: 50px; } #speedController button:hover { background: #45a049; } #speedValue { text-align: center; font-size: 16px; margin: 5px 0; } .speed-row { display: flex; justify-content: center; gap: 5px; } `; (function() { 'use strict'; let currentSpeed = 1.0; let originalSetPlaybackRate; let originalPlaybackRate; // 在脚本开始时保存原始的 playbackRate 设置函数 if (HTMLMediaElement.prototype) { originalSetPlaybackRate = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate').set; originalPlaybackRate = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate').get; } // 劫持所有视频元素的 playbackRate 属性 function hijackPlaybackRate() { try { Object.defineProperty(HTMLMediaElement.prototype, 'playbackRate', { get: function() { return currentSpeed; }, set: function(speed) { currentSpeed = speed; try { originalSetPlaybackRate.call(this, speed); } catch (e) { console.error('Error in playbackRate setter:', e); } }, configurable: true }); } catch (e) { console.error('Error hijacking playbackRate:', e); } } // Speed control function function adjustSpeed(speed, decrease = false, isIncrement = false) { try { if (isIncrement) { currentSpeed = Math.min(16, currentSpeed + speed); } else if (decrease) { currentSpeed = Math.max(0.1, currentSpeed - speed); } else { currentSpeed = Math.min(16, speed); } // 更新所有视频的播放速度 const videos = document.getElementsByTagName('video'); Array.from(videos).forEach(video => { try { // 直接设置视频元素的速度 video.defaultPlaybackRate = currentSpeed; originalSetPlaybackRate.call(video, currentSpeed); // 尝试使用 webkitPlaybackRate (针对一些特殊浏览器) if (video.webkitPlaybackRate !== undefined) { video.webkitPlaybackRate = currentSpeed; } // 强制刷新播放状态 const isPlaying = !video.paused; if (isPlaying) { video.pause(); video.play().catch(() => {}); } } catch (e) { console.error('Error setting video speed:', e); } }); // 更新显示的速度值 const speedValue = document.getElementById('speedValue'); if (speedValue) { speedValue.textContent = currentSpeed.toFixed(1) + 'x'; } // 保存当前速度设置 try { GM_setValue('lastSpeed', currentSpeed); } catch (e) { console.error('Error saving speed:', e); } } catch (e) { console.error('Error in adjustSpeed:', e); } } // Create control panel function createSpeedController() { const controller = document.createElement('div'); controller.id = 'speedController'; controller.innerHTML = `
${currentSpeed.toFixed(1)}x
`; // 事件委托处理按钮点击 controller.addEventListener('click', function(e) { const target = e.target; if (target.tagName === 'BUTTON') { e.preventDefault(); e.stopPropagation(); switch(target.id) { case 'speed05': adjustSpeed(0.5); break; case 'speed10': adjustSpeed(1.0); break; case 'speed15': adjustSpeed(1.5); break; case 'speed20': adjustSpeed(2.0); break; case 'speed30': adjustSpeed(3.0); break; case 'speed50': adjustSpeed(5.0); break; case 'speed80': adjustSpeed(8.0); break; case 'speed160': adjustSpeed(16.0); break; case 'speedDown': adjustSpeed(0.1, true); break; case 'speedUp': adjustSpeed(0.1, false, true); break; } } }); document.body.appendChild(controller); return controller; } // Initialize controller with drag functionality function initializeController() { const controller = createSpeedController(); let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; controller.addEventListener("mousedown", function(e) { if (e.target === controller || e.target.id === 'speedValue') { initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; isDragging = true; } }); document.addEventListener("mousemove", function(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; controller.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`; } }); document.addEventListener("mouseup", function() { initialX = currentX; initialY = currentY; isDragging = false; }); } // 添加样式 function addStyles() { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); } // 在脚本开头添加 URL 变化监听 if (window.onurlchange === null) { window.addEventListener('urlchange', () => { // URL变化时重新应用速度设置 setTimeout(applySpeedToAllVideos, 1000); }); } // 提取视频速度应用逻辑为单独函数 function applySpeedToAllVideos() { const videos = document.getElementsByTagName('video'); Array.from(videos).forEach(video => { try { // 直接设置视频元素的速度 video.defaultPlaybackRate = currentSpeed; originalSetPlaybackRate.call(video, currentSpeed); // 尝试使用 webkitPlaybackRate if (video.webkitPlaybackRate !== undefined) { video.webkitPlaybackRate = currentSpeed; } } catch (e) { console.error('Error setting video speed:', e); } }); } // Initialize everything function initialize() { // 检查当前网站是否有视频元素 function checkForVideos() { const videos = document.getElementsByTagName('video'); if (videos.length > 0) { // 只有在找到视频元素时才初始化控制器 try { currentSpeed = GM_getValue('lastSpeed', 1.0); } catch (e) { currentSpeed = 1.0; console.error('Error getting last speed:', e); } hijackPlaybackRate(); // 确保控制器只被创建一次 if (!document.getElementById('speedController')) { addStyles(); initializeController(); } } } // 初始检查 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', checkForVideos); } else { checkForVideos(); } // 定期检查是否有新的视频元素出现 setInterval(checkForVideos, 2000); // 增强的定期检查逻辑 setInterval(() => { const videos = document.getElementsByTagName('video'); Array.from(videos).forEach(video => { if (video.playbackRate !== currentSpeed) { try { // 应用速度设置 video.defaultPlaybackRate = currentSpeed; originalSetPlaybackRate.call(video, currentSpeed); if (video.webkitPlaybackRate !== undefined) { video.webkitPlaybackRate = currentSpeed; } // 监听 loadeddata 事件,确保在视频加载后应用速度 video.addEventListener('loadeddata', () => { setTimeout(() => { originalSetPlaybackRate.call(video, currentSpeed); }, 100); }, { once: true }); // 监听 play 事件,确保在播放时应用速度 video.addEventListener('play', () => { setTimeout(() => { originalSetPlaybackRate.call(video, currentSpeed); }, 100); }, { once: true }); } catch (e) { console.error('Error updating video speed:', e); } } }); }, 1000); // 增加检查间隔到1秒 // 添加 DOM 变化监听,处理动态加载的视频 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length) { setTimeout(() => { checkForVideos(); applySpeedToAllVideos(); }, 500); } }); }); observer.observe(document.body, { childList: true, subtree: true }); } // Start the script initialize(); })();