Greasy Fork

Greasy Fork is available in English.

华医网视频播放脚本 Pro

该油猴脚本用于华医网的视频继续播放,✅智能切换CC播放器(支持倍速)✅自动播放下一视频✅屏蔽弹窗✅静音播放✅用户行为模拟✅圆球浮窗✅防检测倍速播放(仅限CC播放器,最快8.0x)✅智能跳转逻辑✅自动处理签到弹窗

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         华医网视频播放脚本 Pro
// @namespace    dennischancs
// @version      1.3
// @description  该油猴脚本用于华医网的视频继续播放,✅智能切换CC播放器(支持倍速)✅自动播放下一视频✅屏蔽弹窗✅静音播放✅用户行为模拟✅圆球浮窗✅防检测倍速播放(仅限CC播放器,最快8.0x)✅智能跳转逻辑✅自动处理签到弹窗
// @author       [dennischancs](https://github.com/dennischancs)
// @match        *://*.91huayi.com/*
// @grant        none
// @license      MIT
// @run-at       document-start
// @homepageURL  https://github.com/dennischancs/fuck-huayi-video
// @website      https://github.com/dennischancs/fuck-huayi-video
// @supportURL   https://github.com/dennischancs/fuck-huayi-video/issues
// ==/UserScript==

(function () {
    'use strict';

    // ==================== 🔥 智能播放器切换逻辑 ====================
    (function smartPlayerSwitch() {
        const currentPath = window.location.pathname;
        const currentSearch = window.location.search;
        const urlParams = new URLSearchParams(currentSearch);
        const cwid = urlParams.get('cwid');

        // 检测到 Polyv 播放器,等待加载完成后再切换到 CC
        if (currentPath.includes('course_ware_polyv.aspx')) {
            const failedCC = safeParseJSON(localStorage.getItem('huayi_failed_cc'), []);

            // 如果这个课程之前CC失败过,就不再切换
            if (failedCC.includes(cwid)) {
                console.log('⚠️ 该课程CC不可用,使用Polyv播放器(无倍速)');
                return;
            }

            console.log('🔄 检测到 Polyv 播放器,等待页面加载...');

            // 🔥 等待 Polyv 页面加载完成
            const waitForPolyvLoad = setInterval(() => {
                // 检测 Polyv 播放器是否已加载
                const polyvLoaded = document.querySelector('video') ||
                                   window.player ||
                                   document.querySelector('#player') ||
                                   document.readyState === 'complete';

                if (polyvLoaded) {
                    clearInterval(waitForPolyvLoad);
                    console.log('✅ Polyv 页面已加载,3秒后切换到 CC 播放器...');

                    setTimeout(() => {
                        // 🔥 关键修复:只保留 cwid 参数,清除 Polyv 专用参数
                        const newUrl = currentPath.replace('course_ware_polyv.aspx', 'course_ware_cc.aspx') + `?cwid=${cwid}`;
                        console.log('→ 切换到:', newUrl);
                        window.location.replace(newUrl);
                    }, 3000); // 等待3秒确保加载稳定
                }
            }, 500);

            // 超时保护:最多等待15秒
            setTimeout(() => {
                clearInterval(waitForPolyvLoad);
                if (currentPath.includes('course_ware_polyv.aspx')) {
                    console.log('⏰ 等待超时,强制切换到 CC 播放器');
                    const newUrl = currentPath.replace('course_ware_polyv.aspx', 'course_ware_cc.aspx') + `?cwid=${cwid}`;
                    window.location.replace(newUrl);
                }
            }, 15000);
        }

        // 检测CC播放器是否加载失败
        if (currentPath.includes('course_ware_cc.aspx')) {
            setTimeout(() => {
                const errorMsg = document.body?.textContent || '';

                if (errorMsg.includes('课件准备中') || errorMsg.includes('请刷新后重新进入') || errorMsg.includes('加载失败')) {
                    console.log('❌ CC播放器加载失败,回退到Polyv播放器');

                    // 记录这个课程CC不可用
                    const failedCC = safeParseJSON(localStorage.getItem('huayi_failed_cc'), []);
                    if (cwid && !failedCC.includes(cwid)) {
                        failedCC.push(cwid);
                        localStorage.setItem('huayi_failed_cc', JSON.stringify(failedCC));
                        console.log(`📝 已记录课程 ${cwid} 为CC不可用`);
                    }

                    // 回退到Polyv播放器(只保留cwid参数)
                    const newUrl = currentPath.replace('course_ware_cc.aspx', 'course_ware_polyv.aspx') + `?cwid=${cwid}`;
                    setTimeout(() => {
                        window.location.replace(newUrl);
                    }, 1000);
                }
            }, 3000); // 等待3秒检测是否加载成功
        }

        // 处理 course_ware.aspx 的情况(会自动跳转到 polyv)
        if (currentPath.includes('course_ware.aspx') && !currentPath.includes('_polyv') && !currentPath.includes('_cc')) {
            console.log('🔍 检测到通用入口 course_ware.aspx,等待自动跳转...');
            // 不做任何操作,让它自然跳转到 polyv,然后由上面的逻辑处理
        }
    })();

    // ==================== 🔥 防检测倍速劫持 ====================
    // 必须在 document-start 阶段运行,在网站脚本加载前劫持
    let realPlaybackRate = parseFloat(localStorage.getItem('huayi_playback_speed')) || 1.0;

    // 劫持 HTMLMediaElement.prototype.playbackRate
    const originalDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate');

    Object.defineProperty(HTMLMediaElement.prototype, 'playbackRate', {
        get: function() {
            // 返回假的倍速值(始终返回1.0欺骗检测)
            return 1.0;
        },
        set: function(value) {
            // 实际设置真实倍速
            if (value > 0 && value <= 16) {
                realPlaybackRate = value;
                originalDescriptor.set.call(this, value);
                console.log(`🎯 实际倍速已设置为: ${value}x (对外显示: 1.0x)`);
            }
        },
        configurable: true
    });

    // 劫持 getOwnPropertyDescriptor 防止网站检测我们的劫持
    const originalGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
    Object.getOwnPropertyDescriptor = function(obj, prop) {
        if (obj === HTMLMediaElement.prototype && prop === 'playbackRate') {
            // 返回原始描述符,隐藏我们的劫持
            return originalDescriptor;
        }
        return originalGetOwnPropertyDescriptor(obj, prop);
    };

    console.log('✅ 倍速劫持已启动,可安全使用任意倍速');
    console.log('✅ 智能播放器切换已启用(优先CC,失败自动回退)');

    // ==================== 主脚本 ====================

    let clock = null;
    let timeCheckInterval = null; // 时间监控定时器
    let isExpanded = false;
    let currentSpeed = realPlaybackRate;
    const urlTip = window.location.pathname.split('/').pop().split('?')[0];

    // 🔥 新增:安全的JSON解析函数
    function safeParseJSON(jsonString, defaultValue = null) {
        try {
            if (!jsonString || jsonString.trim() === '') {
                return defaultValue;
            }
            return JSON.parse(jsonString);
        } catch (e) {
            console.warn('JSON解析错误:', e, '数据:', jsonString);
            return defaultValue;
        }
    }

    // 🔥 新增:检查元素是否真正可见
    function isElementVisible(element) {
        if (!element) return false;

        const style = window.getComputedStyle(element);
        const rect = element.getBoundingClientRect();

        // 检查元素是否真的可见
        return style.display !== 'none' &&
               style.visibility !== 'hidden' &&
               style.opacity !== '0' &&
               rect.width > 0 &&
               rect.height > 0 &&
               element.offsetParent !== null;
    }

    // 等待DOM加载后再初始化UI
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    function init() {
        createPanel();

        if (urlTip.includes('course_ware')) {
            setPanelStatus('playing');
            saveCourseList();
            // 根据实际播放器类型初始化
            initVideo(urlTip.includes('polyv') ? 1 : 2);
        } else if (urlTip == 'face.aspx') {
            setPanelStatus('face');
            setTimeout(() => location.reload(), 5 * 60 * 1000);
        } else if (urlTip == 'course.aspx' || urlTip == 'cme.aspx') {
            setPanelStatus('list');
            saveCourseList();
            setTimeout(() => location.reload(), 5 * 60 * 1000);
        } else if (urlTip == 'exam_result.aspx') {
            setPanelStatus('exam');
            initExamPage();
        } else {
            setPanelStatus('error');
        }
    }

    // ==================== 核心功能 ====================

    function initVideo(type) {
        blockPopups();
        simulateUserActivity();

        window.onload = () => {
            clock = setInterval(checkVideoStatus, 3000);
        };

        setTimeout(() => {
            try {
                const video = document.querySelector('video');
                if (video) {
                    video.muted = true;
                    video.defaultMuted = true;

                    // 🔥 修改:确保默认播放速度为1.0
                    video.playbackRate = 1.0;

                    // 添加视频结束事件监听(正常速度播放时使用)
                    video.addEventListener('ended', () => {
                        if (currentSpeed === 1.0) {
                            console.log('🎬 视频自然结束(正常速度)');
                            // 触发状态检查,执行5秒等待逻辑
                            setTimeout(checkVideoStatus, 100);
                        }
                    });
                }

                if (type == 1 && typeof player !== 'undefined') {
                    // Polyv播放器(不支持倍速)
                    player.j2s_setVolume?.(0);
                    player.j2s_resumeVideo?.();
                    console.log('⚠️ 使用Polyv播放器(该课程不支持倍速)');
                } else if (typeof cc_js_Player !== 'undefined') {
                    // CC播放器(支持倍速)
                    cc_js_Player.setVolume?.(0);
                    cc_js_Player.play?.();
                    applyPlaybackSpeed(type);
                    console.log('✅ 使用CC播放器(支持防检测倍速)');
                }
            } catch (e) {
                console.log('播放器初始化错误:', e);
            }
        }, 8000);

        // 持续监听并应用倍速(仅对CC播放器)
        if (type == 2) {
            setInterval(() => {
                applyPlaybackSpeed(type);
            }, 5000);
        }

        // 添加时间监控定时器(倍速模式使用)
        if (type == 2 && currentSpeed > 1.0) {
            startTimeCheck();
        }

        // 页面卸载时清理定时器
        window.addEventListener('beforeunload', () => {
            clearInterval(clock);
            clearInterval(timeCheckInterval);
        });
    }

    // 🔥 修复:时间监控函数(倍速模式)- 考虑倍速影响,调整为240秒
    function startTimeCheck() {
        clearInterval(timeCheckInterval);

        // 🔥 关键修复:根据倍速调整检测频率和跳转时机
        const checkInterval = Math.max(500, 1000 / currentSpeed); // 倍速越高,检测越频繁
        const jumpThreshold = 240 / currentSpeed; // 🔥 修改:倍速下调整跳转阈值为240秒

        console.log(`🔧 倍速监控: 检测间隔${checkInterval}ms, 跳转阈值${jumpThreshold.toFixed(1)}秒`);

        timeCheckInterval = setInterval(() => {
            try {
                const video = document.querySelector('video');
                if (video && video.duration && !isNaN(video.duration)) {
                    const remaining = video.duration - video.currentTime;
                    const progress = video.currentTime / video.duration;

                    // 🔥 修复:使用调整后的跳转阈值,并添加进度保护
                    if (remaining <= jumpThreshold || progress >= 0.95) {
                        console.log(`⏱️ 倍速跳转: 剩余${Math.round(remaining)}秒, 进度${(progress*100).toFixed(1)}%`);
                        clearInterval(timeCheckInterval);
                        proceedToNext();
                    }
                }
            } catch (e) {
                console.log('时间监控错误:', e);
            }
        }, checkInterval);
    }

    function applyPlaybackSpeed(type) {
        try {
            const video = document.querySelector('video');

            if (video) {
                // 使用原始 setter 直接设置(绕过我们的劫持)
                originalDescriptor.set.call(video, currentSpeed);
                console.log(`🚀 倍速强制设置为: ${currentSpeed}x`);
            }

            // 使用 CC 播放器 API 设置(作为补充)
            if (typeof cc_js_Player !== 'undefined' && cc_js_Player.setPlaybackRate) {
                cc_js_Player.setPlaybackRate(currentSpeed);
            }
        } catch (e) {
            console.log('倍速设置错误:', e);
        }
    }

    function setPlaybackSpeed(speed) {
        currentSpeed = speed;
        realPlaybackRate = speed;
        localStorage.setItem('huayi_playback_speed', speed.toString());

        const type = urlTip.includes('polyv') ? 1 : 2;

        if (type == 1) {
            console.log('⚠️ 当前为Polyv播放器,不支持倍速');
        } else {
            applyPlaybackSpeed(type);
            console.log(`⚡ 倍速已更新为: ${speed}x (防检测模式)`);

            // 🔥 修复:如果切换到倍速模式,重启时间监控
            if (speed > 1.0) {
                startTimeCheck();
            } else {
                // 切换到正常速度,停止时间监控
                clearInterval(timeCheckInterval);
            }
        }

        // 刷新面板显示
        if (isExpanded) {
            expandPanel();
        }
    }

    function saveCourseList() {
        const courses = [];
        const items = document.querySelectorAll('.lis-inside-content, .r .lis, a[onclick*="cwid"]');

        items.forEach((item, idx) => {
            const title = item.querySelector('h2, h3, .title, a')?.textContent.trim();
            const status = item.querySelector('button, .status')?.textContent.trim() || '未知';
            const onclick = item.getAttribute('onclick') || item.querySelector('[onclick*="cwid"]')?.getAttribute('onclick');
            const cwid = onclick?.match(/cwid=([^'"\)]+)/)?.[1];

            if (cwid && title) {
                courses.push({ title, status, cwid, index: idx });
            }
        });

        if (courses.length > 0) {
            localStorage.setItem('huayi_course_list', JSON.stringify(courses));
            console.log(`✅ 已保存 ${courses.length} 个课程`);
        }
    }

    function checkVideoStatus() {
        try {
            let state = null;
            const stateEl = document.querySelector("i[id='top_play']");

            if (stateEl) {
                state = stateEl.parentNode?.nextElementSibling?.nextElementSibling?.nextElementSibling?.innerText;
            }

            if (!state) {
                const buttons = document.querySelectorAll('button, .state');
                for (let btn of buttons) {
                    const text = btn.textContent;
                    if (text?.includes('已完成') || text?.includes('待考试')) {
                        state = text.trim();
                        break;
                    }
                }
            }

            const video = document.querySelector('video');

            // 正常速度播放(非倍速)的特殊处理
            if (currentSpeed === 1.0 && video && video.ended) {
                const statusText = state || '';

                // 判断是否为"学习中"或"未学习"状态
                const isLearningStatus = statusText.includes('学习中') ||
                                       statusText.includes('未学习') ||
                                       (!statusText.includes('已完成') &&
                                        !statusText.includes('待考试'));

                if (isLearningStatus) {
                    console.log('📺 正常速度播放完成,等待5秒后跳转...');
                    setPanelStatus('completed');
                    clearInterval(clock);

                    // 等待5秒后跳转
                    setTimeout(() => {
                        console.log('⏰ 5秒等待结束,跳转到下一视频');
                        proceedToNext();
                    }, 5000);
                    return;
                }
            }

            // 🔥 修复:移除倍速模式下的进度检测,避免与时间监控冲突
            // 倍速模式下的跳转完全由 startTimeCheck 函数处理

            // 原有的完成状态检测
            if (state == '已完成') {
                console.log('✅ 视频完成,准备跳转');
                setPanelStatus('completed');
                clearInterval(clock);
                clearInterval(timeCheckInterval);
                setTimeout(() => proceedToNext(), 2000);
            } else if (state == '待考试') {
                console.log('📝 待考试状态,5秒后跳转');
                clearInterval(clock);
                clearInterval(timeCheckInterval);
                setTimeout(() => proceedToNext(), 5000);
            } else if (state) {
                setPanelStatus('playing');
            }
        } catch (e) {
            console.log('状态检测错误:', e);
        }
    }

    function proceedToNext() {
        // 确保当前视频进度被记录
        try {
            const video = document.querySelector('video');
            if (video && video.currentTime > 0) {
                console.log(`📊 记录进度: ${Math.round(video.currentTime/video.duration*100)}%`);
                // 模拟进度提交(根据实际API调整)
                window.dispatchEvent(new Event('beforeunload'));
            }
        } catch (e) {
            console.log('进度记录错误:', e);
        }

        const courses = safeParseJSON(localStorage.getItem('huayi_course_list'), []);
        const currentCwid = new URLSearchParams(location.search).get('cwid');
        const currentIdx = courses.findIndex(c => c.cwid === currentCwid);

        console.log(`🔍 当前课程索引: ${currentIdx}, 总数: ${courses.length}`);

        const isIncomplete = (status) => {
            if (!status) return true;
            const s = status.toLowerCase();
            return !s.includes('已完成') && !s.includes('完成');
        };

        let nextCourse = null;

        for (let i = currentIdx + 1; i < courses.length; i++) {
            if (isIncomplete(courses[i].status)) {
                nextCourse = courses[i];
                break;
            }
        }

        if (!nextCourse) {
            for (let i = 0; i < currentIdx; i++) {
                if (isIncomplete(courses[i].status)) {
                    nextCourse = courses[i];
                    break;
                }
            }
        }

        if (nextCourse) {
            console.log(`✅ 跳转到: ${nextCourse.title}`);

            // 🔥 智能选择播放器
            const failedCC = safeParseJSON(localStorage.getItem('huayi_failed_cc'), []);
            let url;

            if (failedCC.includes(nextCourse.cwid)) {
                // 该课程CC不可用,直接使用Polyv
                console.log('→ 使用Polyv播放器(该课程CC不可用)');
                url = `course_ware_polyv.aspx?cwid=${nextCourse.cwid}`;
            } else {
                // 优先尝试CC播放器(只传 cwid 参数)
                console.log('→ 尝试使用CC播放器(支持倍速)');
                url = `course_ware_cc.aspx?cwid=${nextCourse.cwid}`;
            }

            setTimeout(() => {
                location.href = url;
            }, 1000);
        } else {
            console.log('❌ 无下一课程,刷新页面');
            setTimeout(() => location.reload(), 2000);
        }
    }

    function blockPopups() {
        (async function blockSendQuestion() {
            while (!window.player?.sendQuestion) await new Promise(r => setTimeout(r, 20));
            window.player.sendQuestion = () => {};
        })();

        if (typeof isInteraction !== 'undefined') isInteraction = 'off';

        setInterval(() => {
            if (typeof $ === 'undefined') return;
            try {
                if ($('.pv-ask-head').length) $('.pv-ask-skip').click();
                if ($('.signBtn').length) $('.signBtn').click();
                if ($("button[onclick='closeBangZhu()']").length) $("button[onclick='closeBangZhu()']").click();
                if ($("button[class='btn_sign']").length) $("button[class='btn_sign']").click();

                // 🔥 修复:改进CC播放器签到弹窗检测
                const ccSignBtn = $('.ccSignWrapBtn');
                if (ccSignBtn.length > 0) {
                    let foundVisible = false;
                    ccSignBtn.each(function() {
                        if (isElementVisible(this)) {
                            console.log('📝 发现CC播放器签到弹窗');
                            $(this).click();
                            console.log('✅ 已点击CC播放器签到按钮');
                            foundVisible = true;
                            return false; // 只处理第一个可见的
                        }
                    });
                }

                // 🔥 新增:处理各种可能的签到按钮
                const signButtons = [
                    "button:contains('点击签到')",
                    "button:contains('签到')",
                    "a:contains('点击签到')",
                    "a:contains('签到')",
                    ".sign-in-btn",
                    ".qiandao-btn",
                    "#signBtn",
                    "#qiandaoBtn"
                ];

                signButtons.forEach(selector => {
                    try {
                        const elements = $(selector);
                        if (elements.length > 0) {
                            elements.each(function() {
                                if (isElementVisible(this)) {
                                    console.log(`📝 发现签到按钮: ${selector}`);
                                    $(this).click();
                                    console.log('✅ 已点击签到按钮');
                                    return false; // 只处理第一个可见的
                                }
                            });
                        }
                    } catch (e) {
                        // 忽略jQuery选择器错误
                    }
                });

                // 使用原生JavaScript处理签到按钮(备用方案)
                const allButtons = document.querySelectorAll('button, a, div[role="button"], .ccSignWrapBtn');
                allButtons.forEach(btn => {
                    const text = btn.textContent?.trim();
                    if (text && (text.includes('点击签到') || text.includes('签到'))) {
                        if (isElementVisible(btn)) {
                            console.log('📝 发现签到按钮(原生):', text);
                            btn.click();
                            console.log('✅ 已点击签到按钮(原生)');
                        }
                    }
                });

                const video = $('video').get(0);
                const state = document.querySelector("i[id='top_play']")?.parentNode?.nextElementSibling?.nextElementSibling?.nextElementSibling?.innerText;

                if (video?.paused && state != '已完成' && state != '待考试') {
                    video.play();
                    video.muted = true;
                }
            } catch (e) {}
        }, 10000);

        // 🔥 修复:改进更频繁的签到弹窗检测
        setInterval(() => {
            try {
                // 🔥 专门处理CC播放器签到弹窗 - 使用更严格的检测
                const ccSignWrap = document.querySelector('.ccSignWrap');
                if (ccSignWrap && isElementVisible(ccSignWrap)) {
                    const ccSignBtn = ccSignWrap.querySelector('.ccSignWrapBtn');
                    if (ccSignBtn && isElementVisible(ccSignBtn)) {
                        console.log('📝 发现CC播放器签到弹窗(原生)');
                        ccSignBtn.click();
                        console.log('✅ 已点击CC播放器签到按钮(原生)');
                    }
                }

                // 检查是否有签到弹窗出现
                const modal = document.querySelector('.modal, .popup, .dialog, .overlay');
                if (modal && isElementVisible(modal)) {
                    const signBtn = modal.querySelector('button, a, div[role="button"], .ccSignWrapBtn');
                    if (signBtn && isElementVisible(signBtn) && (signBtn.textContent?.includes('签到') || signBtn.textContent?.includes('点击'))) {
                        console.log('📝 发现弹窗中的签到按钮');
                        signBtn.click();
                        console.log('✅ 已点击弹窗签到按钮');
                    }
                }

                // 检查常见的签到弹窗ID和类名
                const signModals = [
                    '#signModal',
                    '#qiandaoModal',
                    '.sign-modal',
                    '.qiandao-modal',
                    '.sign-popup',
                    '.qiandao-popup'
                ];

                signModals.forEach(selector => {
                    const element = document.querySelector(selector);
                    if (element && isElementVisible(element)) {
                        const closeBtn = element.querySelector('.close, .modal-close, [onclick*="close"], button[aria-label="关闭"]');
                        if (closeBtn && isElementVisible(closeBtn)) {
                            closeBtn.click();
                            console.log('✅ 已关闭签到弹窗');
                        }
                    }
                });
            } catch (e) {
                console.log('签到弹窗处理错误:', e);
            }
        }, 2000); // 改为每2秒检测一次,提高响应速度
    }

    function simulateUserActivity() {
        const getVideoArea = () => {
            const video = document.querySelector('video');
            if (video) {
                const rect = video.getBoundingClientRect();
                return { x: rect.left, y: rect.top, width: rect.width, height: rect.height };
            }
            return {
                x: window.innerWidth * 0.2,
                y: window.innerHeight * 0.2,
                width: window.innerWidth * 0.6,
                height: window.innerHeight * 0.6
            };
        };

        const simulateMove = () => {
            try {
                const area = getVideoArea();
                for (let i = 0; i < 3; i++) {
                    setTimeout(() => {
                        const x = area.x + Math.random() * area.width;
                        const y = area.y + Math.random() * area.height;
                        document.dispatchEvent(new MouseEvent('mousemove', {
                            view: window, bubbles: true, cancelable: true,
                            clientX: x, clientY: y
                        }));
                    }, i * 200);
                }
            } catch (e) {}
        };

        const scheduleActivity = () => {
            const interval = Math.random() * (10 - 5) * 60 * 1000 + 5 * 60 * 1000;
            setTimeout(() => {
                simulateMove();
                scheduleActivity();
            }, interval);
        };

        setTimeout(scheduleActivity, Math.random() * 60000 + 30000);
    }

    function initExamPage() {
        const clickLearn = () => {
            const buttons = document.querySelectorAll('button, a, input[type="button"]');
            for (let btn of buttons) {
                if (btn.textContent?.includes('立即学习')) {
                    btn.click();
                    console.log('📝 点击立即学习');
                    break;
                }
            }
        };

        setInterval(clickLearn, 30000);
        setTimeout(clickLearn, 2000);
    }

// ==================== UI ====================

    function createPanel() {
        if (window.self !== window.top) return;

        const panel = document.createElement('div');
        panel.id = 'huayi-panel';
        panel.style.cssText = `
            position: fixed; top: 20px; right: 20px; width: 40px; height: 40px;
            background: #4CAF50; border-radius: 50%; box-shadow: 0 2px 8px rgba(0,0,0,0.2);
            z-index: 99999; cursor: pointer;
            display: flex; align-items: center; justify-content: center;
            font-size: 20px; color: white; overflow: hidden;
        `;

        document.body.appendChild(panel);

        panel.onclick = (e) => {
            e.stopPropagation();
            togglePanel();
        };

        document.onclick = (e) => {
            const panel = document.getElementById('huayi-panel');
            if (isExpanded && panel && !panel.contains(e.target)) {
                collapsePanel();
            }
        };
    }

    function togglePanel() {
        isExpanded ? collapsePanel() : expandPanel();
    }

    function expandPanel() {
        const panel = document.getElementById('huayi-panel');
        if (!panel) return;

        isExpanded = true;
        panel.onclick = (e) => {
            e.stopPropagation();
        };
        panel.style.cssText = `
            position: fixed; top: 20px; right: 20px; width: 260px; height: auto;
            background: #4CAF50; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            z-index: 99999; cursor: pointer;
            display: flex; flex-direction: column; align-items: flex-start;
            padding: 12px; font-size: 12px; color: white;
        `;

        // 🔥 修复:使用安全的JSON解析
        const courses = safeParseJSON(localStorage.getItem('huayi_course_list'), []);
        const currentCwid = new URLSearchParams(location.search).get('cwid');
        const current = courses.find(c => c.cwid === currentCwid);
        const status = panel.getAttribute('data-status') || 'init';
        const statusMap = {
            init: '初始化', playing: '播放中', completed: '已完成',
            face: '刷脸中', list: '课程列表', exam: '考试处理', error: '未适配'
        };

        const isPolyv = urlTip.includes('polyv');
        const playerType = isPolyv ? 'Polyv (无倍速)' : 'CC (支持倍速)';
        const failedCC = safeParseJSON(localStorage.getItem('huayi_failed_cc'), []);

        const isNormalSpeed = currentSpeed === 1.0;
        // 🔥 修改:根据倍速动态显示跳转时间,调整为240秒
        const jumpTime = isNormalSpeed ? 240 : Math.round(240 / currentSpeed);
        const speedModeText = isNormalSpeed ? '正常速度 (播放完等待5秒)' : `倍速 ${currentSpeed}x (剩余${jumpTime}秒跳转)`;

        panel.innerHTML = `
            <div style="font-weight: bold; margin-bottom: 8px;">🛡️ 华医网视频播放脚本 Pro</div>
            <div style="margin-bottom: 4px;">状态: ${statusMap[status]}</div>
            <div style="margin-bottom: 4px; font-size: 10px; opacity: 0.9;">播放模式: ${speedModeText}</div>
            <div style="margin-bottom: 4px; font-size: 10px; opacity: 0.9;">播放器: ${playerType}</div>
            ${current ? `<div style="font-size: 10px; opacity: 0.8;">当前: ${current.title.substring(0,16)}...</div>` : ''}
            <div style="font-size: 10px; opacity: 0.8; margin-top: 4px;">课程数: ${courses.length} | CC失败: ${failedCC.length}</div>

            <div style="width: 100%; margin-top: 10px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,0.3);">
                <div style="font-weight: bold; margin-bottom: 6px;">⚡ 防检测倍速 ${isPolyv ? '(不可用)' : ''}</div>
                <div style="display: flex; align-items: center; gap: 8px;">
                    <input type="range" id="speedSlider" min="0.5" max="8" step="0.25" value="${currentSpeed}"
                           style="flex: 1; ${isPolyv ? 'disabled' : ''}" />
                    <span id="speedValue" style="min-width: 45px; text-align: center;">${currentSpeed.toFixed(2)}x</span>
                </div>
                ${isPolyv ? '<div style="font-size: 9px; opacity: 0.7; margin-top: 4px;">⚠️ 当前课程不支持倍速</div>' : ''}
                ${isNormalSpeed && !isPolyv ? '<div style="font-size: 9px; opacity: 0.7; margin-top: 4px;">📺 正常模式:播放完等待5秒跳转</div>' : ''}
                ${!isNormalSpeed && !isPolyv ? `<div style="font-size: 9px; opacity: 0.7; margin-top: 4px;">🚀 倍速模式:剩余${jumpTime}秒自动跳转</div>` : ''}
            </div>

            <button id="nextBtn" style="width: 100%; padding: 6px; margin-top: 10px; background: rgba(255,255,255,0.2);
                border: 1px solid rgba(255,255,255,0.5); color: white; border-radius: 4px; cursor: pointer; font-size: 11px;">
                🚀 手动跳转下一课
            </button>

            <button id="clearFailedBtn" style="width: 100%; padding: 4px; margin-top: 6px; background: rgba(255,255,255,0.15);
                border: 1px solid rgba(255,255,255,0.3); color: white; border-radius: 4px; cursor: pointer; font-size: 10px;">
                🔄 清除CC失败记录
            </button>

            <div style="font-size: 9px; opacity: 0.7; margin-top: 8px; padding-top: 4px; border-top: 1px solid rgba(255,255,255,0.2);">
                ✅ 智能切换播放器 ✅ 自动回退 ✅ 防检测倍速<br>✅ 智能跳转逻辑 ✅ 自动处理CC签到
            </div>
        `;

        // 绑定滑块事件
        const speedSlider = document.getElementById('speedSlider');
        const speedValue = document.getElementById('speedValue');
        if (speedSlider) {
            speedSlider.addEventListener('input', (e) => {
                const speed = parseFloat(e.target.value);
                speedValue.textContent = speed.toFixed(2) + 'x';
                setPlaybackSpeed(speed);
            });
        }

        // 绑定跳转按钮事件
        document.getElementById('nextBtn')?.addEventListener('click', (e) => {
            e.stopPropagation();
            proceedToNext();
        });

        // 绑定清除失败记录按钮
        document.getElementById('clearFailedBtn')?.addEventListener('click', (e) => {
            e.stopPropagation();
            localStorage.removeItem('huayi_failed_cc');
            console.log('✅ 已清除CC失败记录');
            expandPanel(); // 刷新面板
        });
    }

    function collapsePanel() {
        const panel = document.getElementById('huayi-panel');
        if (!panel) return;

        isExpanded = false;
        panel.style.cssText = `
            position: fixed; top: 20px; right: 20px; width: 40px; height: 40px;
            background: #4CAF50; border-radius: 50%; box-shadow: 0 2px 8px rgba(0,0,0,0.2);
            z-index: 99999; cursor: pointer;
            display: flex; align-items: center; justify-content: center;
            font-size: 20px; color: white;
        `;

        const config = {
            init: { color: '#9E9E9E', icon: '⚪' },
            playing: { color: '#4CAF50', icon: '▶️' },
            completed: { color: '#2196F3', icon: '✅' },
            face: { color: '#FF9800', icon: '👤' },
            list: { color: '#9C27B0', icon: '📋' },
            exam: { color: '#FF5722', icon: '📝' },
            error: { color: '#F44336', icon: '❌' }
        };

        const status = panel.getAttribute('data-status') || 'init';
        const { color, icon } = config[status];

        panel.style.background = color;
        panel.textContent = icon;

        // 🔥 修复:重新绑定点击事件,确保可以再次展开
        panel.onclick = (e) => {
            e.stopPropagation();
            togglePanel();
        };
    }

    function setPanelStatus(status) {
        const panel = document.getElementById('huayi-panel');
        if (!panel) return;

        panel.setAttribute('data-status', status);

        if (!isExpanded) {
            const config = {
                init: { color: '#9E9E9E', icon: '⚪' },
                playing: { color: '#4CAF50', icon: '▶️' },
                completed: { color: '#2196F3', icon: '✅' },
                face: { color: '#FF9800', icon: '👤' },
                list: { color: '#9C27B0', icon: '📋' },
                exam: { color: '#FF5722', icon: '📝' },
                error: { color: '#F44336', icon: '❌' }
            };

            const { color, icon } = config[status];
            panel.style.background = color;
            panel.textContent = icon;
        }
    }

})();