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);
}
})();