// ==UserScript==
// @name 通用 HTML5 视频倍速自定义 (长按右方向键3倍速)
// @version 1.0
// @description 支持全网所有现代HTML5视频播放器。长按右方向键3倍速(不快进),松开恢复。
// @author Longlone & Gemini
// @license MIT
// @match *://*/*
// @grant none
// @namespace https://greasyfork.org/users/1548979
// @downloadURL https://update.greasyfork.icu/scripts/575988/%E9%80%9A%E7%94%A8%20HTML5%20%E8%A7%86%E9%A2%91%E5%80%8D%E9%80%9F%E8%87%AA%E5%AE%9A%E4%B9%89%20%28%E9%95%BF%E6%8C%89%E5%8F%B3%E6%96%B9%E5%90%91%E9%94%AE3%E5%80%8D%E9%80%9F%29.user.js
// @updateURL https://update.greasyfork.icu/scripts/575988/%E9%80%9A%E7%94%A8%20HTML5%20%E8%A7%86%E9%A2%91%E5%80%8D%E9%80%9F%E8%87%AA%E5%AE%9A%E4%B9%89%20%28%E9%95%BF%E6%8C%89%E5%8F%B3%E6%96%B9%E5%90%91%E9%94%AE3%E5%80%8D%E9%80%9F%29.meta.js
// ==/UserScript==
(function () {
'use strict';
// ==========================================
// 核心逻辑变量
// ==========================================
let pressTimer = null; // 计时器ID
let isLongPressing = false; // 是否处于长按状态标记
let savedSpeed = 1; // 保存长按前的速度
let LONG_PRESS_DELAY = 220; // 长按判定时间(毫秒),可按需微调
// ==========================================
// 动态寻找当前页面主要的 video 元素
// ==========================================
function getActiveVideo() {
const videos = Array.from(document.querySelectorAll('video'));
if (videos.length === 0) return null;
if (videos.length === 1) return videos[0];
let playingVideo = videos.find(v => !v.paused && v.readyState > 2);
if (playingVideo) return playingVideo;
return videos.sort((a, b) => (b.clientWidth * b.clientHeight) - (a.clientWidth * a.clientHeight))[0];
}
// ==========================================
// 判断用户是否正在输入框中打字 (防误触)
// ==========================================
function isTyping(e) {
const target = e.target;
const tagName = target.tagName.toUpperCase();
return tagName === 'INPUT' || tagName === 'TEXTAREA' || target.isContentEditable;
}
// ==========================================
// 视觉指示器逻辑 (UI)
// ==========================================
function getOrCreateIndicator(videoDom) {
const container = videoDom.parentElement;
if (!container) return null;
// 确保父容器可以作为 absolute 元素的定位参考系
const computedStyle = window.getComputedStyle(container);
if (computedStyle.position === 'static') {
container.style.position = 'relative';
}
let indicator = container.querySelector('.speed-indicator-gemini-v3');
if (!indicator) {
indicator = document.createElement('div');
indicator.className = 'speed-indicator-gemini-v3';
Object.assign(indicator.style, {
position: 'absolute',
top: '10%',
left: '50%',
transform: 'translateX(-50%)',
padding: '10px 20px',
backgroundColor: 'rgba(0, 0, 0, 0.75)',
color: 'white',
borderRadius: '8px',
fontSize: '18px',
fontWeight: 'bold',
zIndex: '999999',
opacity: '0',
transition: 'opacity 0.2s ease-in-out',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
gap: '8px',
letterSpacing: '2px'
});
indicator.innerHTML = '▶▶▶';
container.appendChild(indicator);
}
return indicator;
}
function toggleIndicator(videoDom, show) {
const indicator = getOrCreateIndicator(videoDom);
if (indicator) {
indicator.style.opacity = show ? '1' : '0';
}
}
// ==========================================
// 按下键盘逻辑
// ==========================================
window.addEventListener('keydown', (e) => {
if (isTyping(e)) return;
if (e.code !== 'ArrowRight') return;
const videoDom = getActiveVideo();
if (!videoDom) return;
// 阻止原生快进
e.preventDefault();
e.stopPropagation();
if (isLongPressing || e.repeat) return;
if (!pressTimer) {
savedSpeed = videoDom.playbackRate;
pressTimer = setTimeout(() => {
isLongPressing = true;
videoDom.playbackRate = 3.0;
// 触发长按时,显示 UI 指示器
toggleIndicator(videoDom, true);
}, LONG_PRESS_DELAY);
}
}, true);
// ==========================================
// 松开键盘逻辑
// ==========================================
window.addEventListener('keyup', (e) => {
if (isTyping(e)) return;
if (e.code !== 'ArrowRight') return;
const videoDom = getActiveVideo();
if (!videoDom) return;
e.preventDefault();
e.stopPropagation();
if (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
if (isLongPressing) {
// 松手时,恢复速度,隐藏 UI 指示器
videoDom.playbackRate = savedSpeed;
isLongPressing = false;
toggleIndicator(videoDom, false);
} else {
// 短按:触发 5 秒快进
videoDom.currentTime = Math.min(videoDom.currentTime + 5, videoDom.duration);
}
}, true);
})();