您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
1-9设置对应倍速,0设为0.5倍,+-调整0.5倍;在视频播放器内显示倍速和变速时间;
// ==UserScript== // @name 通用视频倍速控制器 // @namespace http://tampermonkey.net/ // @version 20250816.1 // @description 1-9设置对应倍速,0设为0.5倍,+-调整0.5倍;在视频播放器内显示倍速和变速时间; // @author atakhalo // @match *://*/* // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; // 当前控制的视频元素 let currentVideo = null; // 倍速显示元素 let speedDisplay = null; // 时间显示元素 let timeDisplay = null; // 倍速提示的计时器 let speedDisplayTimer = null; // 初始化函数 function init() { // 查找当前视频元素 findCurrentVideo(); if (!currentVideo) return; // 确保视频容器有定位上下文 const videoContainer = getVideoContainer(currentVideo); if (!videoContainer) return; // 创建倍速显示元素 speedDisplay = document.createElement('div'); speedDisplay.id = 'custom-speed-display'; speedDisplay.style.cssText = ` position: absolute; bottom: 60px; right: 20px; background: rgba(0,0,0,0.7); color: #ffcc00; font-size: 28px; font-weight: bold; padding: 10px 20px; border-radius: 8px; z-index: 1000; display: none; pointer-events: none; box-shadow: 0 0 15px rgba(255, 204, 0, 0.5); transition: opacity 0.3s; `; videoContainer.appendChild(speedDisplay); // 创建时间显示元素 timeDisplay = document.createElement('div'); timeDisplay.id = 'custom-time-display'; timeDisplay.style.cssText = ` position: absolute; top: 20px; right: 20px; background: rgba(0,0,0,0.7); color: white; font-size: 16px; padding: 8px 15px; border-radius: 6px; z-index: 1000; pointer-events: none; box-shadow: 0 0 10px rgba(255, 255, 255, 0.3); `; videoContainer.appendChild(timeDisplay); // 监听键盘事件 document.addEventListener('keydown', handleKeyPress, true); // true会吞掉点击,避免触发网站的快捷键 // 监听页面变化,以便在动态加载视频时也能工作 const observer = new MutationObserver(() => { if (!currentVideo || !document.contains(currentVideo)) { findCurrentVideo(); if (currentVideo) { moveDisplaysToVideoContainer(); } } }); observer.observe(document, { childList: true, subtree: true }); // 每秒更新一次时间显示 setInterval(updateTimeDisplay, 1000); // 初始更新时间显示 updateTimeDisplay(); } // 获取视频容器 function getVideoContainer(video) { // 尝试找到合适的容器 let container = video.parentElement; while (container) { if (container.tagName === 'BODY') break; const style = window.getComputedStyle(container); if (style.position !== 'static') { return container; } container = container.parentElement; } // 如果没有定位容器,使用body return document.body; } // 移动显示元素到视频容器 function moveDisplaysToVideoContainer() { if (!currentVideo) return; const videoContainer = getVideoContainer(currentVideo); if (!videoContainer) return; if (speedDisplay && speedDisplay.parentNode !== videoContainer) { if (speedDisplay.parentNode) { speedDisplay.parentNode.removeChild(speedDisplay); } videoContainer.appendChild(speedDisplay); } if (timeDisplay && timeDisplay.parentNode !== videoContainer) { if (timeDisplay.parentNode) { timeDisplay.parentNode.removeChild(timeDisplay); } videoContainer.appendChild(timeDisplay); } } // 查找当前视频元素 function findCurrentVideo() { // 优先选择正在播放的视频 const playingVideos = Array.from(document.querySelectorAll('video')).filter(v => !v.paused); if (playingVideos.length > 0) { currentVideo = playingVideos[0]; return; } // 其次选择有焦点的视频 const focusedVideo = document.querySelector('video:focus'); if (focusedVideo) { currentVideo = focusedVideo; return; } // 最后选择页面上第一个视频 const firstVideo = document.querySelector('video'); if (firstVideo) { currentVideo = firstVideo; } } // 处理按键事件 function handleKeyPress(e) { if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return; // 如果焦点在可输入元素上,则不处理 if (['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement.tagName)) { return; } // 查找当前视频 findCurrentVideo(); if (!currentVideo) return; // 确保显示元素在正确的容器中 moveDisplaysToVideoContainer(); let newSpeed = currentVideo.playbackRate; let speedUpdate = true; // 数字键1-9: 设置对应倍速 if (e.key >= '1' && e.key <= '9') { newSpeed = parseInt(e.key); } // 数字0: 设置为0.5倍速 else if (e.key === '0') { newSpeed = 0.5; } // +键: 增加0.5倍速 else if (e.key === '+' || e.key === '=') { newSpeed = Math.min(currentVideo.playbackRate + 0.5, 16); } // -键: 减少0.5倍速 else if (e.key === '-' || e.key === '_') { newSpeed = Math.max(currentVideo.playbackRate - 0.5, 0.1); } // *键: x2倍速 else if (e.key === '*' || e.key === ']') { console.log(currentVideo.playbackRate * 2) newSpeed = Math.min(Math.ceil(currentVideo.playbackRate * 2), 16); } // /键: /2倍速 else if (e.key === '/' || e.key === '[') { newSpeed = Math.max(currentVideo.playbackRate / 2, 0.1); } // 其他按键忽略 else { return; } // 应用新速度 currentVideo.playbackRate = newSpeed; // 显示倍速提示 showSpeedDisplay(newSpeed); // 更新右上角时间显示 updateTimeDisplay(); // 快捷键生效时不再传播 // 1. 阻止默认行为 e.preventDefault(); // 2. 停止事件传播 e.stopImmediatePropagation(); // 3. 停止事件在DOM树中进一步传播 e.stopPropagation(); } // 显示倍速提示 function showSpeedDisplay(speed) { if (!speedDisplay) return; speedDisplay.textContent = speed.toFixed(1) + 'x'; speedDisplay.style.display = 'block'; speedDisplay.style.opacity = '1'; // 清除之前的计时器 if (speedDisplayTimer) clearTimeout(speedDisplayTimer); // 1秒后淡出 speedDisplayTimer = setTimeout(() => { speedDisplay.style.opacity = '0'; setTimeout(() => { speedDisplay.style.display = 'none'; }, 300); }, 1000); } // 更新右上角时间显示(显示变速后时间) function updateTimeDisplay() { if (!currentVideo || !timeDisplay || isNaN(currentVideo.duration)) { if (timeDisplay) timeDisplay.style.display = 'none'; return; } // 计算变速后的时间 const speed = currentVideo.playbackRate; const actualCurrentTime = currentVideo.currentTime; const actualDuration = currentVideo.duration; // 变速后时间 = 原时间 / 速度 const adjustedCurrentTime = actualCurrentTime / speed; const adjustedDuration = actualDuration / speed; // 格式化时间 (秒 -> mm:ss) function formatTime(seconds) { const min = Math.floor(seconds / 60); const sec = Math.floor(seconds % 60); return `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`; } const current = formatTime(adjustedCurrentTime); const total = formatTime(adjustedDuration); timeDisplay.textContent = `${current} / ${total} (${speed.toFixed(1)}x)`; timeDisplay.style.display = 'block'; } // 页面加载完成后初始化 if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();