BiliSpeedUp

提供B站多倍速播放功能,支持自定义记忆播放速度、鼠标滚轮调节、触控板调节、记忆倍速

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         BiliSpeedUp
// @name:zh-CN   B站视频倍速工具
// @namespace    https://github.com/timerring/BiliSpeedUp
// @version      1.0.0
// @description  提供B站多倍速播放功能,支持自定义记忆播放速度、鼠标滚轮调节、触控板调节、记忆倍速
// @author       timerring
// @match        https://www.bilibili.com/video/*
// @match        https://www.bilibili.com/bangumi/play/*
// @grant        none
// @license      MIT
// @homepage     https://github.com/timerring/BiliSpeedUp
// @supportURL   https://github.com/timerring/BiliSpeedUp/issues
// ==/UserScript==

(function() {
    'use strict';

(function () {
    'use strict';

    /**
     * 配置常量
     */
    const CONFIG = {
        STORAGE_KEY: 'bilibili_custom_speed',
        DEFAULT_SPEED: 1.0,
        MIN_SPEED: 0.07,
        MAX_SPEED: 10.0,
        SPEED_STEP: 0.01,
        CHECK_INTERVAL: 1000,
        MAX_RETRIES: 30,
        // 引导功能版本控制,修改版本号重新显示引导
        TOUR_VERSION: '1.0.0'
    };

    /**
     * 滚轮和触控板配置
     */
    const WHEEL_CONFIG = {
        MOUSE_STEP: 0.1,           // 鼠标滚轮步进
        TOUCHPAD_STEP: 0.02,       // 触控板步进
        TOUCHPAD_THRESHOLD: 30,    // 触控板累积阈值
        TOUCHPAD_DELTA_LIMIT: 50   // 触控板判断阈值
    };

    /**
     * 选择器常量
     */
    const SELECTORS = {
        VIDEO: 'video',
        PLAYBACK_RATE_BTN: '.bpx-player-ctrl-btn.bpx-player-ctrl-playbackrate',
        PLAYBACK_RATE_MENU: '.bpx-player-ctrl-playbackrate-menu',
        PLAYBACK_RATE_RESULT: '.bpx-player-ctrl-playbackrate-result',
        MENU_ITEM: '.bpx-player-ctrl-playbackrate-menu-item',
        CUSTOM_SPEED_INPUT: '#custom-speed-input',
        CUSTOM_SPEED_ITEM: '.custom-speed-item'
    };

    /**
     * 本地存储管理模块
     */

    /**
     * 获取保存的倍速
     * @returns {number} 保存的倍速值
     */
    function getSavedSpeed() {
        try {
            const saved = localStorage.getItem(CONFIG.STORAGE_KEY);
            return saved ? parseFloat(saved) : CONFIG.DEFAULT_SPEED;
        } catch (e) {
            console.error('获取保存的倍速失败:', e);
            return CONFIG.DEFAULT_SPEED;
        }
    }

    /**
     * 保存倍速
     * @param {number} speed - 倍速值
     */
    function saveSpeed(speed) {
        try {
            localStorage.setItem(CONFIG.STORAGE_KEY, speed.toString());
        } catch (e) {
            console.error('保存倍速失败:', e);
        }
    }

    /**
     * 检查引导是否已显示
     * @param {string} version - 引导版本号
     * @returns {boolean}
     */
    function isTourShown(version) {
        const key = `bilibili_speed_tour_shown_v${version}`;
        return !!localStorage.getItem(key);
    }

    /**
     * 标记引导已显示
     * @param {string} version - 引导版本号
     */
    function markTourShown(version) {
        const key = `bilibili_speed_tour_shown_v${version}`;
        localStorage.setItem(key, 'true');
    }

    /**
     * 倍速控制核心模块
     */

    /**
     * 设置视频播放速度
     * @param {number} speed - 倍速值
     */
    function setVideoSpeed(speed) {
        const video = document.querySelector(SELECTORS.VIDEO);
        if (video) {
            video.playbackRate = speed;
            saveSpeed(speed);
            updateSpeedDisplay(speed);
        }
    }

    /**
     * 更新倍速显示
     * @param {number} speed - 倍速值
     */
    function updateSpeedDisplay(speed) {
        const resultDiv = document.querySelector(SELECTORS.PLAYBACK_RATE_RESULT);
        if (resultDiv) {
            resultDiv.textContent = speed === 1 ? '倍速' : `${speed.toFixed(2)}x`;
        }

        // 更新菜单项的激活状态
        const menuItems = document.querySelectorAll(SELECTORS.MENU_ITEM);
        menuItems.forEach(item => {
            const itemValue = parseFloat(item.getAttribute('data-value'));
            if (Math.abs(itemValue - speed) < 0.001) {
                item.classList.add('bpx-state-active');
            } else {
                item.classList.remove('bpx-state-active');
            }
        });

        // 更新自定义输入框的值
        const customInput = document.querySelector(SELECTORS.CUSTOM_SPEED_INPUT);
        if (customInput) {
            customInput.value = speed.toFixed(2);
        }
    }

    /**
     * 应用保存的倍速
     */
    function applySavedSpeed() {
        const savedSpeed = getSavedSpeed();
        if (savedSpeed !== CONFIG.DEFAULT_SPEED) {
            const video = document.querySelector(SELECTORS.VIDEO);
            if (video) {
                video.playbackRate = savedSpeed;
                updateSpeedDisplay(savedSpeed);
                console.log(`已应用保存的倍速: ${savedSpeed}x`);
            }
        }
    }

    /**
     * 限制倍速范围
     * @param {number} speed - 倍速值
     * @returns {number} 限制后的倍速值
     */
    function clampSpeed(speed) {
        return Math.max(CONFIG.MIN_SPEED, Math.min(CONFIG.MAX_SPEED, speed));
    }

    /**
     * 视频监听模块
     */

    /**
     * 启动视频监听
     * 监听视频元素变化,自动应用保存的倍速
     */
    function startVideoMonitor() {
        const observer = new MutationObserver(() => {
            applySavedSpeed();
        });

        const video = document.querySelector(SELECTORS.VIDEO);
        if (video && video.parentElement) {
            observer.observe(video.parentElement, {
                childList: true,
                subtree: true
            });
        }

        return observer;
    }

    /**
     * 倍速输入框组件
     */

    /**
     * 创建自定义倍速输入框
     * @returns {HTMLElement}
     */
    function createSpeedInput() {
        const input = document.createElement('input');
        input.type = 'number';
        input.id = 'custom-speed-input';
        input.min = CONFIG.MIN_SPEED;
        input.max = CONFIG.MAX_SPEED;
        input.step = CONFIG.SPEED_STEP;
        input.value = getSavedSpeed().toFixed(2);
        input.style.cssText = `
        width: 52px;
        padding: 4px 6px;
        border: 1px solid #3a3a3a;
        border-radius: 4px;
        font-size: 14px;
        text-align: center;
        color: #fff;
        background: #212121;
        appearance: textfield;
        -moz-appearance: textfield;
        -webkit-appearance: none;
    `;

        // 悬停效果
        input.onmouseover = () => {
            input.style.background = '#3a3a3a';
        };
        input.onmouseout = () => {
            input.style.background = '#212121';
        };

        // 应用倍速
        const applySpeed = () => {
            let speed = parseFloat(input.value);
            if (isNaN(speed)) {
                speed = CONFIG.DEFAULT_SPEED;
            }
            speed = clampSpeed(speed);
            input.value = speed.toFixed(2);
            setVideoSpeed(speed);
        };

        // 回车应用
        input.onkeypress = (e) => {
            if (e.key === 'Enter') {
                e.stopPropagation();
                applySpeed();
            }
        };

        // 失焦应用
        input.onblur = applySpeed;

        return input;
    }

    /**
     * 创建自定义倍速控制容器
     * @returns {HTMLElement}
     */
    function createCustomSpeedControl() {
        const customItem = document.createElement('li');
        customItem.className = 'bpx-player-ctrl-playbackrate-menu-item custom-speed-item';
        customItem.style.cssText = `
        padding: 8px 10px;
        cursor: default;
        background: transparent;
        margin-top: 0;
        border-top: none;
        display: flex;
        align-items: center;
        justify-content: center;
    `;

        const controlsContainer = document.createElement('div');
        controlsContainer.style.cssText = 'display: flex; flex-direction: column; gap: 6px;';

        const headerRow = document.createElement('div');
        headerRow.style.cssText = 'display: flex; align-items: center; gap: 6px; justify-content: center;';

        const input = createSpeedInput();
        headerRow.appendChild(input);
        controlsContainer.appendChild(headerRow);
        customItem.appendChild(controlsContainer);

        // 阻止点击事件冒泡
        customItem.onclick = (e) => {
            e.stopPropagation();
        };

        return customItem;
    }

    /**
     * 设备识别工具
     */

    /**
     * 判断是否为触控板
     * @param {WheelEvent} event - 滚轮事件
     * @returns {boolean}
     */
    function isTouchpad(event) {
        // deltaMode === 0 (DOM_DELTA_PIXEL) 通常是触控板
        // deltaMode === 1 (DOM_DELTA_LINE) 通常是鼠标滚轮
        return event.deltaMode === 0 && Math.abs(event.deltaY) < WHEEL_CONFIG.TOUCHPAD_DELTA_LIMIT;
    }

    /**
     * 获取滚动方向
     * @param {number} deltaY - 滚动增量
     * @returns {number} 1 表示向下,-1 表示向上
     */
    function getScrollDirection(deltaY) {
        return deltaY > 0 ? 1 : -1;
    }

    /**
     * 滚轮和触控板处理模块
     */

    /**
     * 为倍速按钮添加滚轮事件监听
     */
    function attachWheelHandler() {
        const playbackBtn = document.querySelector(SELECTORS.PLAYBACK_RATE_BTN);
        if (!playbackBtn || playbackBtn.dataset.customWheelBound) {
            return;
        }

        playbackBtn.dataset.customWheelBound = 'true';
        let touchpadDeltaAccum = 0;

        playbackBtn.addEventListener('wheel', (e) => {
            e.preventDefault();
            e.stopPropagation();

            const video = document.querySelector(SELECTORS.VIDEO);
            if (!video) return;

            let speed = video.playbackRate || CONFIG.DEFAULT_SPEED;
            const isTouch = isTouchpad(e);

            if (isTouch) {
                // 触控板:累积阈值 + 细腻步进
                touchpadDeltaAccum += e.deltaY;
                
                if (Math.abs(touchpadDeltaAccum) < WHEEL_CONFIG.TOUCHPAD_THRESHOLD) {
                    return;
                }

                const direction = getScrollDirection(touchpadDeltaAccum);
                touchpadDeltaAccum -= direction * WHEEL_CONFIG.TOUCHPAD_THRESHOLD;
                speed += direction * WHEEL_CONFIG.TOUCHPAD_STEP;
                
                // 触控板四舍五入到 0.02 精度
                speed = Math.round(speed * 50) / 50;
            } else {
                // 鼠标滚轮:直接步进(方向反转:向上增加,向下减少)
                const direction = getScrollDirection(e.deltaY);
                speed -= direction * WHEEL_CONFIG.MOUSE_STEP;  // 使用减法反转方向
                
                // 鼠标滚轮四舍五入到 0.1 精度
                speed = Math.round(speed * 10) / 10;
            }

            speed = clampSpeed(speed);
            setVideoSpeed(speed);
        }, { passive: false });
    }

    /**
     * 菜单增强模块
     */

    /**
     * 增强倍速菜单
     * @returns {boolean} 是否成功增强
     */
    function enhanceSpeedMenu() {
        const menu = document.querySelector(SELECTORS.PLAYBACK_RATE_MENU);
        if (!menu) return false;

        // 检查是否已经添加过自定义控件
        if (menu.querySelector(SELECTORS.CUSTOM_SPEED_ITEM)) {
            return true;
        }

        // 添加自定义倍速控制
        const customControl = createCustomSpeedControl();
        const twoXItem = menu.querySelector('.bpx-player-ctrl-playbackrate-menu-item[data-value="2"]');
        const firstItem = menu.querySelector('.bpx-player-ctrl-playbackrate-menu-item:not(.custom-speed-item)');
        
        if (twoXItem) {
            menu.insertBefore(customControl, twoXItem);
        } else if (firstItem) {
            menu.insertBefore(customControl, firstItem);
        } else {
            menu.appendChild(customControl);
        }

        // 为现有的倍速选项添加点击事件
        const menuItems = menu.querySelectorAll('.bpx-player-ctrl-playbackrate-menu-item:not(.custom-speed-item)');
        menuItems.forEach(item => {
            item.addEventListener('click', () => {
                const speed = parseFloat(item.getAttribute('data-value'));
                setVideoSpeed(speed);
            });
        });

        // 为倍速按钮添加滚轮调节事件
        attachWheelHandler();

        console.log('B站倍速增强已加载');
        return true;
    }

    /**
     * 引导动画样式
     */

    /**
     * 获取引导系统的 CSS 样式
     * @returns {string}
     */
    function getTourStyles() {
        return `
        .tour-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); z-index: 99999; pointer-events: none; transition: opacity 0.3s; }
        .tour-highlight { position: absolute; box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.6); z-index: 99998; border-radius: 4px; pointer-events: none; transition: all 0.3s ease; border: 2px solid #00aeec; }
        .tour-tooltip { position: absolute; background: #212121; color: #fff; padding: 16px; border-radius: 8px; width: 280px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); z-index: 100000; font-size: 14px; line-height: 1.6; border: 1px solid #3a3a3a; transition: all 0.3s ease; pointer-events: none; }
        .tour-tooltip h3 { margin: 0 0 8px 0; color: #00aeec; font-size: 16px; font-weight: bold; }
        .tour-tooltip p { margin: 0 0 16px 0; color: #e0e0e0; }
        .tour-footer { display: flex; justify-content: flex-end; gap: 10px; pointer-events: auto; }
        .tour-btn { padding: 6px 12px; border-radius: 4px; cursor: pointer; border: none; font-size: 12px; transition: background 0.2s; pointer-events: auto; }
        .tour-btn-skip { background: transparent; color: #999; }
        .tour-btn-skip:hover { color: #ccc; }
        .tour-btn-next { background: #00aeec; color: #fff; }
        .tour-btn-next:hover { background: #008bbd; }

        /* 动画样式 */
        .anim-container { display: flex; gap: 20px; margin-bottom: 15px; justify-content: center; }
        .anim-box { display: flex; flex-direction: column; align-items: center; gap: 8px; }
        .anim-label { font-size: 12px; color: #999; }
        
        /* 鼠标图标 */
        .anim-mouse {
            width: 24px; height: 38px;
            border: 2px solid #fff; border-radius: 12px;
            position: relative;
        }
        .anim-scroll {
            width: 4px; height: 6px; background: #00aeec;
            border-radius: 2px; position: absolute;
            left: 50%; transform: translateX(-50%);
            top: 6px;
            animation: scroll-wheel 1.5s infinite;
        }
        @keyframes scroll-wheel {
            0% { top: 6px; opacity: 1; }
            100% { top: 20px; opacity: 0; }
        }

        /* 触控板图标 */
        .anim-touchpad {
            width: 38px; height: 38px;
            border: 2px solid #fff; border-radius: 4px;
            position: relative; overflow: hidden;
        }
        .anim-finger {
            width: 8px; height: 8px; background: #00aeec;
            border-radius: 50%; position: absolute;
            left: 50%; top: 60%;
            transform: translate(-50%, -50%);
            animation: scroll-touch 1.5s infinite;
            box-shadow: 0 0 0 4px rgba(0, 174, 236, 0.3);
        }
        @keyframes scroll-touch {
            0% { top: 70%; opacity: 0; }
            20% { top: 70%; opacity: 1; }
            80% { top: 30%; opacity: 1; }
            100% { top: 30%; opacity: 0; }
        }

        /* 强制显示菜单样式 */
        .tour-force-show {
            display: block !important;
            visibility: visible !important;
            opacity: 1 !important;
        }
    `;
    }

    /**
     * DOM 工具函数
     */


    /**
     * 创建样式元素
     * @param {string} css - CSS 内容
     * @returns {HTMLStyleElement}
     */
    function createStyle(css) {
        const style = document.createElement('style');
        style.textContent = css;
        return style;
    }

    /**
     * 检查页面是否为 B站视频页面
     * @returns {boolean}
     */
    function isBilibiliVideoPage() {
        return location.hostname.includes('bilibili.com') && 
               (location.pathname.includes('/video/') || location.pathname.includes('/bangumi/play/'));
    }

    /**
     * 首次使用引导系统
     */

    class TourGuide {
        constructor() {
            this.steps = [];
            this.currentStep = 0;
            this.overlay = null;
            this.tooltip = null;
            this.highlight = null;
        }

        /**
         * 启动引导
         */
        start() {
            if (isTourShown(CONFIG.TOUR_VERSION)) return;
            
            this.initStyles();
            this.createOverlay();
            this.createTooltip();
            this.defineSteps();
            
            setTimeout(() => this.showStep(0), 1000);
        }

        /**
         * 初始化样式
         */
        initStyles() {
            const style = createStyle(getTourStyles());
            document.head.appendChild(style);
        }

        /**
         * 创建遮罩层
         */
        createOverlay() {
            this.highlight = document.createElement('div');
            this.highlight.className = 'tour-highlight';
            document.body.appendChild(this.highlight);
        }

        /**
         * 创建提示框
         */
        createTooltip() {
            this.tooltip = document.createElement('div');
            this.tooltip.className = 'tour-tooltip';
            document.body.appendChild(this.tooltip);
        }

        /**
         * 定义引导步骤
         */
        defineSteps() {
            this.steps = [
                {
                    element: SELECTORS.PLAYBACK_RATE_BTN,
                    title: '倍速控制增强',
                    content: '👋 欢迎使用倍速增强脚本!<br>这里是倍速控制入口,支持悬停查看菜单。',
                    position: 'top'
                },
                {
                    element: SELECTORS.CUSTOM_SPEED_INPUT,
                    title: '自定义倍速',
                    content: '🔢 在这里直接输入任意倍速 (0.07 - 10.0)。<br>支持 0.01 精度,输入后回车即可应用。',
                    position: 'right',
                    action: () => {
                        const menu = document.querySelector(SELECTORS.PLAYBACK_RATE_MENU);
                        if (menu) {
                            menu.style.display = 'block';
                            menu.style.visibility = 'visible';
                            menu.style.opacity = '1';
                        }
                        const input = document.querySelector(SELECTORS.CUSTOM_SPEED_INPUT);
                        if (input) input.focus();
                    }
                },
                {
                    element: SELECTORS.PLAYBACK_RATE_MENU,
                    title: '滚轮与触控板调节',
                    content: `
                    <div class="anim-container">
                        <div class="anim-box">
                            <div class="anim-mouse"><div class="anim-scroll"></div></div>
                            <span class="anim-label">鼠标滚轮</span>
                        </div>
                        <div class="anim-box">
                            <div class="anim-touchpad"><div class="anim-finger"></div></div>
                            <span class="anim-label">触控板滑动</span>
                        </div>
                    </div>
                    上滑/滚动增加倍速,下滑/滚动减少倍速。<br><br>
                    🖱️ <b>鼠标滚轮:</b>在按钮或菜单上滚动,快速调节 (±0.1)。<br>
                    👆 <b>触控板:</b>在按钮或菜单上上下滑动,细腻微调 (±0.02)。<br>
                    💾 <b>自动记忆:</b>您的倍速设置会自动保存,下次观看自动恢复。
                `,
                    position: 'left',
                    action: () => {
                        const menu = document.querySelector(SELECTORS.PLAYBACK_RATE_MENU);
                        if (menu) {
                            menu.style.display = 'block';
                            menu.style.visibility = 'visible';
                            menu.style.opacity = '1';
                        }
                    },
                    isLast: true
                }
            ];
        }

        /**
         * 显示指定步骤
         * @param {number} index - 步骤索引
         */
        showStep(index) {
            if (index >= this.steps.length) {
                this.end();
                return;
            }

            this.currentStep = index;
            const step = this.steps[index];
            if (step.action) step.action();

            let target = step.element;
            if (typeof target === 'string') target = document.querySelector(target);

            if (!target && !step.isLast) {
                this.showStep(index + 1);
                return;
            }

            // 强制保持菜单显示
            if (step.element === SELECTORS.PLAYBACK_RATE_MENU || step.element === SELECTORS.CUSTOM_SPEED_INPUT) {
                const menu = document.querySelector(SELECTORS.PLAYBACK_RATE_MENU);
                if (menu) {
                    menu.style.display = 'block !important';
                    menu.style.visibility = 'visible !important';
                    menu.style.opacity = '1 !important';
                    menu.classList.add('tour-force-show');
                }
            }

            if (step.position === 'center') {
                this.highlight.style.display = 'none';
                this.tooltip.style.top = '50%';
                this.tooltip.style.left = '50%';
                this.tooltip.style.transform = 'translate(-50%, -50%)';
            } else {
                const rect = target.getBoundingClientRect();
                const scrollY = window.scrollY;
                const scrollX = window.scrollX;

                this.highlight.style.display = 'block';
                this.highlight.style.width = `${rect.width}px`;
                this.highlight.style.height = `${rect.height}px`;
                this.highlight.style.top = `${rect.top + scrollY}px`;
                this.highlight.style.left = `${rect.left + scrollX}px`;

                this.positionTooltip(step.position, rect, scrollY, scrollX);
            }

            this.renderTooltip(step);
        }

        /**
         * 定位提示框
         */
        positionTooltip(position, rect, scrollY, scrollX) {
            if (position === 'top') {
                this.tooltip.style.top = `${rect.top + scrollY - 160}px`;
                this.tooltip.style.left = `${rect.left + scrollX - 100}px`;
                this.tooltip.style.transform = 'none';
            } else if (position === 'right') {
                this.tooltip.style.top = `${rect.top + scrollY}px`;
                this.tooltip.style.left = `${rect.right + scrollX + 20}px`;
                this.tooltip.style.transform = 'none';
            } else if (position === 'left') {
                this.tooltip.style.top = `${rect.top + scrollY}px`;
                this.tooltip.style.left = `${rect.left + scrollX - 320}px`;
                this.tooltip.style.transform = 'none';
            }
        }

        /**
         * 渲染提示框内容
         */
        renderTooltip(step) {
            this.tooltip.innerHTML = `
            <h3>${step.title}</h3>
            <p>${step.content}</p>
            <div class="tour-footer">
                <button class="tour-btn tour-btn-skip" id="tour-skip">跳过</button>
                <button class="tour-btn tour-btn-next" id="tour-next">
                    ${step.isLast ? '完成' : '下一步'}
                </button>
            </div>
        `;

            document.getElementById('tour-next').onclick = () => this.showStep(this.currentStep + 1);
            document.getElementById('tour-skip').onclick = () => this.end();
        }

        /**
         * 结束引导
         */
        end() {
            if (this.highlight) this.highlight.remove();
            if (this.tooltip) this.tooltip.remove();
            markTourShown(CONFIG.TOUR_VERSION);
            
            // 清理强制显示的样式和类名
            const menu = document.querySelector(SELECTORS.PLAYBACK_RATE_MENU);
            if (menu) {
                menu.classList.remove('tour-force-show');
                menu.style.display = '';
                menu.style.visibility = '';
                menu.style.opacity = '';
            }
            
            // 确保菜单关闭
            const btn = document.querySelector(SELECTORS.PLAYBACK_RATE_BTN);
            if (btn) btn.dispatchEvent(new MouseEvent('mouseout'));
        }
    }

    /**
     * B站倍速播放增强 - 主入口
     * @author timerring
     * @version 1.0.0
     */


    /**
     * 初始化脚本
     */
    function init() {
        let retries = 0;
        
        const checkAndInit = setInterval(() => {
            retries++;
            
            // 尝试增强菜单
            if (enhanceSpeedMenu()) {
                // 应用保存的倍速
                setTimeout(applySavedSpeed, 500);
                
                // 启动引导(如果是首次)
                setTimeout(() => new TourGuide().start(), 1500);
                
                clearInterval(checkAndInit);
                
                // 启动视频监听
                startVideoMonitor();
            }
            
            // 超过最大重试次数则停止
            if (retries >= CONFIG.MAX_RETRIES) {
                clearInterval(checkAndInit);
                console.warn('B站倍速增强加载失败:未找到播放器控制元素');
            }
        }, CONFIG.CHECK_INTERVAL);
    }

    /**
     * 主函数
     */
    function main() {
        // 检查是否在B站视频页面
        if (isBilibiliVideoPage()) {
            console.log('开始加载 B站倍速增强...');
            init();
        } else {
            console.warn('请在 B站视频页面运行此脚本');
        }
    }

    // 启动脚本
    main();

})();

})();