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