Greasy Fork

Greasy Fork is available in English.

全局视频控制栏 - 简洁版

提供悬浮控制栏,支持点击播放暂停、单击快进快退、长按持续加速等操作

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         全局视频控制栏 - 简洁版
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  提供悬浮控制栏,支持点击播放暂停、单击快进快退、长按持续加速等操作
// @author       Your Name
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // ================================
    // 配置常量
    // ================================
    const CONFIG = {
        // 长按持续加速设置
        HOLD_SEEK_INITIAL_DELAY: 400,
        HOLD_SEEK_REPEAT_INTERVAL: 100,
        HOLD_SEEK_ACCELERATION: 1.5,
        HOLD_SEEK_MAX_SPEED: 20,
        HOLD_SEEK_INITIAL_SPEED: 2,

        // 亮度设置
        BRIGHTNESS_MIN: 0.2,
        BRIGHTNESS_MAX: 2.0,
        BRIGHTNESS_STEP: 0.1,

        // 音量设置
        VOLUME_MIN: 0.0,
        VOLUME_MAX: 1.0,
        VOLUME_STEP: 0.05,

        // 界面设置
        HINT_FADE_DELAY: 1200,
        OVERLAY_CLASS: 'tm-mobile-overlay',
        CONTROL_BAR_CLASS: 'tm-mobile-control-bar',
        CONTROL_BUTTON_CLASS: 'tm-mobile-control-button',
        CONTROL_HINT_CLASS: 'tm-mobile-control-hint',

        // 强制覆盖设置
        FORCE_OVERLAY_ZINDEX: '2147483647'
    };

    const DEFAULT_SETTINGS = Object.freeze({
        enableGestures: true,
        enableHints: true,
        enableHoldSeek: true,
        forceOverlay: true,
        doubleTapStep: 10
    });

    // ================================
    // 工具函数
    // ================================
    const Utils = {
        // 数值限制
        clamp(val, min, max) {
            return Math.min(Math.max(val, min), max);
        },

        // 视频跳转
        seekVideo(video, delta) {
            const target = Math.min(
                Math.max(video.currentTime + delta, 0),
                Number.isFinite(video.duration) ? video.duration : Number.MAX_SAFE_INTEGER
            );
            video.currentTime = target;
            return target;
        },

        // 亮度控制
        getBrightness(video) {
            return Number(video.dataset.tmBrightness || '1');
        },

        setBrightness(video, value) {
            video.dataset.tmBrightness = value.toString();
            this.applyBrightness(video, value);
        },

        applyBrightness(video, value) {
            const base = (video.dataset.tmOriginalFilter || '').trim();
            const parts = [];
            if (base) parts.push(base);
            parts.push(`brightness(${value.toFixed(2)})`);
            video.style.filter = parts.join(' ');
        },

        // 音量控制
        setVolume(video, value) {
            video.volume = this.clamp(value, CONFIG.VOLUME_MIN, CONFIG.VOLUME_MAX);
            if (video.volume > 0 && video.muted) video.muted = false;
        },

        // 获取播放器容器 - 增强版
        getPlayerContainer(video) {
            // 尝试多种选择器来找到播放器容器
            const selectors = [
                'video',
                '.video-player',
                '.player',
                '.video-container',
                '.vp-player',
                '.bpx-player-container',
                '.bilibili-player',
                '.ytp-chrome-bottom',
                '.html5-video-player',
                '[data-player]',
                '[class*="player"]',
                '[class*="video"]',
                'div:has(> video)'
            ];
            
            for (const selector of selectors) {
                const container = video.closest(selector);
                if (container && container !== video && container instanceof HTMLElement) {
                    return container;
                }
            }
            
            // 如果没找到合适的容器,返回视频的直接父元素
            return video.parentElement;
        },

        // 时间格式化
        formatTime(seconds) {
            if (!Number.isFinite(seconds) || seconds < 0) {
                return '--:--';
            }
            const whole = Math.floor(seconds);
            const mins = Math.floor(whole / 60);
            const secs = whole % 60;
            return `${mins}:${secs.toString().padStart(2, '0')}`;
        },

        formatTimeLabel(current, duration) {
            const currentText = this.formatTime(current);
            if (!Number.isFinite(duration) || duration <= 0) {
                return currentText;
            }
            return `${currentText} / ${this.formatTime(duration)}`;
        },

        // 禁用原生控制栏的指针事件
        disableNativeControls(video) {
            // 查找并禁用常见的播放器控制栏
            const controlSelectors = [
                '.ytp-chrome-controls',
                '.bpx-player-control',
                '.control-bar',
                '.video-controls',
                '.player-controls',
                '[class*="control"]'
            ];
            
            controlSelectors.forEach(selector => {
                const controls = video.parentElement?.querySelectorAll(selector);
                controls?.forEach(control => {
                    if (control instanceof HTMLElement) {
                        control.style.pointerEvents = 'none';
                        control.style.zIndex = '1';
                    }
                });
            });
        }
    };

    // ================================
    // 设置管理器
    // ================================
    class SettingsManager {
        constructor() {
            this.settings = {
                enableGestures: GM_getValue('enableGestures', DEFAULT_SETTINGS.enableGestures),
                enableHints: GM_getValue('enableHints', DEFAULT_SETTINGS.enableHints),
                enableHoldSeek: GM_getValue('enableHoldSeek', DEFAULT_SETTINGS.enableHoldSeek),
                forceOverlay: GM_getValue('forceOverlay', DEFAULT_SETTINGS.forceOverlay),
                doubleTapStep: GM_getValue('doubleTapStep', DEFAULT_SETTINGS.doubleTapStep)
            };
        }

        updateSetting(key, value) {
            this.settings[key] = value;
            GM_setValue(key, value);
        }

        registerMenuCommands() {
            GM_registerMenuCommand('📱 开关控制栏', () => {
                this.toggleSetting('enableGestures');
            });
            
            GM_registerMenuCommand('⏩ 开关长按加速', () => {
                this.toggleSetting('enableHoldSeek');
            });

            GM_registerMenuCommand('🛡️ 开关置顶浮层', () => {
                this.toggleSetting('forceOverlay');
            });

            GM_registerMenuCommand('💡 开关提示信息', () => {
                this.toggleSetting('enableHints');
            });

            GM_registerMenuCommand('⏭️ 设置快进快退秒数', () => {
                this.promptNumericSetting({
                    key: 'doubleTapStep',
                    title: '请输入每次快进/快退的秒数 (1 - 120)',
                    min: 1,
                    max: 120,
                    step: 1,
                    decimals: 0,
                    unit: 's'
                });
            });

            GM_registerMenuCommand('♻️ 重置所有设置', () => {
                this.resetSettings();
            });
        }

        toggleSetting(key) {
            const newValue = !this.settings[key];
            this.updateSetting(key, newValue);
            const names = {
                enableGestures: '控制栏',
                enableHoldSeek: '长按加速',
                forceOverlay: '置顶浮层',
                enableHints: '提示信息'
            };
            const label = names[key] || key;
            alert(`${label} ${newValue ? '已开启' : '已关闭'}`);
        }

        promptNumericSetting({ key, title, min, max, step = 1, decimals = 2, unit = '' }) {
            const current = this.settings[key];
            const message = `${title}\n当前值: ${current}${unit}`;
            const input = window.prompt(message, current);
            if (input === null) return;

            let value = Number(input);
            if (!Number.isFinite(value)) {
                alert('请输入有效的数字');
                return;
            }

            if (step > 0) {
                value = Math.round(value / step) * step;
            }

            value = Utils.clamp(value, min, max);
            const formatted = decimals >= 0 ? Number(value.toFixed(decimals)) : value;

            this.updateSetting(key, formatted);
            alert(`${title} 已设置为 ${formatted}${unit}`);
        }

        resetSettings() {
            Object.entries(DEFAULT_SETTINGS).forEach(([key, value]) => {
                this.updateSetting(key, value);
            });
            alert('所有设置已重置为默认值');
        }
    }

    // ================================
    // 长按持续加速管理器
    // ================================
    class HoldSeekManager {
        constructor(settingsManager) {
            this.settingsManager = settingsManager;
            this.repeatTimers = new WeakMap();
            this.holdStates = new WeakMap();
            this.speeds = new WeakMap();
        }

        // 开始长按持续加速
        startHoldSeek(video, direction, onProgress) {
            if (!this.settingsManager.settings.enableHoldSeek) return;
            this.stopHoldSeek(video);

            let speed = CONFIG.HOLD_SEEK_INITIAL_SPEED;
            let lastTime = Date.now();

            const performSeek = () => {
                const now = Date.now();
                const deltaTime = (now - lastTime) / 1000; // 转换为秒
                const seekAmount = direction * speed * deltaTime;
                const target = Utils.seekVideo(video, seekAmount);
                lastTime = now;

                // 持续加速
                speed = Math.min(speed * CONFIG.HOLD_SEEK_ACCELERATION, CONFIG.HOLD_SEEK_MAX_SPEED);
                this.speeds.set(video, speed);

                onProgress(seekAmount, speed, target);
            };

            const intervalId = setInterval(performSeek, CONFIG.HOLD_SEEK_REPEAT_INTERVAL);
            this.repeatTimers.set(video, intervalId);
            this.speeds.set(video, speed);

            console.log(`🎬 开始持续加速: ${direction > 0 ? '快进' : '快退'}, 初始速度: ${speed}x`);
        }

        stopHoldSeek(video) {
            const timer = this.repeatTimers.get(video);
            if (timer) {
                clearInterval(timer);
                this.repeatTimers.delete(video);
                this.speeds.delete(video);
                console.log('🎬 停止持续加速');
            }
        }

        isHolding(video) {
            return this.repeatTimers.has(video);
        }

        getCurrentSpeed(video) {
            return this.speeds.get(video) || 0;
        }
    }

    // ================================
    // 手势管理器
    // ================================
    class GestureManager {
        constructor(holdManager, settingsManager) {
            this.holdManager = holdManager;
            this.settingsManager = settingsManager;
            this.bound = new WeakSet();
        }

        ensure(video) {
            if (this.bound.has(video)) return;
            this.bound.add(video);

            if (!this.settingsManager.settings.enableGestures) return;

            const container = Utils.getPlayerContainer(video);
            let overlayHost = container instanceof HTMLElement ? container : video.parentElement;

            if (!overlayHost) {
                console.warn('未找到合适的覆盖层容器,使用视频元素本身');
                overlayHost = video;
            }

            this.forceContainerStyle(overlayHost);

            const { overlay, hint, controlBar } = this.buildOverlay();

            this.attachOverlay(overlayHost, overlay);
            this.initBrightness(video);
            this.initControls(video, hint, controlBar);

            if (this.settingsManager.settings.forceOverlay) {
                Utils.disableNativeControls(video);
            }

            console.log('🎬 视频控制栏已绑定:', video);
        }

        forceContainerStyle(container) {
            if (!this.settingsManager.settings.forceOverlay) return;

            Object.assign(container.style, {
                position: 'relative',
                zIndex: 'auto'
            });
        }

        buildOverlay() {
            const overlay = document.createElement('div');
            overlay.className = CONFIG.OVERLAY_CLASS;

            Object.assign(overlay.style, {
                position: 'absolute',
                inset: '0',
                zIndex: CONFIG.FORCE_OVERLAY_ZINDEX,
                pointerEvents: 'none'
            });

            const hint = document.createElement('div');
            hint.className = CONFIG.CONTROL_HINT_CLASS;

            Object.assign(hint.style, {
                position: 'absolute',
                top: '12%',
                left: '50%',
                transform: 'translate(-50%, -50%)',
                padding: '10px 16px',
                borderRadius: '16px',
                background: 'rgba(30,30,30,0.75)',
                color: '#fff',
                fontSize: '15px',
                fontWeight: '500',
                boxShadow: '0 4px 14px rgba(0,0,0,0.35)',
                backdropFilter: 'blur(10px)',
                opacity: '0',
                transition: 'opacity 0.2s ease',
                pointerEvents: 'none',
                textAlign: 'center',
                minWidth: '120px',
                whiteSpace: 'pre-line'
            });

            const controlBar = document.createElement('div');
            controlBar.className = CONFIG.CONTROL_BAR_CLASS;

            Object.assign(controlBar.style, {
                position: 'absolute',
                left: '50%',
                bottom: '24px',
                transform: 'translate(-50%, 12px)',
                display: 'flex',
                alignItems: 'center',
                gap: '12px',
                padding: '10px 14px',
                borderRadius: '22px',
                background: 'rgba(18,18,18,0.72)',
                boxShadow: '0 10px 24px rgba(0,0,0,0.35)',
                pointerEvents: 'auto',
                backdropFilter: 'blur(12px)',
                opacity: '0',
                transition: 'opacity 0.25s ease, transform 0.25s ease'
            });

            overlay.append(hint, controlBar);
            return { overlay, hint, controlBar };
        }

        attachOverlay(target, overlay) {
            if (!(target instanceof HTMLElement)) return;

            const existingOverlay = target.querySelector(`.${CONFIG.OVERLAY_CLASS}`);
            if (existingOverlay) {
                existingOverlay.remove();
            }

            const style = getComputedStyle(target);
            if (style.position === 'static') {
                if (!target.dataset.tmGestureOriginalPosition) {
                    target.dataset.tmGestureOriginalPosition = target.style.position || '';
                }
                target.style.position = 'relative';
            }

            target.appendChild(overlay);
        }

        initBrightness(video) {
            if (!video.dataset.tmOriginalFilterCaptured) {
                video.dataset.tmOriginalFilterCaptured = '1';
                video.dataset.tmOriginalFilter = video.style.filter || '';
                if (!video.dataset.tmBrightness) {
                    video.dataset.tmBrightness = '1';
                }
                Utils.applyBrightness(video, Number(video.dataset.tmBrightness));
            }
        }

        makeHintHandler(hint) {
            return (text) => {
                if (!this.settingsManager.settings.enableHints) return;

                hint.textContent = text;
                hint.style.opacity = '1';

                clearTimeout(hint.hideTimer);
                hint.hideTimer = setTimeout(() => {
                    hint.style.opacity = '0';
                }, CONFIG.HINT_FADE_DELAY);
            };
        }

        initControls(video, hint, controlBar) {
            const showHint = this.makeHintHandler(hint);
            controlBar.replaceChildren();

            const visibility = this.createVisibilityController(controlBar);
            visibility.hideImmediate();

            const { backButton, playButton, forwardButton } = this.createControlButtons(video, controlBar, showHint);
            this.updatePlayButtonIcon(playButton, video);

            const focusControls = () => visibility.showTemporarily();
            ['pointerdown', 'pointermove', 'touchstart', 'click', 'keydown'].forEach(evt => {
                video.addEventListener(evt, focusControls, { passive: true });
            });

            const updateIcon = () => this.updatePlayButtonIcon(playButton, video);
            video.addEventListener('play', updateIcon);
            video.addEventListener('pause', updateIcon);
        }

        createVisibilityController(controlBar) {
            let hideTimer = null;
            const SHOW_DURATION = 2500;

            const show = () => {
                controlBar.style.opacity = '1';
                controlBar.style.transform = 'translate(-50%, 0)';
            };

            const hide = () => {
                controlBar.style.opacity = '0';
                controlBar.style.transform = 'translate(-50%, 12px)';
            };

            const hideImmediate = () => {
                clearTimeout(hideTimer);
                controlBar.style.transition = 'none';
                hide();
                requestAnimationFrame(() => {
                    controlBar.style.transition = 'opacity 0.25s ease, transform 0.25s ease';
                });
            };

            const showTemporarily = () => {
                show();
                clearTimeout(hideTimer);
                hideTimer = setTimeout(() => {
                    hide();
                }, SHOW_DURATION);
            };

            controlBar.addEventListener('pointerenter', () => {
                clearTimeout(hideTimer);
                show();
            });

            controlBar.addEventListener('pointerleave', () => {
                hideTimer = setTimeout(() => {
                    hide();
                }, SHOW_DURATION);
            });

            return { showTemporarily, hideImmediate };
        }

        createControlButtons(video, container, showHint) {
            const backButton = this.createControlButton('⏪', '点击后退,按住持续快退');
            const playButton = this.createControlButton(video.paused ? '▶️' : '⏸', '播放 / 暂停');
            const forwardButton = this.createControlButton('⏩', '点击快进,按住持续快进');

            container.append(backButton, playButton, forwardButton);

            this.setupSeekButton(video, backButton, -1, showHint);
            this.setupPlayButton(video, playButton, showHint);
            this.setupSeekButton(video, forwardButton, 1, showHint);

            return { backButton, playButton, forwardButton };
        }

        createControlButton(label, title) {
            const button = document.createElement('button');
            button.type = 'button';
            button.className = CONFIG.CONTROL_BUTTON_CLASS;
            button.textContent = label;
            if (title) button.title = title;

            Object.assign(button.style, {
                border: 'none',
                outline: 'none',
                background: 'rgba(255,255,255,0.08)',
                color: '#fff',
                fontSize: '20px',
                width: '52px',
                height: '52px',
                borderRadius: '26px',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                cursor: 'pointer',
                transition: 'background 0.2s ease, transform 0.2s ease',
                userSelect: 'none',
                touchAction: 'manipulation',
                fontFamily: 'inherit'
            });

            return button;
        }

        setButtonPressed(button, pressed) {
            if (!button) return;
            button.style.background = pressed ? 'rgba(255,255,255,0.22)' : 'rgba(255,255,255,0.08)';
            button.style.transform = pressed ? 'scale(0.95)' : 'scale(1)';
        }

        getSeekStep() {
            const configured = this.settingsManager.settings.doubleTapStep;
            if (Number.isFinite(configured) && configured > 0) {
                return configured;
            }
            return DEFAULT_SETTINGS.doubleTapStep;
        }

        setupPlayButton(video, button, showHint) {
            this.setButtonPressed(button, false);

            button.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                this.togglePlayback(video, showHint);
            });

            const reset = () => this.setButtonPressed(button, false);

            button.addEventListener('pointerdown', (e) => {
                if (e.pointerType === 'mouse' && e.button !== 0) return;
                this.setButtonPressed(button, true);
            });
            button.addEventListener('pointerup', reset);
            button.addEventListener('pointerleave', reset);
            button.addEventListener('pointercancel', reset);
        }

        showPlaybackHint(video, showHint, state) {
            if (!showHint) return;
            const label = Utils.formatTimeLabel(video.currentTime, video.duration);
            if (state === 'play') {
                showHint(`▶️ 播放\n${label}`);
            } else {
                showHint(`⏸ 暂停\n${label}`);
            }
        }

        updatePlayButtonIcon(button, video) {
            if (!button) return;
            button.textContent = video.paused ? '▶️' : '⏸';
        }

        setupSeekButton(video, button, direction, showHint) {
            const state = {
                pointerId: null,
                holdTimer: null,
                holding: false
            };

            const clearHoldTimer = () => {
                if (state.holdTimer) {
                    clearTimeout(state.holdTimer);
                    state.holdTimer = null;
                }
            };

            const stopHold = (withMessage) => {
                if (!state.holding) {
                    this.holdManager.stopHoldSeek(video);
                    return;
                }

                const peak = this.holdManager.getCurrentSpeed(video) || 0;
                this.holdManager.stopHoldSeek(video);
                state.holding = false;

                if (withMessage) {
                    const label = Utils.formatTimeLabel(video.currentTime, video.duration);
                    const suffix = peak > 0 ? `\n最高速度: ${peak.toFixed(1)}x` : '';
                    showHint(`✅ ${direction > 0 ? '快进结束' : '快退结束'}${suffix}\n${label}`);
                }
            };

            const startHold = () => {
                if (!this.settingsManager.settings.enableHoldSeek) return;
                state.holding = true;
                const dirTxt = direction > 0 ? '快进' : '快退';
                showHint(`⏩ ${dirTxt}中...\n${Utils.formatTimeLabel(video.currentTime, video.duration)}`);
                this.holdManager.startHoldSeek(video, direction, (seekAmount, speed, target) => {
                    const label = Utils.formatTimeLabel(target, video.duration);
                    showHint(`⏩ ${dirTxt} ${speed.toFixed(1)}x\n${label}`);
                });
            };

            const finish = (e, canceled) => {
                if (state.pointerId === null || e.pointerId !== state.pointerId) return;

                clearHoldTimer();

                if (!state.holding && !canceled) {
                    const step = this.getSeekStep();
                    const delta = direction * step;
                    const target = Utils.seekVideo(video, delta);
                    const label = Utils.formatTimeLabel(target, video.duration);
                    const icon = direction > 0 ? '▶▶' : '◀◀';
                    const action = direction > 0 ? '快进' : '快退';
                    showHint(`${icon} ${action} ${Math.abs(delta)}s\n${label}`);
                }

                stopHold(!canceled);
                this.setButtonPressed(button, false);

                try {
                    button.releasePointerCapture(e.pointerId);
                } catch (_) {}

                state.pointerId = null;
            };

            button.addEventListener('pointerdown', (e) => {
                if (e.pointerType === 'mouse' && e.button !== 0) return;
                e.preventDefault();
                e.stopPropagation();

                if (state.pointerId !== null) return;

                state.pointerId = e.pointerId;
                this.setButtonPressed(button, true);

                clearHoldTimer();
                if (this.settingsManager.settings.enableHoldSeek) {
                    state.holdTimer = setTimeout(() => {
                        state.holdTimer = null;
                        startHold();
                    }, CONFIG.HOLD_SEEK_INITIAL_DELAY);
                }

                try {
                    button.setPointerCapture(e.pointerId);
                } catch (_) {}
            }, { passive: false });

            button.addEventListener('pointerup', (e) => finish(e, false), { passive: false });
            button.addEventListener('pointercancel', (e) => finish(e, true), { passive: false });
            button.addEventListener('pointerleave', (e) => finish(e, true), { passive: false });
        }

        togglePlayback(video, showHint) {
            if (video.paused) {
                const result = video.play();
                if (result && typeof result.then === 'function') {
                    result.then(() => {
                        this.showPlaybackHint(video, showHint, 'play');
                    }).catch(() => {
                        video.dispatchEvent(new MouseEvent('click', { bubbles: true }));
                    });
                } else {
                    this.showPlaybackHint(video, showHint, 'play');
                }
            } else {
                video.pause();
                this.showPlaybackHint(video, showHint, 'pause');
            }
        }

        scan(root = document) {
            root.querySelectorAll('video').forEach(v => this.ensure(v));
        }
    }

    // ================================
    // 初始化脚本
    // ================================
    function initialize() {
        const settingsManager = new SettingsManager();
        const holdManager = new HoldSeekManager(settingsManager);
        const gestureManager = new GestureManager(holdManager, settingsManager);

        // 注册菜单命令
        settingsManager.registerMenuCommands();

        // 监听DOM变化
        const observer = new MutationObserver(mutations => {
            for (const mutation of mutations) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType !== 1) return;
                    if (node.tagName === 'VIDEO') {
                        setTimeout(() => gestureManager.ensure(node), 100);
                    } else {
                        gestureManager.scan(node);
                    }
                });
            }
        });

        observer.observe(document.documentElement, {
            childList: true,
            subtree: true
        });

        // 初始扫描
        setTimeout(() => {
            gestureManager.scan();
            console.log('🎬 全局视频控制栏脚本已加载 - 简洁版 v3.0');
            console.log('📱 功能说明:');
            console.log('   ⏯️ 单击播放键: 播放/暂停');
            console.log('   ⏩ 单击快进/快退: 按设置秒数跳转');
            console.log('   ⏱️ 长按快进/快退: 持续加速跳转');
            console.log('   💡 控制栏提示: 显示当前时间与操作状态');
        }, 1000);
    }

    // 启动脚本
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();