大家好!最近在安卓 Edge 浏览器上用 Tampermonkey 开发了一个简单实用的油猴脚本,专门针对视频播放的痛点:长按视频区域实现倍速调节(类似夸克浏览器的体验)。脚本支持长按左侧从 1× 开始、右侧从 2× 开始,上滑加速、下滑减速,松手恢复原速。还加了小提示和轻振动反馈。

这个脚本基于我跟 AI 助手多次迭代优化,已经基本稳定可用,能兼容大部分 H5 视频网站(B站、YouTube、腾讯视频等)。但还有两个小问题没彻底解决:全屏模式下速度提示消失与 HTML5视频播放器增强脚本(h5player)冲突导致倍速被覆盖。求大佬们帮忙修复或优化!如果能完美兼容 h5player 和全屏提示永可见,就太完美了。

脚本功能亮点

  • 长按触发:左侧从 1×、右侧从 2×,300ms 内触发,轻振动提醒。
  • 滑动调节:上滑 +0.25×/步,下滑 -0.25×/步,范围 0.25~16×。
  • 提示显示:右上角小半透明胶囊,800ms 淡出。
  • 防误触:不会弹出系统复制/分享菜单。
  • 兼容性:保留原生进度条,全屏/非全屏都可用(除提示问题外)。
  • 适用场景:追剧、学习视频倍速神器,远超 Edge 原生播放器。

安装与使用

  1. 在安卓 Edge 安装 Tampermonkey(扩展商店搜索)。
  2. 新建脚本,复制下面代码保存启用。
  3. 打开视频页面,长按视频区域测试。
  4. 可与 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); }); }); })();

求助:两个小问题修复

  1. 全屏模式下速度提示消失:进入视频全屏后,右上角提示会隐藏或被覆盖。希望能让提示在全屏时永可见(或许用更高优先级 DOM 或 Canvas 绘制?)。
  2. 与 h5player 脚本冲突:同时开启 h5player 时,长按倍速会被 h5player 的速度监控覆盖/恢复。求方法彻底兼容,让长按倍速优先生效。

📌 转载信息
转载时间:
2026/1/2 23:48:05