Greasy Fork

Greasy Fork is available in English.

通用视频倍速播放控制

为各类网站视频提供通用的倍速播放控制功能

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            通用视频倍速播放控制
// @namespace       http://tampermonkey.net/
// @version         3.5
// @description     为各类网站视频提供通用的倍速播放控制功能
// @author          ming2k
// @match           https://www.bilibili.com/video/*
// @match           https://www.youtube.com/*
// @match           https://www.iyf.tv/play/*
// @grant           none
// @run-at          document-idle
// @license         MIT
// ==/UserScript==

(function() {
    'use strict';

    // 配置信息 - 针对慢加载视频优化
    const config = {
        // 默认初始播放速度
        defaultSpeed: 1.0,
        // 变速步长
        speedStep: 0.1,
        // 最小速度
        minSpeed: 0.1,
        // 最大速度
        maxSpeed: 4.0,
        // 提示显示时间(毫秒)
        notificationDuration: 1500,
        // 快捷键设置(与MPV兼容)
        hotkeys: {
            increaseSpeed: ']', // 增加速度
            decreaseSpeed: '[', // 减少速度
            resetSpeed: '{',    // 重置速度
            doubleSpeed: '}'    // 2倍速
        },
        // 启用详细日志
        debug: true,
        // 尝试间隔(毫秒) - 增加间隔以处理慢速加载
        retryInterval: 2000,
        // 最大尝试次数 - 大幅增加以处理慢速加载
        maxRetries: 40,
        // 延长初始等待时间(毫秒)
        initialWaitTime: 5000,
        // 长时间监控 - 持续时间(毫秒)
        longTermMonitoring: 300000, // 5分钟
        // 长时间监控 - 检查间隔(毫秒)
        longTermCheckInterval: 8000
    };

    // 状态变量
    let videoElement = null;
    let retryCount = 0;
    let notificationTimer = null;
    let notification = null;
    // 统一的存储键名
    const storageKey = 'video_playback_speed';
    let lastStoredSpeed = parseFloat(localStorage.getItem(storageKey)) || config.defaultSpeed;
    let isInitialized = false;
    let videoObserver = null;
    let speedApplied = false;
    let currentVideoUrl = location.href; // 记录当前视频URL
    let longTermMonitoringTimer = null;
    let videoFoundTime = null;
    let pendingSpeedPromise = null;
    let waitingForVideo = false;

    // 日志函数
    function log(message, force = false) {
        if (config.debug || force) {
            console.log(`[视频倍速控制] ${message}`);
        }
    }

    // 添加样式
    function addStyles() {
        const styles = `
            .speed-notification {
                position: fixed;
                top: 70px;
                left: 50%;
                transform: translateX(-50%);
                background-color: rgba(0, 0, 0, 0.8);
                color: white;
                padding: 10px 20px;
                border-radius: 4px;
                z-index: 9999999;
                font-size: 16px;
                font-weight: bold;
                opacity: 0;
                transition: opacity 0.3s ease;
                box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16);
                text-align: center;
                pointer-events: none;
            }
            .speed-notification.show {
                opacity: 1;
            }
        `;

        const styleElement = document.createElement('style');
        styleElement.textContent = styles;
        document.head.appendChild(styleElement);
        log('样式已添加');
    }

    // 创建通知元素
    function createNotification() {
        notification = document.createElement('div');
        notification.className = 'speed-notification';
        document.body.appendChild(notification);
        log('通知元素已创建');
    }

    // 显示通知
    function showNotification(message) {
        if (!notification) {
            createNotification();
        }

        notification.textContent = message;
        notification.classList.add('show');

        // 清除之前的定时器
        if (notificationTimer) {
            clearTimeout(notificationTimer);
        }

        // 设置新定时器隐藏通知
        notificationTimer = setTimeout(() => {
            notification.classList.remove('show');
        }, config.notificationDuration);
    }

    // 检查视频是否可用函数 - 更严格的可用性检查
    function isVideoPlayable(video) {
        if (!video) return false;

        try {
            // 检查视频是否有效
            if (video.readyState === 0) {
                log('视频元素存在但尚未初始化 (readyState=0)');
                return false;
            }

            // 检查视频是否有效源或内容
            const hasSource = video.src ||
                              video.querySelector('source') ||
                              video.readyState > 0;

            // 检查视频是否在文档中
            const isInDocument = document.contains(video);

            // 检查视频是否有尺寸
            const hasDimensions = (video.videoWidth > 0 && video.videoHeight > 0) ||
                                 (video.offsetWidth > 0 && video.offsetHeight > 0);

            // 基本检查
            if (!hasSource) {
                log('视频元素没有有效源');
                return false;
            }

            if (!isInDocument) {
                log('视频元素不在文档中');
                return false;
            }

            // 额外检查 - 可选
            if (!hasDimensions && video.readyState <= 1) {
                log('视频元素没有尺寸且readyState低');
                // 注意:某些网站可能隐藏了视频尺寸,所以这不是硬性条件
                // 如果视频的readyState > 1,我们认为它是可用的,即使没有尺寸
                if (video.readyState <= 1) {
                    return false;
                }
            }

            return true;
        } catch (e) {
            log(`检查视频可用性时出错: ${e.message}`);
            return false;
        }
    }

    // 增强版查找视频元素
    function findVideoElement() {
        // 检查现有的视频元素是否仍然有效
        if (videoElement && isVideoPlayable(videoElement)) {
            return videoElement;
        }

        log('开始查找视频元素...');

        // 尝试多种选择器
        const selectors = [
            'video',                         // 标准视频标签
            '.video-container video',        // 从HTML结构分析得出的可能选择器
            '.video-box video',              // 另一个可能的选择器
            'vg-player video',               // Videogular
            'aa-videoplayer video',          // 自定义播放器
            '*[id*="player"] video',         // ID中包含player的元素内的视频
            '*[class*="player"] video',      // class中包含player的元素内的视频
            // 增加更多选择器用于慢加载网站
            '*[id*="video"] video',          // ID中包含video的元素
            '*[class*="video"] video',       // class中包含video的元素
            'iframe[src*="player"] video',   // iframe中的播放器视频
            'video[preload]',                // 预加载的视频
            '.container video',              // 常见容器内的视频
            'main video',                    // 主内容区域的视频
            'article video',                 // 文章内的视频
            'div > video',                   // div直接子元素的视频
            'video[src]',                    // 有src属性的视频
            'video > source',                // 有source子元素的视频
        ];

        let foundVideos = [];

        // 收集所有视频元素
        for (const selector of selectors) {
            const elements = document.querySelectorAll(selector);
            if (elements && elements.length > 0) {
                for (const video of elements) {
                    // 获取实际视频元素(可能是source的父元素)
                    const actualVideo = video.tagName.toLowerCase() === 'video' ? video : video.closest('video');
                    if (actualVideo && !foundVideos.includes(actualVideo) && isVideoPlayable(actualVideo)) {
                        foundVideos.push(actualVideo);
                        log(`找到可能的视频元素: ${selector}`);
                    }
                }
            }
        }

        // 如果找到多个视频,尝试找出主视频
        if (foundVideos.length > 1) {
            log(`找到${foundVideos.length}个视频元素,尝试确定主视频`);

            // 按照可能性排序:
            // 1. 可见的
            // 2. 更大的
            // 3. 位于视口中的
            foundVideos.sort((a, b) => {
                const aVisible = isElementVisible(a);
                const bVisible = isElementVisible(b);

                // 可见性优先
                if (aVisible && !bVisible) return -1;
                if (!aVisible && bVisible) return 1;

                // 然后是尺寸
                const aSize = (a.offsetWidth || 0) * (a.offsetHeight || 0);
                const bSize = (b.offsetWidth || 0) * (b.offsetHeight || 0);
                if (aSize > bSize) return -1;
                if (aSize < bSize) return 1;

                // 最后是视口中的位置
                const aInViewport = isInViewport(a);
                const bInViewport = isInViewport(b);
                if (aInViewport && !bInViewport) return -1;
                if (!aInViewport && bInViewport) return 1;

                return 0;
            });

            log(`选择最可能的主视频元素`);
            return foundVideos[0];
        } else if (foundVideos.length === 1) {
            return foundVideos[0];
        }

        // 尝试搜索所有iframe
        const iframes = document.querySelectorAll('iframe');
        for (let iframe of iframes) {
            try {
                if (iframe.contentDocument) {
                    const iframeVideo = iframe.contentDocument.querySelector('video');
                    if (iframeVideo && isVideoPlayable(iframeVideo)) {
                        log('在iframe中找到视频');
                        return iframeVideo;
                    }
                }
            } catch (e) {
                // 跨域iframe无法访问内容
                log('无法访问iframe内容: ' + e.message);
            }
        }

        // 深度搜索
        function deepSearch(element, depth = 0) {
            if (!element || depth > 15) return null; // 增加搜索深度

            if (element.tagName && element.tagName.toLowerCase() === 'video' && isVideoPlayable(element)) {
                return element;
            }

            // 搜索shadowDOM
            if (element.shadowRoot) {
                const shadowVideo = element.shadowRoot.querySelector('video');
                if (shadowVideo && isVideoPlayable(shadowVideo)) return shadowVideo;

                // 深度搜索shadowDOM
                const allShadowElements = element.shadowRoot.querySelectorAll('*');
                for (const shadowEl of allShadowElements) {
                    const found = deepSearch(shadowEl, depth + 1);
                    if (found) return found;
                }
            }

            for (let i = 0; i < element.children.length; i++) {
                const found = deepSearch(element.children[i], depth + 1);
                if (found) return found;
            }

            return null;
        }

        const deepSearchResult = deepSearch(document.body);
        if (deepSearchResult) {
            log('通过深度搜索找到视频');
            return deepSearchResult;
        }

        // 最后尝试通过动态加载的iframe查找
        // 某些网站可能延迟加载包含视频的iframe
        try {
            // 查找所有iframe,包括新加载的
            const allIframes = document.querySelectorAll('iframe');
            for (const iframe of allIframes) {
                try {
                    // 尝试判断iframe是否是视频播放器
                    const isVideoFrame = iframe.src.includes('player') ||
                                       iframe.src.includes('video') ||
                                       iframe.src.includes('embed') ||
                                       iframe.id.includes('player') ||
                                       iframe.className.includes('player');

                    if (isVideoFrame) {
                        log('找到可能包含视频的iframe,但无法直接访问内容');
                        // 不能直接返回iframe,但可以记录它存在
                        // 一些网站可能需要特殊处理
                    }
                } catch (e) {
                    // 忽略跨域错误
                }
            }
        } catch (e) {
            log(`检查iframe时出错: ${e.message}`);
        }

        log('未找到视频元素');
        return null;
    }

    // 检查元素是否可见
    function isElementVisible(element) {
        if (!element) return false;

        const style = window.getComputedStyle(element);
        return style.display !== 'none' &&
               style.visibility !== 'hidden' &&
               style.opacity !== '0' &&
               element.offsetWidth > 0 &&
               element.offsetHeight > 0;
    }

    // 检查元素是否在视口中
    function isInViewport(element) {
        if (!element) return false;

        const rect = element.getBoundingClientRect();
        return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
            rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        );
    }

    // 获取最后使用的速度
    function getLastSpeed() {
        try {
            const savedSpeed = parseFloat(localStorage.getItem(storageKey));
            if (!isNaN(savedSpeed) && savedSpeed > 0) {
                log(`从localStorage加载速度: ${savedSpeed}x`);
                return savedSpeed;
            }
        } catch (e) {
            log(`无法读取保存的速度: ${e.message}`);
        }
        log(`未找到保存的速度,使用默认速度 ${config.defaultSpeed}x`);
        return config.defaultSpeed;
    }

    // 保存最后使用的速度
    function saveLastSpeed(speed) {
        lastStoredSpeed = speed;
        try {
            localStorage.setItem(storageKey, speed);
            log(`已保存播放速度 ${speed}x 到localStorage`);
        } catch (e) {
            log(`无法保存速度: ${e.message}`);
        }
    }

    // 尝试多种方法设置播放速度 - 添加承诺等待功能
    function setPlaybackSpeed(speed, showNotifFlag = true) {
        // 确保速度在范围内
        speed = Math.max(config.minSpeed, Math.min(config.maxSpeed, speed));
        speed = parseFloat(speed.toFixed(1)); // 保留一位小数

        // 如果已经有等待设置的承诺,则取消它
        if (pendingSpeedPromise) {
            clearTimeout(pendingSpeedPromise);
            pendingSpeedPromise = null;
        }

        // 查找视频元素
        const video = findVideoElement();
        if (video) {
            try {
                video.playbackRate = speed;
                log(`使用标准方式设置速度: ${speed}x`);
                if (showNotifFlag) {
                    showNotification(`播放速度: ${speed}x`);
                }
                saveLastSpeed(speed);
                return true;
            } catch (e) {
                log(`标准方式设置速度失败: ${e.message}`);
            }
        } else {
            // 如果没找到视频但脚本已运行一段时间,设置一个延迟尝试
            log(`未找到视频元素,设置延迟尝试设置速度: ${speed}x`);
            waitingForVideo = true;
            pendingSpeedPromise = setTimeout(() => {
                log(`执行延迟设置速度: ${speed}x`);
                setPlaybackSpeed(speed, showNotifFlag);
                pendingSpeedPromise = null;
            }, 5000);

            // 返回false表示当前未成功设置
            return false;
        }

        // 尝试使用JavaScript注入
        try {
            const script = document.createElement('script');
            script.textContent = `
                (function() {
                    try {
                        const videos = document.querySelectorAll('video');
                        let success = false;
                        if (videos.length > 0) {
                            videos.forEach(v => {
                                try {
                                    // 检查视频是否有效
                                    if (v && !v.ended && v.readyState > 0) {
                                        v.playbackRate = ${speed};
                                        success = true;
                                    }
                                } catch(e) {
                                    console.error('无法设置视频速度:', e);
                                }
                            });
                        }

                        // 尝试从全局变量中找到播放器
                        const playerVars = ['player', 'videoPlayer', 'vgPlayer', 'mediaPlayer', 'ytPlayer', 'videojs'];
                        for (const varName of playerVars) {
                            if (window[varName]) {
                                // 尝试不同的方法设置速度
                                try {
                                    if (typeof window[varName].setPlaybackRate === 'function') {
                                        window[varName].setPlaybackRate(${speed});
                                        success = true;
                                    } else if (window[varName].player && typeof window[varName].player.setPlaybackRate === 'function') {
                                        window[varName].player.setPlaybackRate(${speed});
                                        success = true;
                                    } else if (window[varName].getMedia && window[varName].getMedia()) {
                                        window[varName].getMedia().playbackRate = ${speed};
                                        success = true;
                                    } else if (window[varName].video && window[varName].video.playbackRate !== undefined) {
                                        window[varName].video.playbackRate = ${speed};
                                        success = true;
                                    } else if (window[varName].el && window[varName].el.playbackRate !== undefined) {
                                        window[varName].el.playbackRate = ${speed};
                                        success = true;
                                    }
                                } catch(e) {
                                    console.error('尝试通过全局变量设置速度失败:', e);
                                }
                            }
                        }

                        // 尝试在Angular组件中查找
                        if (window.ng && window.ng.probe) {
                            const playerElements = document.querySelectorAll('vg-player, [class*="player"]');
                            playerElements.forEach(el => {
                                try {
                                    const componentInstance = window.ng.probe(el);
                                    if (componentInstance && componentInstance.componentInstance) {
                                        const instance = componentInstance.componentInstance;
                                        if (instance.media && instance.media.playbackRate !== undefined) {
                                            instance.media.playbackRate = ${speed};
                                            success = true;
                                        }
                                    }
                                } catch(e) {}
                            });
                        }

                        // 尝试特定网站API
                        // YouTube
                        if (typeof document.querySelector('.html5-video-player') !== 'undefined') {
                            const ytPlayer = document.querySelector('.html5-video-player');
                            if (ytPlayer && ytPlayer.getPlayer) {
                                try {
                                    ytPlayer.getPlayer().setPlaybackRate(${speed});
                                    success = true;
                                } catch(e) {}
                            }
                        }

                        // HTML5 video API - 再次尝试
                        document.querySelectorAll('video').forEach(v => {
                            if (v && v.readyState > 0) {
                                try {
                                    v.playbackRate = ${speed};
                                    success = true;
                                } catch(e) {}
                            }
                        });

                        window._speedChangeSuccess = success;
                    } catch(e) {
                        console.error('播放速度注入脚本错误:', e);
                        window._speedChangeSuccess = false;
                    }
                })();
            `;
            document.body.appendChild(script);
            document.body.removeChild(script);

            // 检查是否成功
            if (window._speedChangeSuccess) {
                log(`通过脚本注入设置速度: ${speed}x`);
                if (showNotifFlag) {
                    showNotification(`播放速度: ${speed}x`);
                }
                saveLastSpeed(speed);
                return true;
            }
        } catch (e) {
            log(`脚本注入设置失败: ${e.message}`);
        }

        if (showNotifFlag) {
            log('无法设置播放速度,将在视频加载后重试', true);
            showNotification(`已保存速度设置,等待视频加载...`);

            // 保存要设置的速度,以便在视频加载后应用
            saveLastSpeed(speed);
        }
        return false;
    }

    // 修改播放速度
    function changeSpeed(delta) {
        const video = findVideoElement();
        const currentSpeed = video ? video.playbackRate : lastStoredSpeed;
        const newSpeed = Math.round((currentSpeed + delta) * 10) / 10; // 保留一位小数
        if (setPlaybackSpeed(newSpeed)) {
            lastStoredSpeed = newSpeed; // 确保当前调整的速度被记录为"最后一次速度"
        } else {
            // 如果设置失败但视频尚未加载,仍保存速度以便之后应用
            lastStoredSpeed = newSpeed;
            saveLastSpeed(newSpeed);
        }
    }

    // 播放速度控制相关函数
    function resetSpeed() {
        setPlaybackSpeed(config.defaultSpeed);
    }

    function doubleSpeed() {
        setPlaybackSpeed(2.0);
    }

    // 改进版:只处理特定按键的冲突而不是禁用所有键盘事件
    function handleConflictingKeyBindings() {
        // 创建包含我们需要拦截的按键数组
        const ourHotkeys = Object.values(config.hotkeys);
        log(`我们的热键: ${ourHotkeys.join(', ')}`);

        // 替代注入脚本,只阻止特定按键的事件
        const script = document.createElement('script');
        script.textContent = `
            (function() {
                try {
                    // 获取我们需要处理的快捷键
                    const keysToCatch = ${JSON.stringify(ourHotkeys)};

                    // 创建更精确的事件监听器,在捕获阶段先拦截
                    document.addEventListener('keydown', function(event) {
                        // 只拦截我们关心的按键,且不在输入框中
                        if (keysToCatch.includes(event.key) &&
                            event.target.tagName !== 'INPUT' &&
                            event.target.tagName !== 'TEXTAREA') {
                            // 标记这个事件已被我们的脚本处理
                            event._handledBySpeedController = true;
                        }
                    }, true);

                    // 如果网站使用特定库,修改它们的行为而不是完全禁用

                    // 处理keyboardJS库
                    if (window.keyboardJS && typeof window.keyboardJS.bind === 'function') {
                        const originalBind = window.keyboardJS.bind;
                        window.keyboardJS.bind = function(key, pressHandler, releaseHandler) {
                            // 如果绑定的是我们使用的键,则包装其处理程序
                            if (keysToCatch.includes(key)) {
                                const wrappedPressHandler = function(e) {
                                    if (e._handledBySpeedController) return;
                                    if (pressHandler) pressHandler(e);
                                };
                                const wrappedReleaseHandler = function(e) {
                                    if (e._handledBySpeedController) return;
                                    if (releaseHandler) releaseHandler(e);
                                };
                                return originalBind.call(this, key, wrappedPressHandler, wrappedReleaseHandler);
                            }
                            // 其他键正常绑定
                            return originalBind.apply(this, arguments);
                        };
                        console.log('已修改keyboardJS.bind以处理冲突键');
                    }

                    // 处理Mousetrap库
                    if (window.Mousetrap && typeof window.Mousetrap.bind === 'function') {
                        const originalBind = window.Mousetrap.prototype.bind;
                        window.Mousetrap.prototype.bind = function(keys, callback, action) {
                            // 检查单个键或键数组
                            const keysArray = Array.isArray(keys) ? keys : [keys];
                            const hasConflict = keysArray.some(k => keysToCatch.includes(k));

                            if (hasConflict) {
                                // 包装回调函数
                                const wrappedCallback = function(e, combo) {
                                    if (e._handledBySpeedController) return;
                                    if (callback) return callback(e, combo);
                                };
                                return originalBind.call(this, keys, wrappedCallback, action);
                            }
                            // 其他键正常绑定
                            return originalBind.apply(this, arguments);
                        };
                        console.log('已修改Mousetrap.bind以处理冲突键');
                    }

                    console.log('成功设置冲突按键处理');
                } catch(e) {
                    console.error('设置按键冲突处理失败:', e);
                }
            })();
        `;
        document.body.appendChild(script);
        document.body.removeChild(script);

        log('已设置按键冲突处理');
    }

    // 键盘事件处理,使用捕获阶段以确保我们的处理器先运行
    function handleKeyPress(event) {
        // 忽略在输入框中的按键事件
        if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
            return;
        }

        const key = event.key;

        // 检查是否匹配我们的快捷键
        let handled = false;
        switch (key) {
            case config.hotkeys.increaseSpeed:
                changeSpeed(config.speedStep);
                handled = true;
                break;
            case config.hotkeys.decreaseSpeed:
                changeSpeed(-config.speedStep);
                handled = true;
                break;
            case config.hotkeys.resetSpeed:
                resetSpeed();
                handled = true;
                break;
            case config.hotkeys.doubleSpeed:
                doubleSpeed();
                handled = true;
                break;
        }

        if (handled) {
            // 如果处理了快捷键,阻止事件冒泡和默认行为
            event.stopPropagation();
            event.preventDefault();
        }
    }

    // 处理URL变化的函数 - 增强检测
    function handleUrlChange() {
        const newUrl = location.href;
        if (newUrl !== currentVideoUrl) {
            log(`检测到URL变化,从 ${currentVideoUrl} 到 ${newUrl}`);
            currentVideoUrl = newUrl;

            // URL变化时重置视频元素和状态
            videoElement = null;
            speedApplied = false;

            // 重置视频记录时间
            videoFoundTime = null;

            // 重新查找视频元素,使用渐进式延迟
            const delays = [500, 1000, 2000, 3000, 5000, 8000, 13000];

            delays.forEach((delay, index) => {
                setTimeout(() => {
                    if (!videoElement) {
                        log(`URL变更后第${index + 1}次查找视频 (延迟${delay}ms)`);
                        videoElement = findVideoElement();
                        if (videoElement) {
                            log('URL变化后找到视频元素');
                            applyInitialSpeed();
                        }
                    }
                }, delay);
            });
        }
    }

    // 设置MutationObserver监视DOM变化
    function setupVideoObserver() {
        if (videoObserver) {
            videoObserver.disconnect();
        }

        videoObserver = new MutationObserver((mutations) => {
            // 如果还没找到视频元素,继续尝试
            if (!videoElement) {
                videoElement = findVideoElement();

                if (videoElement) {
                    log('MutationObserver找到视频元素');
                    speedApplied = false;
                    applyInitialSpeed();
                }
            }

            // 检查是否有新的视频加载(例如切换到新一集)
            for (const mutation of mutations) {
                if (mutation.type === 'childList') {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeName === 'VIDEO' ||
                            (node.nodeType === Node.ELEMENT_NODE && node.querySelector('video'))) {
                            const newVideo = node.nodeName === 'VIDEO' ? node : node.querySelector('video');
                            if (newVideo && newVideo !== videoElement && isVideoPlayable(newVideo)) {
                                log('检测到新的视频元素');
                                videoElement = newVideo;
                                speedApplied = false;
                                applyInitialSpeed();
                            }
                        }
                    }
                }
            }

            // 检查视频src变化,可能是同一个视频元素载入新内容
            if (videoElement && !speedApplied) {
                applyInitialSpeed();
            }
        });

        videoObserver.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['src']
        });

        log('已设置视频元素观察器');
    }

    // 改进版应用初始播放速度 - 减少自动干预但改进对慢加载视频的处理
    function applyInitialSpeed() {
        // 如果已经应用过速度,不重复应用
        if (speedApplied) {
            return;
        }

        if (videoElement) {
            // 记录找到视频的时间
            if (!videoFoundTime) {
                videoFoundTime = Date.now();
            }

            const speed = getLastSpeed();
            log(`准备应用上次使用的速度: ${speed}x`);

            // 首次应用速度,增加延迟等待视频完全加载
            setTimeout(() => {
                try {
                    // 检查视频readyState是否足够应用速度
                    if (videoElement.readyState >= 2) {
                        videoElement.playbackRate = speed;
                        log(`成功应用播放速度: ${speed}x (readyState=${videoElement.readyState})`);
                        showNotification(`应用播放速度: ${speed}x`);
                        speedApplied = true;

                        // 添加事件监听,但只在播放开始时检查一次,不持续监听
                        videoElement.addEventListener('play', function() {
                            // 只在播放开始时检查一次
                            if (videoElement.playbackRate !== speed) {
                                log(`检测到播放开始时速度不正确,重新应用: ${speed}x`);
                                videoElement.playbackRate = speed;
                            }
                        }, { once: true });

                        // 监听loadeddata事件确保在数据加载后应用速度
                        videoElement.addEventListener('loadeddata', function() {
                            if (videoElement.playbackRate !== speed) {
                                log(`检测到视频数据加载后速度不正确,重新应用: ${speed}x`);
                                videoElement.playbackRate = speed;
                            }
                        }, { once: true });
                    } else {
                        // 视频尚未准备好,设置重试
                        log(`视频元素readyState不足 (${videoElement.readyState}),设置重试`);

                        // 使用条件触发的事件监听器
                        const eventTypes = ['loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough'];

                        // 为每个事件类型添加一次性监听器
                        eventTypes.forEach(eventType => {
                            videoElement.addEventListener(eventType, function() {
                                if (!speedApplied && videoElement.readyState >= 2) {
                                    log(`在 ${eventType} 事件后应用速度 ${speed}x`);
                                    try {
                                        videoElement.playbackRate = speed;
                                        showNotification(`应用播放速度: ${speed}x`);
                                        speedApplied = true;
                                    } catch (e) {
                                        log(`在 ${eventType} 事件后设置速度失败: ${e.message}`);
                                    }
                                }
                            }, { once: true });
                        });

                        // 添加备用延迟尝试,以防事件未触发
                        setTimeout(() => {
                            if (!speedApplied) {
                                log('使用备用延迟尝试应用速度');
                                try {
                                    videoElement.playbackRate = speed;
                                    log(`延迟备用设置成功 (readyState=${videoElement.readyState})`);
                                    showNotification(`应用播放速度: ${speed}x`);
                                    speedApplied = true;
                                } catch (e) {
                                    log(`延迟备用设置失败: ${e.message}`);
                                }
                            }
                        }, 4000);
                    }
                } catch (e) {
                    log(`应用播放速度失败: ${e.message}`);

                    // 设置递增延迟的备用尝试
                    const retryDelays = [2000, 4000, 8000];

                    retryDelays.forEach((delay, index) => {
                        setTimeout(() => {
                            if (!speedApplied) {
                                try {
                                    log(`第${index + 1}次重试应用速度 ${speed}x`);
                                    videoElement.playbackRate = speed;
                                    log(`重试成功`);
                                    showNotification(`应用播放速度: ${speed}x`);
                                    speedApplied = true;
                                } catch (err) {
                                    log(`第${index + 1}次重试失败: ${err.message}`);
                                }
                            }
                        }, delay);
                    });
                }
            }, 1000); // 给视频更多时间加载
        } else if (waitingForVideo) {
            // 如果在等待视频但没有找到,不执行额外操作
            log('等待视频元素出现...');
        } else {
            // 启动长期监控以处理非常慢的视频加载
            startLongTermMonitoring();
        }
    }

    // 启动长期监控以处理特别慢的视频加载
    function startLongTermMonitoring() {
        if (longTermMonitoringTimer) {
            clearInterval(longTermMonitoringTimer);
        }

        log('启动长期监控以等待视频加载');
        let monitoringStartTime = Date.now();

        longTermMonitoringTimer = setInterval(() => {
            const elapsedTime = Date.now() - monitoringStartTime;

            // 如果监控时间过长,停止监控
            if (elapsedTime > config.longTermMonitoring) {
                log('长期监控超时,停止监控');
                clearInterval(longTermMonitoringTimer);
                return;
            }

            // 如果已经找到视频并应用了速度,停止监控
            if (videoElement && speedApplied) {
                log('视频已找到并应用了速度,停止长期监控');
                clearInterval(longTermMonitoringTimer);
                return;
            }

            // 如果找到了视频但还没应用速度
            if (videoElement && !speedApplied) {
                log('发现视频尚未应用速度,尝试应用');
                applyInitialSpeed();
                return;
            }

            // 尝试再次查找视频
            log(`长期监控:第${Math.floor(elapsedTime / config.longTermCheckInterval)}次尝试查找视频`);
            videoElement = findVideoElement();
            if (videoElement) {
                log('长期监控找到视频元素');
                applyInitialSpeed();
            }
        }, config.longTermCheckInterval);
    }

    // 主初始化函数
    function initialize() {
        log('初始化中...');

        // 加载保存的速度
        lastStoredSpeed = getLastSpeed();
        log(`初始化时加载速度: ${lastStoredSpeed}x`);

        // 添加样式
        addStyles();

        // 创建通知元素
        createNotification();

        // 处理冲突按键而不是禁用所有键盘事件
        handleConflictingKeyBindings();

        // 监听键盘事件 - 使用捕获阶段以确保我们的处理器先运行
        document.addEventListener('keydown', handleKeyPress, true);

        // 监听URL变化
        if (window.history && window.history.pushState) {
            // 监听popstate事件
            window.addEventListener('popstate', handleUrlChange);

            // 重写history方法以捕获SPA页面变化
            const originalPushState = window.history.pushState;
            window.history.pushState = function() {
                originalPushState.apply(this, arguments);
                handleUrlChange();
            };

            const originalReplaceState = window.history.replaceState;
            window.history.replaceState = function() {
                originalReplaceState.apply(this, arguments);
                handleUrlChange();
            };
        }

        // 定期检查URL变化(用于不触发history事件的变化)
        setInterval(handleUrlChange, 4000);

        // 设置视频观察器
        setupVideoObserver();

        // 延迟查找视频以处理慢加载
        log(`等待${config.initialWaitTime / 1000}秒后开始查找视频...`);
        setTimeout(() => {
            // 立即尝试查找视频
            videoElement = findVideoElement();
            if (videoElement) {
                log('初始化时找到视频元素');
                applyInitialSpeed();
                return;
            }

            // 如果没有立即找到,定期尝试
            log('未立即找到视频,启动定期查找');
            let findVideoInterval = setInterval(() => {
                retryCount++;

                videoElement = findVideoElement();
                if (videoElement) {
                    log(`在第${retryCount}次尝试时找到视频元素`);
                    clearInterval(findVideoInterval);
                    applyInitialSpeed();
                    return;
                }

                if (retryCount >= config.maxRetries) {
                    log('达到最大尝试次数,仍未找到视频元素');
                    clearInterval(findVideoInterval);

                    // 最后尝试一次并启动长期监控
                    setTimeout(() => {
                        videoElement = findVideoElement();
                        if (videoElement) {
                            log('最后一次常规尝试找到视频元素');
                            applyInitialSpeed();
                        } else {
                            log('常规查找未找到视频,切换到长期监控');
                            startLongTermMonitoring();
                        }
                    }, 10000);
                }
            }, config.retryInterval);
        }, config.initialWaitTime);

        // 显示通知脚本已加载
        showNotification('视频倍速控制已加载 [慢视频优化版]');
    }

    // 等待页面加载完成并初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => setTimeout(initialize, 1000));
    } else {
        setTimeout(initialize, 1000); // 给页面更多时间加载JS
    }
})();