分享一个自用的视频倍速增强油猴脚本(目前存在两个小问题)
大家好!最近在安卓 Edge 浏览器上用 Tampermonkey 开发了一个简单实用的油猴脚本,专门针对视频播放的痛点:长按视频区域实现倍速调节(类似夸克浏览器的体验)。脚本支持长按左侧从 1× 开始、右侧从 2× 开始,上滑加速、下滑减速,松手恢复原速。还加了小提示和轻振动反馈。
这个脚本基于我跟 AI 助手多次迭代优化,已经基本稳定可用,能兼容大部分 H5 视频网站(B站、YouTube、腾讯视频等)。但还有两个小问题没彻底解决:全屏模式下速度提示消失 和 与 HTML5视频播放器增强脚本(h5player)冲突导致倍速被覆盖。求大佬们帮忙修复或优化!如果能完美兼容 h5player 和全屏提示永可见,就太完美了。
脚本功能亮点
- 长按触发:左侧从 1×、右侧从 2×,300ms 内触发,轻振动提醒。
- 滑动调节:上滑 +0.25×/步,下滑 -0.25×/步,范围 0.25~16×。
- 提示显示:右上角小半透明胶囊,800ms 淡出。
- 防误触:不会弹出系统复制/分享菜单。
- 兼容性:保留原生进度条,全屏/非全屏都可用(除提示问题外)。
- 适用场景:追剧、学习视频倍速神器,远超 Edge 原生播放器。
安装与使用
- 在安卓 Edge 安装 Tampermonkey(扩展商店搜索)。
- 新建脚本,复制下面代码保存启用。
- 打开视频页面,长按视频区域测试。
- 可与 h5player 同时用,但有冲突时建议临时关闭 h5player。
完整脚本代码(版本 2.5,作者:Taocrypt)
// ==UserScript== // @name 手机长按倍速播放增强 // @namespace http://tampermonkey.net/ // @version 2.5 // @description 安卓专用:长按左侧从1×、右侧从2×开始,上滑加速、下滑减速,松手恢复。 // @author Taocrypt // @match *://*/* // @grant none // @run-at document-start // @noframes // ==/UserScript==
(function () {
'use strict';
let currentVideo = null;
let isLongPressing = false;
let originalSpeed = 1.0;
let baseSpeed = 1.0;
let currentSpeed = 1.0;
let startY = 0;
const STEP = 0.25;
const MIN_SPEED = 0.25;
const MAX_SPEED = 16.0;
const LONG_PRESS_TIME = 300;
const SLIDE_THRESHOLD = 25;
let originalSetter = null;
let hijacked = false;
// 小半透明右上角提示(全屏永可见) function showSpeedTip(speed) {
let tip = document.getElementById('my-longpress-tip');
const root = document.documentElement || document.body;
if (!tip || !root.contains(tip)) {
if (tip) tip.remove();
tip = document.createElement('div');
tip.id = 'my-longpress-tip';
tip.style.cssText = `
position: fixed !important;
top: 20px !important; right: 20px !important;
background: rgba(0,0,0,0.6) !important;
color: #fff !important;
padding: 6px 12px !important;
border-radius: 16px !important;
font-size: 16px !important;
font-weight: bold !important;
z-index: 2147483647 !important; /* 最高z-index */
pointer-events: none !important;
transition: opacity 0.4s !important;
backdrop-filter: blur(4px) !important;
`;
root.appendChild(tip);
}
tip.textContent = speed.toFixed(2) + '×';
tip.style.opacity = '0.9';
clearTimeout(tip.hideTimer);
tip.hideTimer = setTimeout(() => tip.style.opacity = '0', 800);
}
// 劫持 playbackRate(最高优先级) function enableHijack() {
if (hijacked) return;
originalSetter = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate')?.set;
if (!originalSetter) return;
Object.defineProperty(HTMLMediaElement.prototype, 'playbackRate', {
set: function(value) {
if (isLongPressing && this === currentVideo) {
currentSpeed = value;
showSpeedTip(value);
}
return originalSetter.call(this, value);
},
configurable: true
});
hijacked = true;
}
// 恢复原生(脚本卸载或出错时保险) function disableHijack() {
if (!hijacked || !originalSetter) return;
Object.defineProperty(HTMLMediaElement.prototype, 'playbackRate', {
set: originalSetter,
configurable: true
});
hijacked = false;
}
function attach(video) {
if (video._myLongPressAttached) return;
video._myLongPressAttached = true;
let pressTimer = null;
video.addEventListener('touchstart', e => {
if (e.touches.length !== 1 || isLongPressing) return;
e.stopImmediatePropagation();
const touch = e.touches[0];
const rect = video.getBoundingClientRect();
if (rect.width < 30 || rect.height < 30) return;
const x = touch.clientX - rect.left;
const width = rect.width;
startY = touch.clientY;
if (pressTimer) clearTimeout(pressTimer);
pressTimer = setTimeout(() => {
isLongPressing = true;
currentVideo = video;
originalSpeed = video.playbackRate || 1.0;
baseSpeed = (x > width * 0.5) ? 2.0 : 1.0;
currentSpeed = baseSpeed;
enableHijack(); // 开启最高优先级劫持
video.playbackRate = currentSpeed; // 触发显示 showSpeedTip(currentSpeed);
if (navigator.vibrate) navigator.vibrate([20, 30, 20]); // 轻柔振动
}, LONG_PRESS_TIME);
}, { passive: false });
video.addEventListener('touchmove', e => {
if (!isLongPressing || !currentVideo) return;
const touch = e.touches[0];
const deltaY = startY - touch.clientY;
const steps = Math.floor(Math.abs(deltaY) / SLIDE_THRESHOLD) * (deltaY > 0 ? 1 : -1);
if (steps !== 0) {
currentSpeed = Math.max(MIN_SPEED, Math.min(MAX_SPEED, currentSpeed + steps * STEP));
video.playbackRate = currentSpeed; // 通过劫持setter自动显示提示
startY = touch.clientY;
}
e.stopImmediatePropagation();
}, { passive: true });
video.addEventListener('touchend', () => {
if (pressTimer) clearTimeout(pressTimer);
if (isLongPressing && currentVideo) {
currentVideo.playbackRate = originalSpeed;
showSpeedTip(originalSpeed);
if (navigator.vibrate) navigator.vibrate(20);
isLongPressing = false;
currentVideo = null;
}
});
video.addEventListener('contextmenu', e => {
e.preventDefault();
e.stopPropagation();
});
}
function scan() {
document.querySelectorAll('video').forEach(attach);
}
scan();
setTimeout(scan, 500);
setTimeout(scan, 1500);
setTimeout(scan, 4000);
setTimeout(scan, 10000);
new MutationObserver(scan).observe(document, { childList: true, subtree: true });
// 全屏变化时强制重建提示
['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'msfullscreenchange'].forEach(evt => {
document.addEventListener(evt, () => {
setTimeout(() => showSpeedTip(currentSpeed || originalSpeed || 1.0), 200);
});
});
})();
求助:两个小问题修复
- 全屏模式下速度提示消失:进入视频全屏后,右上角提示会隐藏或被覆盖。希望能让提示在全屏时永可见(或许用更高优先级 DOM 或 Canvas 绘制?)。
- 与 h5player 脚本冲突:同时开启 h5player 时,长按倍速会被 h5player 的速度监控覆盖/恢复。求方法彻底兼容,让长按倍速优先生效。
评论区(暂无评论)