左手单手调视频进度

模拟方向键原生行为(支持长按),左手即可完成进度调整

// ==UserScript==
// @name         左手单手调视频进度
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  模拟方向键原生行为(支持长按),左手即可完成进度调整
// @author       Vz
// @license MIT
// @match        *://*.bilibili.com/*
// @match        *://*.youtube.com/*
// @match        *://*.pan.baidu.com/pfile/video?*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // ===================== 配置区域 =====================
    const CONFIG = {
        keyMap: {
            'x': 'ArrowLeft',  // X键模拟左方向键
            'c': 'ArrowRight'  // C键模拟右方向键
        },
        pressConfig: {
            repeatDelay: 500,   // 长按首次触发延迟(毫秒)
            repeatInterval: 100 // 长按重复间隔(毫秒)
        }
    };

    // ==================== 核心逻辑 =====================
    const activeKeys = new Map();
    let isModifierPressed = false;

    function createKeyboardEvent(type, key) {
        const isLeft = key === 'ArrowLeft';
        return new KeyboardEvent(type, {
            key: key,
            code: key,
            keyCode: isLeft ? 37 : 39,
            bubbles: true,
            cancelable: true,
            composed: true
        });
    }

    function dispatchToVideo(event) {
        // 优先发送给焦点元素
        const target = document.activeElement.tagName === 'VIDEO'
            ? document.activeElement
            : document.querySelector('video, .bpx-player-video-wrap video');

        if (target) {
            target.dispatchEvent(event);
            return true;
        }
        return false;
    }

    function startKeyRepeat(key) {
        const nativeKey = CONFIG.keyMap[key];
        if (!nativeKey) return;

        // 发送初始keydown
        const downEvent = createKeyboardEvent('keydown', nativeKey);
        if (!dispatchToVideo(downEvent)) return;

        // 设置重复定时器
        const timer = setInterval(() => {
            const repeatEvent = createKeyboardEvent('keydown', nativeKey);
            dispatchToVideo(repeatEvent);
        }, CONFIG.pressConfig.repeatInterval);

        activeKeys.set(key, {
            timer: timer,
            isPressed: true
        });
    }

    function stopKeyRepeat(key) {
        const record = activeKeys.get(key);
        if (record) {
            clearInterval(record.timer);
            const upEvent = createKeyboardEvent('keyup', CONFIG.keyMap[key]);
            dispatchToVideo(upEvent);
            activeKeys.delete(key);
        }
    }

    // ================== 事件监听器 ====================
    function handleKeyDown(e) {
        // 忽略组合键
        if (e.ctrlKey || e.altKey || e.metaKey) {
            isModifierPressed = true;
            return;
        }

        const key = e.key.toLowerCase();
        if (!CONFIG.keyMap[key] || isModifierPressed) return;

        // 排除输入元素
        const activeEl = document.activeElement;
        if (['INPUT','TEXTAREA','SELECT'].includes(activeEl.tagName) || activeEl.isContentEditable) {
            return;
        }

        e.preventDefault();
        e.stopPropagation();

        if (!activeKeys.has(key)) {
            // 立即触发首次按下
            const downEvent = createKeyboardEvent('keydown', CONFIG.keyMap[key]);
            if (dispatchToVideo(downEvent)) {
                // 设置长按定时器
                const timer = setTimeout(() => {
                    startKeyRepeat(key);
                }, CONFIG.pressConfig.repeatDelay);

                activeKeys.set(key, {
                    timer: timer,
                    isPressed: true
                });
            }
        }
    }

    function handleKeyUp(e) {
        const key = e.key.toLowerCase();
        if (activeKeys.has(key)) {
            clearTimeout(activeKeys.get(key).timer);
            stopKeyRepeat(key);
        }
        isModifierPressed = false;
    }

    // ================== 初始化 ====================
    document.addEventListener('keydown', handleKeyDown, true);
    document.addEventListener('keyup', handleKeyUp, true);

    // 保护系统快捷键
    document.addEventListener('keydown', (e) => {
        if (e.ctrlKey || e.metaKey) {
            // 清理所有激活状态
            activeKeys.forEach((_, key) => stopKeyRepeat(key));
            activeKeys.clear();
        }
    }, true);
})();