// ==UserScript== // @name 自定义视频倍速播放 // @version 2.4 // @description 自定义视频播放速度 // @author DeepSeek // @match http://*/* // @match https://*/* // @grant GM_setValue // @grant GM_getValue // @run-at document-idle // @namespace https://greasyfork.org/users/452911 // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 全局变量 let currentSpeed = parseFloat(GM_getValue('videoSpeed')) || 1; let controlBtn = null; // 控制按钮 let inputPanel = null; // 输入框面板 let speedInput = null; // 输入框 let initialized = false; // 获取所有视频元素 function getAllVideoElements() { return document.querySelectorAll('video,[class*="player"] *'); } // 主初始化函数 function init() { if (initialized || document.fullscreenElement) return; let videos = getAllVideoElements(); if (videos.length === 0) return; initialized = true; // 应用速度到当前视频 applySpeedToVideos(videos, currentSpeed); // 创建控制按钮(如果不存在) if (!controlBtn || !controlBtn.parentNode) { createControlButton(); } // 尝试处理iframe中的视频 tryProcessIframeVideos(); } // 创建控制按钮 function createControlButton() { controlBtn = document.createElement('div'); controlBtn.textContent = `倍速: ${currentSpeed}x`; controlBtn.title = '点击修改播放倍速'; controlBtn.style.cssText = ` position: fixed; right: 5px; top: 30px; padding: 8px 12px; background: rgba(0, 0, 0, 0.8); color: white; border: 1px solid #555; border-radius: 4px; cursor: pointer; z-index: 9999; font-size: 14px; font-family: Arial, sans-serif; user-select: none; min-width: 70px; text-align: center; `; controlBtn.addEventListener('click', showSpeedInput); document.body.appendChild(controlBtn); } // 显示速度输入框 function showSpeedInput() { // 如果已经有输入面板,先移除 if (inputPanel && inputPanel.parentNode) { inputPanel.parentNode.removeChild(inputPanel); } // 创建输入框面板 inputPanel = document.createElement('div'); inputPanel.style.cssText = ` position: fixed; right: 5px; top: 70px; background: rgba(0, 0, 0, 0.85); border: 1px solid #555; border-radius: 6px; z-index: 10000; font-size: 14px; font-family: Arial, sans-serif; user-select: none; min-width: 150px; padding: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.3); display: flex; flex-direction: column; gap: 10px; `; // 创建标题 const title = document.createElement('div'); title.textContent = '设置播放倍速'; title.style.cssText = ` color: white; font-weight: bold; font-size: 14px; margin-bottom: 5px; text-align: center; `; // 创建输入框 speedInput = document.createElement('input'); speedInput.type = 'number'; speedInput.value = currentSpeed; speedInput.min = '0.1'; speedInput.max = '16'; speedInput.step = '0.1'; speedInput.style.cssText = ` width: 100%; padding: 8px 10px; border: 1px solid #666; border-radius: 4px; background: #333; color: white; font-size: 14px; outline: none; box-sizing: border-box; `; // 创建按钮容器 const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = ` display: flex; gap: 8px; justify-content: space-between; margin-top: 5px; `; // 创建取消按钮 const cancelBtn = document.createElement('button'); cancelBtn.textContent = '取消'; cancelBtn.style.cssText = ` flex: 1; padding: 8px 0; background: #666; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: bold; transition: background 0.2s; `; // 创建确定按钮 const confirmBtn = document.createElement('button'); confirmBtn.textContent = '确定'; confirmBtn.style.cssText = ` flex: 1; padding: 8px 0; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: bold; transition: background 0.2s; `; // 按钮悬停效果 cancelBtn.addEventListener('mouseover', () => { cancelBtn.style.background = '#777'; }); cancelBtn.addEventListener('mouseout', () => { cancelBtn.style.background = '#666'; }); confirmBtn.addEventListener('mouseover', () => { confirmBtn.style.background = '#45a049'; }); confirmBtn.addEventListener('mouseout', () => { confirmBtn.style.background = '#4CAF50'; }); // 按钮事件处理 cancelBtn.addEventListener('click', hideSpeedInput); confirmBtn.addEventListener('click', applyNewSpeed); // 输入框回车和ESC事件 speedInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { applyNewSpeed(); } else if (e.key === 'Escape') { hideSpeedInput(); } }); // 创建常用倍速按钮容器 const quickButtons = document.createElement('div'); quickButtons.style.cssText = ` display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; `; // 常用倍速按钮 const quickSpeeds = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 3.0]; quickSpeeds.forEach(speed => { const btn = document.createElement('button'); btn.textContent = `${speed}x`; btn.style.cssText = ` flex: 1; min-width: calc(33% - 4px); padding: 6px 0; background: ${Math.abs(speed - currentSpeed) < 0.01 ? '#2196F3' : '#555'}; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; transition: background 0.2s; `; btn.addEventListener('click', () => { speedInput.value = speed; // 立即应用快速倍速,不需要确认 setSpeed(speed); hideSpeedInput(); }); btn.addEventListener('mouseover', () => { if (Math.abs(speed - currentSpeed) >= 0.01) { btn.style.background = '#666'; } }); btn.addEventListener('mouseout', () => { if (Math.abs(speed - currentSpeed) >= 0.01) { btn.style.background = '#555'; } }); quickButtons.appendChild(btn); }); // 组装所有元素 buttonContainer.appendChild(cancelBtn); buttonContainer.appendChild(confirmBtn); inputPanel.appendChild(title); inputPanel.appendChild(speedInput); inputPanel.appendChild(buttonContainer); inputPanel.appendChild(quickButtons); document.body.appendChild(inputPanel); // 自动选中输入框内容并聚焦 setTimeout(() => { speedInput.focus(); speedInput.select(); }, 10); // 点击页面其他区域关闭输入框 document.addEventListener('click', handleOutsideClick, true); } // 处理外部点击 function handleOutsideClick(e) { if (inputPanel && inputPanel.parentNode) { // 如果点击的是输入框或按钮,不关闭 if (inputPanel.contains(e.target) || controlBtn.contains(e.target)) { return; } hideSpeedInput(); } } // 隐藏输入框 function hideSpeedInput() { if (inputPanel && inputPanel.parentNode) { inputPanel.parentNode.removeChild(inputPanel); inputPanel = null; // 移除外部点击监听 document.removeEventListener('click', handleOutsideClick, true); } } // 应用新速度 function applyNewSpeed() { let newSpeed = parseFloat(speedInput.value); if (!isNaN(newSpeed) && newSpeed >= 0.1 && newSpeed <= 16) { setSpeed(newSpeed); hideSpeedInput(); } else { // 无效输入,恢复原值 speedInput.value = currentSpeed; alert('请输入有效的倍速值 (0.1 - 16)'); speedInput.focus(); speedInput.select(); } } // 设置速度(主函数) function setSpeed(newSpeed) { // 更新当前速度 currentSpeed = parseFloat(newSpeed.toFixed(2)); GM_setValue('videoSpeed', currentSpeed); // 更新控制按钮显示 if (controlBtn) { controlBtn.textContent = `倍速: ${currentSpeed}x`; } // 应用新速度到当前文档的所有视频 let videos = getAllVideoElements(); applySpeedToVideos(videos, currentSpeed); // 触发自定义事件(用于页面内通信) window.dispatchEvent(new CustomEvent('speedChanged', { detail: { speed: currentSpeed } })); // 尝试处理iframe中的视频 tryProcessIframeVideos(currentSpeed); } // 应用速度到视频元素 function applySpeedToVideos(videoElements, speed) { videoElements.forEach(element => { try { // 如果是video元素 if (element.tagName === 'VIDEO') { element.playbackRate = speed; } // 如果是其他元素,查找其中的video元素 else { let video = element.querySelector('video'); if (video) { video.playbackRate = speed; } else { // 如果元素本身有playbackRate属性,也尝试设置 if ('playbackRate' in element) { element.playbackRate = speed; } } } } catch (e) { // 忽略错误 } }); } // 尝试处理iframe中的视频 function tryProcessIframeVideos(speed = currentSpeed) { document.querySelectorAll('iframe').forEach(iframe => { try { // 尝试访问iframe内容(仅同源iframe) if (iframe.contentDocument) { // 使用相同的选择器 let iframeVideos = iframe.contentDocument.querySelectorAll('video,[class*="player"] *'); applySpeedToVideos(iframeVideos, speed); // 递归处理嵌套iframe let nestedIframes = iframe.contentDocument.querySelectorAll('iframe'); nestedIframes.forEach(nestedIframe => { tryProcessIframeElement(nestedIframe, speed); }); } } catch (e) { // 跨域iframe会抛出安全错误,尝试使用postMessage tryPostMessageToIframe(iframe, speed); } }); } // 递归处理iframe元素 function tryProcessIframeElement(iframe, speed) { try { if (iframe.contentDocument) { let iframeVideos = iframe.contentDocument.querySelectorAll('video,[class*="player"] *'); applySpeedToVideos(iframeVideos, speed); } } catch (e) { // 忽略错误 } } // 尝试使用postMessage与iframe通信 function tryPostMessageToIframe(iframe, speed) { try { iframe.contentWindow.postMessage({ type: 'SET_VIDEO_SPEED', speed: speed }, '*'); } catch (e) { // 忽略错误 } } // 监听来自iframe的消息(用于跨域通信) window.addEventListener('message', (e) => { if (e.data && e.data.type === 'SPEED_CHANGED' && e.data.speed) { setSpeed(e.data.speed); } }); // 监听自定义事件(用于页面内通信) window.addEventListener('speedChanged', (e) => { if (e.detail && e.detail.speed) { setSpeed(e.detail.speed); } }); // 轮询检查新视频(处理动态加载的视频) function checkForNewVideos() { if (document.fullscreenElement) return; let videos = getAllVideoElements(); if (videos.length > 0) { // 如果有视频但控制按钮不存在,初始化 if (!controlBtn || !controlBtn.parentNode) { init(); } else { // 否则只应用速度 applySpeedToVideos(videos, currentSpeed); } } } // 节流函数 let throttleTimer; function throttleCheck() { if (throttleTimer) return; throttleTimer = setTimeout(() => { checkForNewVideos(); throttleTimer = null; }, 800); } // 事件监听器 window.addEventListener('scroll', throttleCheck); window.addEventListener('load', throttleCheck); document.addEventListener('DOMContentLoaded', throttleCheck); // MutationObserver 监听DOM变化 const observer = new MutationObserver(throttleCheck); observer.observe(document.body, { childList: true, subtree: true }); // 初始运行 setTimeout(throttleCheck, 1000); // 添加全局函数以便其他脚本调用 window.setVideoSpeed = setSpeed; window.getCurrentVideoSpeed = () => currentSpeed; })();