B站长按左键快进、双击后长按快退(保留点击暂停和双击全屏)

长按鼠标左键快进,双击后长按快退,保留单击暂停和双击全屏功能

// ==UserScript==
// @name         B站长按左键快进、双击后长按快退(保留点击暂停和双击全屏)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  长按鼠标左键快进,双击后长按快退,保留单击暂停和双击全屏功能
// @author       PZ Yin (Modified)
// @match        https://www.bilibili.com/video/*
// @require      https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/limonte-sweetalert2/11.4.4/sweetalert2.all.min.js
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // 配置键
    const FORWARD_RATE_KEY = 'bilibili_quick_rate';
    const REWIND_INTERVAL_KEY = 'bilibili_rewind_interval';

    // 获取配置
    let forwardRate = GM_getValue(FORWARD_RATE_KEY) || 3;
    let rewindInterval = GM_getValue(REWIND_INTERVAL_KEY) || 5;

    // 状态变量
    let quickMode = false;      // 是否处于快进模式
    let rewindMode = false;     // 是否处于快退模式
    let originalRate = 1;       // 原始播放速率
    let isLongPress = false;    // 是否长按
    let pressTimer = null;      // 长按计时器
    let rewindTimer = null;     // 快退计时器
    let lastClickTime = 0;      // 上次点击时间
    let isDoubleClick = false;  // 是否双击
    let rewindJustFinished = false; // 快退刚刚结束标志

    // 创建HUD显示
    const createHUD = () => {
        const hud = document.createElement('div');
        Object.assign(hud.style, {
            position: 'fixed',
            top: '10%',
            left: '50%',
            transform: 'translateX(-50%)',
            background: 'rgba(0, 0, 0, 0.7)',
            color: 'white',
            padding: '10px 20px',
            borderRadius: '10px',
            fontSize: '18px',
            zIndex: 9999,
            display: 'none',
            pointerEvents: 'none'  // 防止HUD拦截鼠标事件
        });
        hud.id = 'bilibili-control-hud';
        document.body.appendChild(hud);
        return hud;
    };

    const hud = createHUD();

    // 显示/隐藏HUD
    const showHUD = (text) => {
        hud.textContent = text;
        hud.style.display = 'block';

        // 3秒后自动隐藏
        setTimeout(() => {
            if (hud.textContent === text) {
                hideHUD();
            }
        }, 3000);
    };

    const hideHUD = () => {
        hud.style.display = 'none';
    };

    // 初始化函数
    const initialize = () => {
        // 找到视频元素
        const findVideo = setInterval(() => {
            const video = document.querySelector('video');
            if (video) {
                clearInterval(findVideo);
                setupControls(video);
            }
        }, 500);
    };

    // 设置控制
    const setupControls = (video) => {
        // 获取视频容器
        const container = document.querySelector('.bpx-player-video-wrap') ||
                          document.querySelector('.bilibili-player-video-wrap') ||
                          video.parentElement;

        if (!container) return;

        // 监听鼠标按下
        container.addEventListener('mousedown', (e) => {
            if (e.button !== 0) return; // 只处理左键

            // 如果快退刚刚结束,忽略这次点击
            if (rewindJustFinished) {
                e.stopPropagation();
                e.preventDefault();
                rewindJustFinished = false;
                return;
            }

            // 检测双击
            const now = Date.now();
            isDoubleClick = (now - lastClickTime < 300);
            lastClickTime = now;

            // 记录原始播放速率
            originalRate = video.playbackRate;
            isLongPress = false;

            // 设置长按计时器
            pressTimer = setTimeout(() => {
                isLongPress = true;

                if (isDoubleClick) {
                    // 双击后长按:快退模式
                    rewindMode = true;

                    // 设置快退计时器
                    rewindTimer = setInterval(() => {
                        // 每隔200ms快退一次
                        video.currentTime = Math.max(0, video.currentTime - rewindInterval);
                    }, 200);

                    showHUD(`快退模式: 每步 ${rewindInterval} 秒`);

                    // 阻止事件传播以防止触发其他操作
                    e.stopPropagation();
                    e.preventDefault();
                } else {
                    // 普通长按:快进模式
                    quickMode = true;
                    video.playbackRate = forwardRate;
                    showHUD(`快进模式: ${forwardRate}x`);
                }
            }, 300); // 300ms判定为长按
        });

        // 防止双击后快退松开触发其他事件
    let preventActionUntil = 0;

    // 监听双击事件来阻止全屏
    container.addEventListener('dblclick', (e) => {
        if (rewindMode || (isLongPress && isDoubleClick)) {
            e.stopPropagation();
            e.preventDefault();
        }
    }, true); // 捕获阶段

    // 监听鼠标松开
    container.addEventListener('mouseup', (e) => {
        if (e.button !== 0) return; // 只处理左键

        // 清除长按计时器
        clearTimeout(pressTimer);

        // 处理快进模式
        if (quickMode) {
            video.playbackRate = originalRate;
            quickMode = false;
            hideHUD();
        }

        // 处理快退模式
        if (rewindMode) {
            clearInterval(rewindTimer);
            rewindMode = false;
            hideHUD();

            // 设置防止操作的时间锁(500ms内不触发其他操作)
            preventActionUntil = Date.now() + 500;

            // 阻止事件传播,防止双击全屏或单击暂停
            e.stopPropagation();
            e.preventDefault();

            // 确保视频播放
            if (video.paused) {
                video.play().catch(() => {});
            } else {
                // 如果视频已经在播放,确保不会被暂停
                // 通过设置一个短暂的间隔来检查和恢复播放状态
                const ensurePlayingInterval = setInterval(() => {
                    if (video.paused) {
                        video.play().catch(() => {});
                    }
                }, 50);

                // 500ms后停止检查
                setTimeout(() => {
                    clearInterval(ensurePlayingInterval);
                    rewindJustFinished = false;
                }, 500);
            }

            // 标记快退刚刚结束,用于阻止后续的点击事件
            rewindJustFinished = true;

            // 要确保之后的几个连续 click 事件也被拦截,设置一个临时的拦截器
            const clickBlocker = (evt) => {
                evt.stopPropagation();
                evt.preventDefault();
                evt.stopImmediatePropagation();
            };

            // 给 document 和 container 都添加事件监听器
            document.addEventListener('click', clickBlocker, true);
            container.addEventListener('click', clickBlocker, true);

            // 500ms 后移除这些监听器
            setTimeout(() => {
                document.removeEventListener('click', clickBlocker, true);
                container.removeEventListener('click', clickBlocker, true);
                rewindJustFinished = false;
            }, 500);

            return false;
        }
    });

    // 在捕获阶段阻止长按和快退后短时间内产生的各种事件
    container.addEventListener('click', (e) => {
        // 阻止快退结束后或长按状态下的点击事件
        if (isLongPress || Date.now() < preventActionUntil) {
            e.stopImmediatePropagation();
            e.preventDefault();
        }
    }, true); // 捕获阶段

        // 处理鼠标离开容器
        container.addEventListener('mouseleave', () => {
            // 清除计时器
            clearTimeout(pressTimer);
            clearInterval(rewindTimer);

            // 重置模式
            if (quickMode) {
                video.playbackRate = originalRate;
                quickMode = false;
            }

            rewindMode = false;
            isLongPress = false;
            hideHUD();
        });

        // 设置快进速率菜单
        GM_registerMenuCommand('设置快进速率', async () => {
            const result = await Swal.fire({
                title: '设置快进速率(0=暂停,最高16)',
                input: 'range',
                inputAttributes: {
                    min: 0,
                    max: 16,
                    step: 0.1,
                },
                inputValue: forwardRate
            });

            if (result.isConfirmed) {
                forwardRate = +result.value;
                GM_setValue(FORWARD_RATE_KEY, forwardRate);
                Swal.fire({
                    position: 'top-end',
                    icon: 'success',
                    title: `设置成功,当前快进速率: ${forwardRate}x`,
                    showConfirmButton: false,
                    timer: 1500
                });
            }
        });

        // 设置快退间隔菜单
        GM_registerMenuCommand('设置快退间隔', async () => {
            const result = await Swal.fire({
                title: '设置快退间隔(1-30秒)',
                input: 'range',
                inputAttributes: {
                    min: 1,
                    max: 30,
                    step: 1,
                },
                inputValue: rewindInterval
            });

            if (result.isConfirmed) {
                rewindInterval = +result.value;
                GM_setValue(REWIND_INTERVAL_KEY, rewindInterval);
                Swal.fire({
                    position: 'top-end',
                    icon: 'success',
                    title: `设置成功,当前快退间隔: ${rewindInterval}秒/步`,
                    showConfirmButton: false,
                    timer: 1500
                });
            }
        });
    };

    // 启动脚本
    initialize();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址