华医网课脚本

华医岗位课程自动播放工具:支持课程自动播放、倍速调节、全局静音切换、自动跳转下一课

// ==UserScript==
// @name         华医网课脚本
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  华医岗位课程自动播放工具:支持课程自动播放、倍速调节、全局静音切换、自动跳转下一课
// @author       ssxbs
// @match        https://jcpxkh.91huayi.com/exercise/ExerciseHome/index*
// @match        *://*/*courseware_id*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // 配置参数:新增首次加载优化相关配置
    const CONFIG = {
        // 原有配置保持不变...
        POST_COURSE_SEL: 'div.navItem[id="/Exercise/ExerciseCourse/ExcellentIndex?package=true"]',
        IFRAME_SEL: '#student_iframe',
        COURSE_LIST_SEL: '.itemList',
        COURSE_ITEM_SEL: '.contentItem',
        COURSE_PLAY_PATHS: [
            'exercise/ExerciseCourse/CoursePlay',
            'exercise/ExerciseCourse/BJYCoursePlay'
        ],
        PAGE_CLOSE_DELAY: 2000,
        PAGE_CLOSE_RETRY: 2,
        DEBUG_MODE: true,
        PANEL_STYLE: {
            position: 'fixed',
            top: '10px',
            left: '10px',
            backgroundColor: 'rgba(0,0,0,0.8)',
            color: '#fff',
            padding: '12px',
            borderRadius: '8px',
            fontSize: '14px',
            zIndex: '99999',
            minWidth: '320px',
            maxHeight: '80vh',
            overflowY: 'auto',
            boxShadow: '0 4px 12px rgba(0,0,0,0.3)'
        },
        DRAG_HANDLE_STYLE: {
            height: '24px',
            cursor: 'move',
            margin: '-12px -12px 12px -12px',
            padding: '4px 12px',
            backgroundColor: 'rgba(50,50,100,0.5)',
            borderRadius: '8px 8px 0 0',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            userSelect: 'none'
        },
        COURSE_ITEM_STYLE: {
            padding: '8px',
            margin: '6px 0',
            backgroundColor: 'rgba(255,255,255,0.1)',
            borderRadius: '4px',
            cursor: 'pointer',
            transition: 'background-color 0.3s',
            display: 'flex',
            alignItems: 'center'
        },
        COURSE_ITEM_HOVER_STYLE: {
            backgroundColor: 'rgba(255,255,255,0.2)'
        },
        COURSE_ITEM_PLAYING_STYLE: {
            backgroundColor: 'rgba(66, 133, 244, 0.3)',
            border: '1px solid #4285f4',
            boxShadow: '0 0 8px rgba(66, 133, 244, 0.5)'
        },
        BUTTON_STYLE: {
            padding: '6px 10px',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
            transition: 'all 0.2s',
            fontSize: '13px',
            fontWeight: '500',
            boxShadow: '0 1px 3px rgba(0,0,0,0.2)'
        },
        BUTTON_HOVER_STYLE: {
            transform: 'translateY(-1px)',
            boxShadow: '0 2px 5px rgba(0,0,0,0.3)'
        },
        BUTTON_COLORS: {
            speed: { background: '#4285f4', color: '#fff' },
            mute: { background: '#fbbc05', color: '#000' }
        },
        LIST_TOGGLE_STYLE: {
            padding: '3px 8px',
            border: '1px solid rgba(255,255,255,0.3)',
            borderRadius: '4px',
            backgroundColor: 'rgba(255,255,255,0.1)',
            color: '#fff',
            cursor: 'pointer',
            fontSize: '14px',
            transition: 'all 0.2s'
        },
        CHECK_INTERVAL: 1500,
        INIT_DELAY: 2000,
        DEFAULT_SPEED: 1.0,
        BPLAYER_SPEED: 2.0,
        DETECT_INTERVAL_MS: 1000,
        STARTUP_PROGRESS_CHECK_TIME: 30000,
        LEARNED_COUNTDOWN_SECONDS: 10,
        AUTO_PLAY_RETRY_INTERVAL_MS: 1000,
        PROGRESS_CHANGE_THRESHOLD: 0.1,
        MAX_NO_PROGRESS_COUNT: 30,
        PROGRESS_STALL_RETRY_SECONDS: 10,
        AUTO_MUTE_ON_PLAY: true,
        VOLUME_CONTROL_SELECTORS: [
            '#speaker', '.ccH5vm', '.volume-btn', '.bplayer-volume-btn'
        ],
        VOLUME_SLIDER_SEL: '.volume-slider, .bplayer-volume-slider, .ccH5VolumeSlider',

        // 新增:首次加载优化配置
        FIRST_LOAD_DELAY: 3000,          // 首次加载额外延迟(ms)
        RESOURCE_CHECK_RETRY: 5,         // 资源检查重试次数
        RESOURCE_CHECK_INTERVAL: 1000,   // 资源检查间隔(ms)
        FIRST_JUMP_DELAY: 1500,          // 首次跳转额外延迟(ms)
        MIN_LOADING_TIME: 2000,          // 最小加载时间(ms)
        INITIAL_OPERATION_DELAY: 1000    // 初始操作延迟(ms)
    };

    // 全局变量:新增首次加载状态标记
    let hasClickedPostCourse = false;
    let iframeDoc = null;
    let pendingCourses = [];
    let mainPanel = null;
    let checkIframeTimer = null;
    let updateCourseTimer = null;
    let playbackSpeed = CONFIG.DEFAULT_SPEED;
    let hasPlayedNext = false;
    let speedChecked = false;
    let detectorTimerId = null;
    let startupProgressTimerId = null;
    let countdownTimerId = null;
    let learnedCountdownTimerId = null;
    let countdownTimeRemaining = CONFIG.MAX_NO_PROGRESS_COUNT;
    let learnedCountdownTimeRemaining = CONFIG.LEARNED_COUNTDOWN_SECONDS;
    let isLearnedCountdownActive = false;
    let autoPlayIntervalId = null;
    let initialProgress = 0;
    let currentCourseId = null;
    let lastProgress = 0;
    let noProgressCount = 0;
    let isJumping = false;
    let hasAutoJumped = false;
    let isGlobalMuted = false;
    let muteAttempts = 0;
    const MAX_MUTE_ATTEMPTS = 5;
    let jumpAttempts = 0;
    const MAX_JUMP_ATTEMPTS = 3;
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;
    let isCourseListExpanded = true;
    let userMuteToggle = false;
    // 新增:首次加载状态跟踪
    let isFirstLoad = true;            // 是否首次加载
    let pageLoadStartTime = 0;         // 页面加载开始时间
    let resourceCheckAttempts = 0;     // 资源检查尝试次数

    // 调试日志
    const log = (message) => {
        if (CONFIG.DEBUG_MODE) console.log(`[华医脚本] ${message}`);
    };

    // 面板拖拽功能
    const makePanelDraggable = () => {
        const dragHandle = document.createElement('div');
        dragHandle.id = 'huayiDragHandle';
        dragHandle.textContent = '拖动面板';
        Object.keys(CONFIG.DRAG_HANDLE_STYLE).forEach(key => {
            dragHandle.style[key] = CONFIG.DRAG_HANDLE_STYLE[key];
        });

        mainPanel.insertBefore(dragHandle, mainPanel.firstChild);

        dragHandle.addEventListener('mousedown', (e) => {
            isDragging = true;
            const rect = mainPanel.getBoundingClientRect();
            offsetX = e.clientX - rect.left;
            offsetY = e.clientY - rect.top;
            mainPanel.style.transition = 'none';
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging || !mainPanel) return;

            const x = e.clientX - offsetX;
            const y = e.clientY - offsetY;
            const maxX = window.innerWidth - mainPanel.offsetWidth;
            const maxY = window.innerHeight - mainPanel.offsetHeight;
            const boundedX = Math.max(0, Math.min(x, maxX));
            const boundedY = Math.max(0, Math.min(y, maxY));

            mainPanel.style.left = `${boundedX}px`;
            mainPanel.style.top = `${boundedY}px`;
        });

        document.addEventListener('mouseup', () => {
            if (isDragging && mainPanel) {
                isDragging = false;
                mainPanel.style.transition = '';
            }
        });
    };

    // 切换课程列表展开/收起状态
    const toggleCourseList = () => {
        isCourseListExpanded = !isCourseListExpanded;
        const courseListContainer = document.getElementById('huayiCourseListContainer');
        const toggleBtn = document.getElementById('huayiListToggleBtn');

        courseListContainer.style.display = isCourseListExpanded ? 'block' : 'none';
        toggleBtn.textContent = isCourseListExpanded ? '收起列表' : '展开列表';
        localStorage.setItem('huayiCourseListExpanded', isCourseListExpanded);
    };

    // 设置音量为0
    const setVolumeToZero = () => {
        if (userMuteToggle) return;

        document.querySelectorAll('video').forEach(video => {
            try { video.volume = 0; }
            catch (e) { log(`设置video音量失败: ${e.message}`); }
        });

        document.querySelectorAll(CONFIG.VOLUME_SLIDER_SEL).forEach(slider => {
            try {
                if (slider.tagName === 'INPUT' && ['range', 'slider'].includes(slider.type)) {
                    slider.value = 0;
                    slider.dispatchEvent(new Event('change', { bubbles: true }));
                    slider.dispatchEvent(new Event('input', { bubbles: true }));
                } else if (slider.style.width !== undefined) {
                    slider.style.width = '0%';
                }
            } catch (e) {
                log(`操作音量滑块失败: ${e.message}`);
            }
        });
    };

    // 点击音量按钮尝试静音
    const clickVolumeButtons = () => {
        if (userMuteToggle) return;

        let success = false;
        CONFIG.VOLUME_CONTROL_SELECTORS.forEach(selector => {
            document.querySelectorAll(selector).forEach(button => {
                try {
                    const isMuted = button.getAttribute('data-muted') === 'true' ||
                                   button.classList.contains('muted') ||
                                   button.classList.contains('ccH5vm-mute');

                    if (!isMuted) {
                        const rect = button.getBoundingClientRect();
                        button.dispatchEvent(new MouseEvent('click', {
                            clientX: rect.left + rect.width / 2,
                            clientY: rect.top + rect.height / 2,
                            bubbles: true,
                            cancelable: true
                        }));
                        button.setAttribute('data-muted', 'true');
                        success = true;
                    } else {
                        success = true;
                    }
                } catch (e) {
                    log(`点击音量按钮(${selector})失败: ${e.message}`);
                }
            });
        });
        return success;
    };

    // 确保静音状态
    const ensureMuted = () => {
        if (userMuteToggle || !CONFIG.AUTO_MUTE_ON_PLAY) return;
        if (muteAttempts >= MAX_MUTE_ATTEMPTS) {
            log(`已尝试${MAX_MUTE_ATTEMPTS}次静音,暂停尝试`);
            return;
        }

        log(`第${muteAttempts + 1}次尝试静音...`);
        const buttonSuccess = clickVolumeButtons();
        setVolumeToZero();

        try {
            if (document.muted !== undefined) document.muted = true;
            document.querySelectorAll('video').forEach(video => video.muted = true);
            isGlobalMuted = true;
            updateMuteStatus();
        } catch (e) {
            log(`使用muted属性静音失败: ${e.message}`);
        }

        muteAttempts++;
        if (!buttonSuccess && muteAttempts < MAX_MUTE_ATTEMPTS) {
            setTimeout(ensureMuted, 2000);
        }
    };

    // 切换全局静音状态
    const toggleGlobalMute = () => {
        userMuteToggle = true;

        if (isGlobalMuted) {
            try {
                if (document.muted !== undefined) document.muted = false;
                document.querySelectorAll('video').forEach(video => {
                    video.muted = false;
                    if (video.volume === 0) video.volume = 1;
                });

                CONFIG.VOLUME_CONTROL_SELECTORS.forEach(selector => {
                    document.querySelectorAll(selector).forEach(button => {
                        button.setAttribute('data-muted', 'false');
                        button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
                    });
                });
                isGlobalMuted = false;
            } catch (e) {
                log(`取消静音失败: ${e.message}`);
            }
        } else {
            try {
                if (document.muted !== undefined) document.muted = true;
                document.querySelectorAll('video').forEach(video => video.muted = true);

                CONFIG.VOLUME_CONTROL_SELECTORS.forEach(selector => {
                    document.querySelectorAll(selector).forEach(button => {
                        button.setAttribute('data-muted', 'true');
                        button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
                    });
                });
                isGlobalMuted = true;
            } catch (e) {
                log(`设置静音失败: ${e.message}`);
            }
        }
        updateMuteStatus();
    };

    // 更新静音状态显示
    const updateMuteStatus = () => {
        const muteLabel = document.getElementById('videoMuteLabel');
        if (muteLabel) {
            muteLabel.textContent = `全局声音:${isGlobalMuted ? '🔇 已静音' : '🔊 未静音'}`;
        }
    };

    // 标记课程为异常
    const markCourseAsAbnormal = (courseId) => {
        if (!courseId) return;
        const courseIndex = pendingCourses.findIndex(c => c.id === courseId);
        if (courseIndex === -1) return;

        // 只有未学习的课程才会被标记为异常
        if (!pendingCourses[courseIndex].isLearned) {
            pendingCourses[courseIndex].isAbnormal = true;
            syncToLocalStorage();
            updatePendingListUI();
            updateNextVideo();
            log(`已标记课程 ${courseId} 为异常`);
        }
    };

    // 检查页面关键资源是否加载完成
    const checkPageResources = () => {
        // 检查基本DOM元素是否存在
        if (isFirstLoad && window.location.href.includes('courseware_id')) {
            // 播放页需要检查的资源
            const videoContainer = document.querySelector('video') ||
                                 document.querySelector('.bplayer-video') ||
                                 document.querySelector('.ccH5Video');

            const progressBar = document.querySelector('.bplayer-progress') ||
                              document.querySelector('.ccH5Progress');

            // 资源未加载完成
            if (!videoContainer || !progressBar) {
                resourceCheckAttempts++;
                if (resourceCheckAttempts < CONFIG.RESOURCE_CHECK_RETRY) {
                    log(`第${resourceCheckAttempts}次检查播放页资源,尚未准备就绪`);
                    return false;
                } else {
                    log(`达到最大资源检查次数(${CONFIG.RESOURCE_CHECK_RETRY}),尝试继续`);
                }
            }
        } else if (isFirstLoad) {
            // 列表页需要检查的资源
            const courseList = document.querySelector(CONFIG.COURSE_LIST_SEL) ||
                             document.querySelector(CONFIG.IFRAME_SEL);

            if (!courseList) {
                resourceCheckAttempts++;
                if (resourceCheckAttempts < CONFIG.RESOURCE_CHECK_RETRY) {
                    log(`第${resourceCheckAttempts}次检查列表页资源,尚未准备就绪`);
                    return false;
                } else {
                    log(`达到最大资源检查次数(${CONFIG.RESOURCE_CHECK_RETRY}),尝试继续`);
                }
            }
        }

        // 检查页面加载时间是否足够
        const elapsedTime = Date.now() - pageLoadStartTime;
        if (elapsedTime < CONFIG.MIN_LOADING_TIME) {
            log(`页面加载时间不足(${elapsedTime}ms),等待至${CONFIG.MIN_LOADING_TIME}ms`);
            return false;
        }

        return true;
    };

    // 等待页面资源加载完成
    const waitForResources = (callback) => {
        if (checkPageResources()) {
            log("页面资源检查通过,执行操作");
            callback();
        } else if (resourceCheckAttempts < CONFIG.RESOURCE_CHECK_RETRY) {
            setTimeout(() => waitForResources(callback), CONFIG.RESOURCE_CHECK_INTERVAL);
        } else {
            log("资源检查超时,仍尝试执行操作");
            callback();
        }
    };

    // 清理所有资源
    const clearAllResources = () => {
        log("开始清理当前页资源...");
        [checkIframeTimer, updateCourseTimer, detectorTimerId,
         startupProgressTimerId, countdownTimerId, learnedCountdownTimerId,
         autoPlayIntervalId].forEach(timer => {
            if (timer) clearInterval(timer);
        });

        const videoElem = document.querySelector('video');
        if (videoElem) {
            videoElem.removeEventListener('ended', handleVideoEnded);
            videoElem.pause();
        }

        isLearnedCountdownActive = false;
        isJumping = false;
        jumpAttempts = 0;
        log("当前页资源清理完成");
    };

    // 关闭当前页面
    const closeCurrentPage = (retryCount = 0) => {
        if (!window.location.href.includes('courseware_id')) return;

        clearAllResources();
        setTimeout(() => {
            try {
                if (window.close()) {
                    log(`第${retryCount + 1}次关闭成功`);
                    return;
                }

                log(`第${retryCount + 1}次直接关闭失败,尝试刷新后关闭`);
                window.location.reload();
                setTimeout(() => {
                    if (window.close()) log("刷新后关闭成功");
                    else throw new Error("刷新后关闭仍失败");
                }, 1000);

            } catch (e) {
                log(`关闭失败: ${e.message}`);
                if (retryCount < CONFIG.PAGE_CLOSE_RETRY) {
                    setTimeout(() => closeCurrentPage(retryCount + 1), CONFIG.PAGE_CLOSE_DELAY);
                } else {
                    alert(`自动关闭失败(已重试${CONFIG.PAGE_CLOSE_RETRY + 1}次),请手动关闭`);
                }
            }
        }, CONFIG.PAGE_CLOSE_DELAY);
    };

    // 创建控制面板
    const createMainPanel = () => {
        if (document.getElementById('huayiMainPanel')) return;

        const storedExpanded = localStorage.getItem('huayiCourseListExpanded');
        if (storedExpanded !== null) isCourseListExpanded = storedExpanded === 'true';

        mainPanel = document.createElement('div');
        mainPanel.id = 'huayiMainPanel';
        Object.keys(CONFIG.PANEL_STYLE).forEach(key => {
            mainPanel.style[key] = CONFIG.PANEL_STYLE[key];
        });

        makePanelDraggable();

        // 状态显示区域
        const statusSection = document.createElement('div');
        statusSection.id = 'huayiStatusSection';
        statusSection.style.marginBottom = '12px';
        statusSection.style.paddingBottom = '10px';
        statusSection.style.borderBottom = '1px solid rgba(255,255,255,0.3)';

        const statusTitle = document.createElement('h4');
        statusTitle.textContent = '播放状态监控';
        statusTitle.style.margin = '0 0 8px 0';
        statusSection.appendChild(statusTitle);

        // 状态标签集合
        ['videoStatusLabel', 'videoMuteLabel', 'learnedCountdownLabel',
         'countdownLabel', 'videoProgressLabel', 'videoSpeedLabel',
         'nextVideoLabel', 'currentCourseLabel'].forEach(id => {
            const label = document.createElement('p');
            label.id = id;
            if (id === 'learnedCountdownLabel') {
                label.style.display = 'none';
                label.style.color = '#4CAF50';
            } else if (id === 'currentCourseLabel') {
                label.style.fontSize = '12px';
                label.style.color = 'rgba(255,255,255,0.7)';
            }
            statusSection.appendChild(label);
        });

        // 控制按钮区域
        const btnRow = document.createElement('div');
        btnRow.style.display = 'flex';
        btnRow.style.gap = '6px';
        btnRow.style.marginTop = '8px';
        btnRow.style.flexWrap = 'wrap';

        const speedBtn = createStyledButton('设置倍速', 'speed');
        const muteBtn = createStyledButton('切换静音', 'mute');
        btnRow.appendChild(speedBtn);
        btnRow.appendChild(muteBtn);

        statusSection.appendChild(btnRow);
        mainPanel.appendChild(statusSection);

        // 课程列表区域
        const pendingSection = document.createElement('div');
        pendingSection.id = 'huayiPendingSection';

        const pendingHeader = document.createElement('div');
        pendingHeader.style.display = 'flex';
        pendingHeader.style.alignItems = 'center';

        const pendingTitle = document.createElement('h4');
        pendingTitle.textContent = '待播放课程列表';
        pendingTitle.style.margin = '0 0 8px 0';
        pendingTitle.style.flex = '1';

        const toggleBtn = document.createElement('button');
        toggleBtn.id = 'huayiListToggleBtn';
        toggleBtn.textContent = isCourseListExpanded ? '收起列表' : '展开列表';
        Object.keys(CONFIG.LIST_TOGGLE_STYLE).forEach(key => {
            toggleBtn.style[key] = CONFIG.LIST_TOGGLE_STYLE[key];
        });
        toggleBtn.addEventListener('click', toggleCourseList);

        pendingHeader.appendChild(pendingTitle);
        pendingHeader.appendChild(toggleBtn);
        pendingSection.appendChild(pendingHeader);

        const courseListContainer = document.createElement('div');
        courseListContainer.id = 'huayiCourseListContainer';
        courseListContainer.style.display = isCourseListExpanded ? 'block' : 'none';
        pendingSection.appendChild(courseListContainer);

        const emptyTip = document.createElement('p');
        emptyTip.id = 'huayiEmptyTip';
        emptyTip.textContent = '等待加载课程数据...';
        courseListContainer.appendChild(emptyTip);

        mainPanel.appendChild(pendingSection);
        document.body.appendChild(mainPanel);

        // 绑定按钮事件
        speedBtn.onclick = () => {
            const newSpeed = prompt('请输入播放速度 (0.5-5.0):', playbackSpeed);
            if (newSpeed !== null) {
                const speedVal = parseFloat(newSpeed);
                if (!isNaN(speedVal) && speedVal >= 0.5 && speedVal <= 5.0) {
                    playbackSpeed = speedVal;
                    setVideoSpeed(playbackSpeed);
                    updateSpeed(playbackSpeed);
                    speedChecked = true;
                    clearDetectorTimer();
                } else {
                    alert('请输入有效数值 (0.5-5.0)');
                }
            }
        };

        muteBtn.onclick = toggleGlobalMute;

        // 初始化UI显示
        updateStatus(getCurrentPlayStatus());
        updateMuteStatus();
        updateLearnedCountdown(0);
        updateCountdown(countdownTimeRemaining);
        updateProgress(0, '00:00', '00:00');
        updateSpeed(playbackSpeed);
        updateNextVideo();
        updateCurrentCourseId();
    };

    // 创建带样式的按钮
    const createStyledButton = (text, type) => {
        const btn = document.createElement('button');
        btn.textContent = text;

        Object.keys(CONFIG.BUTTON_STYLE).forEach(key => {
            btn.style[key] = CONFIG.BUTTON_STYLE[key];
        });

        const colors = CONFIG.BUTTON_COLORS[type];
        if (colors) {
            btn.style.backgroundColor = colors.background;
            btn.style.color = colors.color;
        }

        btn.addEventListener('mouseover', () => {
            Object.keys(CONFIG.BUTTON_HOVER_STYLE).forEach(key => {
                btn.style[key] = CONFIG.BUTTON_HOVER_STYLE[key];
            });
        });

        btn.addEventListener('mouseout', () => {
            Object.keys(CONFIG.BUTTON_HOVER_STYLE).forEach(key => {
                btn.style[key] = '';
            });
        });

        return btn;
    };

    // 更新当前课程ID显示
    const updateCurrentCourseId = () => {
        const label = document.getElementById('currentCourseLabel');
        if (label) label.textContent = `当前课程ID: ${currentCourseId || '未设置'}`;
        updatePendingListUI();
    };

    // 点击岗位课程
    const clickPostCourse = () => {
        if (hasClickedPostCourse) return;

        // 首次加载增加延迟
        const delay = isFirstLoad ? CONFIG.FIRST_LOAD_DELAY : 0;

        setTimeout(() => {
            const postCourseElem = document.querySelector(CONFIG.POST_COURSE_SEL);
            if (!postCourseElem) {
                setTimeout(clickPostCourse, CONFIG.CHECK_INTERVAL);
                return;
            }

            try {
                postCourseElem.click();
                hasClickedPostCourse = true;
                startCheckIframe();
                // 首次操作后标记为非首次
                isFirstLoad = false;
            } catch (e) {
                log(`点击岗位课程出错: ${e.message}`);
            }
        }, delay);
    };

    // 开始检查iframe
    const startCheckIframe = () => {
        if (checkIframeTimer) clearInterval(checkIframeTimer);
        checkIframeTimer = setInterval(() => {
            iframeDoc = getIframeDocument();
            if (iframeDoc) {
                clearInterval(checkIframeTimer);
                startUpdateCourseList();
            }
        }, CONFIG.CHECK_INTERVAL);
    };

    // 获取iframe文档
    const getIframeDocument = () => {
        const iframeElem = document.querySelector(CONFIG.IFRAME_SEL);
        if (!iframeElem) return null;

        try {
            const doc = iframeElem.contentDocument || iframeElem.contentWindow.document;
            return doc && doc.querySelector(CONFIG.COURSE_LIST_SEL) ? doc : null;
        } catch (e) {
            log(`访问iframe出错: ${e.message}`);
            return null;
        }
    };

    // 解析课程项
    const parseCourseItem = (itemElem) => {
        const courseData = {
            id: '',
            name: '',
            status: '',
            onClickParams: [],
            isLearned: false,
            isAbnormal: false,
            fullUrls: [],
            originalIndex: -1,
            actualPlayPath: ''
        };

        const titleElem = itemElem.querySelector('.itemTitle');
        if (titleElem) courseData.name = titleElem.textContent.trim().replace(/^\s+|\s+$/g, '');

        const statusElem = itemElem.querySelector('.type p');
        if (statusElem) courseData.status = statusElem.textContent.trim();

        const linkElem = itemElem.querySelector('.itemText');
        if (linkElem && linkElem.hasAttribute('onclick')) {
            const onclickStr = linkElem.getAttribute('onclick');
            const paramReg = /OpenVideoPlay\(\s*'([^']*)'\s*,\s*'([^']*)'\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/;
            const matchRes = onclickStr.match(paramReg);

            if (matchRes && matchRes.length >= 5) {
                courseData.id = matchRes[1];
                courseData.onClickParams = [
                    matchRes[1], matchRes[2], parseInt(matchRes[3]), parseInt(matchRes[4])
                ];

                CONFIG.COURSE_PLAY_PATHS.forEach(path => {
                    courseData.fullUrls.push(`https://jcpxkh.91huayi.com/${path}?courseware_id=${matchRes[1]}`);
                });

                courseData.actualPlayPath = onclickStr.includes('BJYCoursePlay')
                    ? CONFIG.COURSE_PLAY_PATHS[1]
                    : CONFIG.COURSE_PLAY_PATHS[0];
            }
        }

        return courseData;
    };

    // 筛选待播放课程
    const filterPendingCourses = (allCourses) => {
        const validStatus = ['观看中', '未观看'];
        return allCourses.filter(course =>
            validStatus.includes(course.status) &&
            course.id && course.name && course.fullUrls.length > 0
        );
    };

    // 更新课程列表
    const updateCourseList = () => {
        if (!iframeDoc) return;
        const courseElems = iframeDoc.querySelectorAll(CONFIG.COURSE_ITEM_SEL);

        if (courseElems.length === 0) {
            pendingCourses = [];
            syncToLocalStorage();
            updatePendingListUI();
            return;
        }

        let allCourses = Array.from(courseElems).map((elem, index) => {
            const course = parseCourseItem(elem);
            course.originalIndex = index;
            return course;
        });

        const storedCourses = getFromLocalStorage();
        if (storedCourses.length > 0) {
            allCourses = allCourses.map(course => {
                const stored = storedCourses.find(s => s.id === course.id);
                return stored ? {
                    ...course,
                    isLearned: stored.isLearned,
                    isAbnormal: stored.isAbnormal || false
                } : course;
            });
        }

        pendingCourses = filterPendingCourses(allCourses)
            .sort((a, b) => a.originalIndex - b.originalIndex);

        syncToLocalStorage();
        updatePendingListUI();
        updateNextVideo();
        log(`更新课程列表,共${pendingCourses.length}个待播放课程`);

        const isHomeEnv = window.location.href.includes('ExerciseHome/index');
        if (isHomeEnv && !hasAutoJumped && pendingCourses.length > 0) {
            // 首次跳转前等待资源加载
            waitForResources(() => {
                autoJumpToFirstValidCourse();
            });
        }
    };

    // 智能跳转课程:优化首次跳转逻辑
    const smartJumpToCourse = (course, retry = false) => {
        if (isJumping && !retry) return;
        isJumping = true;

        // 计算延迟:首次跳转或重试时增加延迟
        const delay = (isFirstLoad || retry) ? CONFIG.FIRST_JUMP_DELAY : 0;

        // 等待延迟后执行跳转
        setTimeout(() => {
            if (retry) {
                jumpAttempts++;
                if (jumpAttempts > MAX_JUMP_ATTEMPTS) {
                    log(`已尝试${MAX_JUMP_ATTEMPTS}次跳转,标记课程为异常`);
                    markCourseAsAbnormal(course.id);
                    isJumping = false;
                    return;
                }
                log(`第${jumpAttempts}次重试跳转至课程: ${course.name}`);
            } else {
                log(`开始跳转至课程: ${course.name}`);
            }

            try {
                currentCourseId = course.id;
                syncToLocalStorage();
                updateCurrentCourseId();

                const isHomeEnv = window.location.href.includes('ExerciseHome/index');
                const isPlayEnv = window.location.href.includes('courseware_id');

                // 列表页跳转:使用原生函数打开新页面
                if (isHomeEnv && iframeDoc && iframeDoc.defaultView &&
                    typeof iframeDoc.defaultView.OpenVideoPlay === 'function' &&
                    course.onClickParams.length >= 4) {

                    clearAllResources();
                    iframeDoc.defaultView.OpenVideoPlay(...course.onClickParams);
                    setTimeout(() => {
                        closeCurrentPage();
                        isJumping = false;
                        // 首次跳转后标记为非首次
                        isFirstLoad = false;
                    }, CONFIG.PAGE_CLOSE_DELAY / 2);
                    return;
                }

                // 播放页跳转:打开新窗口后关闭原页面
                const baseUrl = `https://jcpxkh.91huayi.com/${course.actualPlayPath}?courseware_id=${course.id}`;
                clearAllResources();

                let newWindow = null;
                if (isPlayEnv) {
                    newWindow = window.open(baseUrl, '_blank');
                    if (!newWindow) throw new Error("浏览器阻止了弹窗,请允许弹出窗口后重试");

                    setTimeout(() => {
                        if (newWindow && !newWindow.closed) {
                            log("新播放窗口已打开,准备关闭原页面");
                            closeCurrentPage();
                        } else {
                            throw new Error("新窗口未成功打开");
                        }
                        isJumping = false;
                        // 首次跳转后标记为非首次
                        isFirstLoad = false;
                    }, CONFIG.PAGE_CLOSE_DELAY);
                } else {
                    window.location.href = baseUrl;
                    isJumping = false;
                    // 首次跳转后标记为非首次
                    isFirstLoad = false;
                }

            } catch (e) {
                log(`跳转处理出错: ${e.message}`);
                setTimeout(() => {
                    smartJumpToCourse(course, true);
                    isJumping = false;
                }, 2000);
            }
        }, delay);
    };

    // 自动跳转至第一个有效课程
    const autoJumpToFirstValidCourse = () => {
        const validCourse = pendingCourses.find(c => !c.isLearned);
        if (!validCourse) {
            log('待播放列表中无有效课程');
            const emptyTip = document.getElementById('huayiEmptyTip');
            if (emptyTip) emptyTip.textContent = '待播放列表中无有效课程(全为已学习)';
            return;
        }

        log(`自动跳转至第一个有效课程: ${validCourse.name}`);
        hasAutoJumped = true;

        const courseItemElem = document.querySelector(`.huayiPendingCourseItem[data-course-id="${validCourse.id}"]`);
        if (courseItemElem) courseItemElem.click();
        else smartJumpToCourse(validCourse);
    };

    // 更新待播放列表UI
    const updatePendingListUI = () => {
        const listContainer = document.getElementById('huayiCourseListContainer');
        const emptyTip = document.getElementById('huayiEmptyTip');

        while (listContainer.children.length > 1) {
            listContainer.removeChild(listContainer.lastChild);
        }

        if (pendingCourses.length === 0) {
            emptyTip.textContent = '暂无待播放课程';
            return;
        }

        emptyTip.style.display = 'none';

        pendingCourses.forEach((course, index) => {
            const courseItem = document.createElement('div');
            courseItem.className = 'huayiPendingCourseItem';
            courseItem.dataset.courseId = course.id;
            courseItem.dataset.actualPath = course.actualPlayPath;

            Object.keys(CONFIG.COURSE_ITEM_STYLE).forEach(key => {
                courseItem.style[key] = CONFIG.COURSE_ITEM_STYLE[key];
            });

            const isCurrentPlaying = course.id === currentCourseId;
            if (isCurrentPlaying) {
                Object.keys(CONFIG.COURSE_ITEM_PLAYING_STYLE).forEach(key => {
                    courseItem.style[key] = CONFIG.COURSE_ITEM_PLAYING_STYLE[key];
                });
            }

            let statusLabel = '';
            if (course.isAbnormal) {
                statusLabel = `<span style="padding:2px 6px;border-radius:4px;font-size:12px;background:#f44336;color:#fff;">异常</span>`;
            } else if (course.isLearned) {
                statusLabel = `<span style="padding:2px 6px;border-radius:4px;font-size:12px;background:#9e9e9e;">已学习</span>`;
            } else {
                statusLabel = `<span style="padding:2px 6px;border-radius:4px;font-size:12px;${
                    course.status === '观看中' ? 'background:#ff9800' : 'background:#2196f3'
                }">${course.status}</span>`;
            }

            const pathLabel = course.actualPlayPath.includes('BJY') ? 'BJY路径' : '普通路径';
            const playIcon = isCurrentPlaying ? '▶️ ' : '';

            courseItem.innerHTML = `
                <span style="display:inline-block;width:20px;text-align:center;">${playIcon}${index + 1}.</span>
                <span style="margin:0 8px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;${
                    course.isLearned ? 'text-decoration:line-through;color:#aaa;' : ''
                }">${course.name}</span>
                <span style="font-size:11px;color:#ccc;">${pathLabel}</span>
                ${statusLabel}
            `;

            courseItem.addEventListener('click', () => {
                // 点击课程时先检查资源
                waitForResources(() => {
                    smartJumpToCourse(course);
                });
            });

            courseItem.addEventListener('mouseover', () => {
                if (isCurrentPlaying) courseItem.style.backgroundColor = 'rgba(66, 133, 244, 0.4)';
                else Object.keys(CONFIG.COURSE_ITEM_HOVER_STYLE).forEach(key => {
                    courseItem.style[key] = CONFIG.COURSE_ITEM_HOVER_STYLE[key];
                });
            });

            courseItem.addEventListener('mouseout', () => {
                if (isCurrentPlaying) {
                    courseItem.style.backgroundColor = CONFIG.COURSE_ITEM_PLAYING_STYLE.backgroundColor;
                } else {
                    Object.keys(CONFIG.COURSE_ITEM_STYLE).forEach(key => {
                        courseItem.style[key] = CONFIG.COURSE_ITEM_STYLE[key];
                    });
                }
            });

            listContainer.appendChild(courseItem);
        });
    };

    // 启动课程列表更新
    const startUpdateCourseList = () => {
        if (updateCourseTimer) clearInterval(updateCourseTimer);
        updateCourseList();
        updateCourseTimer = setInterval(updateCourseList, CONFIG.CHECK_INTERVAL * 2);
    };

    // 获取当前播放状态
    const getCurrentPlayStatus = () => {
        const bplayerWrap = document.querySelector('.bplayer-wrap');
        if (bplayerWrap) return bplayerWrap.classList.contains('bplayer-playing');

        const videoElem = document.querySelector('video');
        return videoElem ? !videoElem.paused : false;
    };

    // 获取bplayer播放按钮
    const getBplayerPlayBtn = () => document.querySelector('.bplayer-play-btn');

    // 确保播放状态
    const ensurePlaying = () => {
        const isPlaying = getCurrentPlayStatus();
        if (!isPlaying) {
            const bplayerBtn = getBplayerPlayBtn();
            const videoElem = document.querySelector('video');

            if (bplayerBtn) bplayerBtn.click();
            if (videoElem) videoElem.play().catch(err => log(`video播放出错: ${err.message}`));
        }
        return getCurrentPlayStatus();
    };

    // 启动自动播放监控
    const startAutoPlayMonitor = () => {
        if (autoPlayIntervalId) return;
        autoPlayIntervalId = setInterval(() => {
            const isPlaying = getCurrentPlayStatus();
            updateStatus(isPlaying);
            if (!isPlaying) ensurePlaying();
        }, CONFIG.AUTO_PLAY_RETRY_INTERVAL_MS);
    };

    // 停止自动播放监控
    const stopAutoPlayMonitor = () => {
        if (autoPlayIntervalId) {
            clearInterval(autoPlayIntervalId);
            autoPlayIntervalId = null;
        }
    };

    // 更新播放状态显示
    const updateStatus = (isPlaying) => {
        const label = document.getElementById('videoStatusLabel');
        if (label) label.textContent = `状态: ${isPlaying ? '正在播放' : '暂停'}`;
    };

    // 更新无进度倒计时显示
    const updateCountdown = (time) => {
        const label = document.getElementById('countdownLabel');
        if (label) {
            label.style.display = isLearnedCountdownActive ? 'none' : 'block';
            if (!isLearnedCountdownActive) label.textContent = `无进度变化: ${time} 秒后切换`;
        }
    };

    // 更新已学习倒计时显示
    const updateLearnedCountdown = (time) => {
        const label = document.getElementById('learnedCountdownLabel');
        if (label) {
            label.style.display = isLearnedCountdownActive ? 'block' : 'none';
            if (isLearnedCountdownActive) label.textContent = `已学完,${time} 秒后切换下一个`;
        }
    };

    // 更新进度显示
    const updateProgress = (progress, currentTime, totalTime) => {
        const label = document.getElementById('videoProgressLabel');
        if (label) {
            label.textContent = `当前进度: ${progress.toFixed(2)}% (${currentTime} / ${totalTime})`;
        }
    };

    // 更新倍速显示
    const updateSpeed = (speed) => {
        const label = document.getElementById('videoSpeedLabel');
        if (label) label.textContent = `播放速度: ${speed.toFixed(1)}×`;
    };

    // 更新下一个视频显示
    const updateNextVideo = () => {
        const label = document.getElementById('nextVideoLabel');
        if (!label) return;

        let nextCourse = null;
        if (currentCourseId && pendingCourses.length > 0) {
            const currentIndex = pendingCourses.findIndex(c => c.id === currentCourseId);
            if (currentIndex !== -1) {
                for (let i = currentIndex + 1; i < pendingCourses.length; i++) {
                    if (!pendingCourses[i].isLearned) {
                        nextCourse = pendingCourses[i];
                        break;
                    }
                }
            }
        }

        if (!nextCourse) nextCourse = pendingCourses.find(c => !c.isLearned);
        label.textContent = nextCourse ? `下一个视频: ${nextCourse.name}` : '下一个视频: 无未学习课程';
    };

    // 检测并设置播放速度
    const detectAndSetPlaybackSpeed = () => {
        if (speedChecked) return;
        const isBPlayer = !!document.querySelector('.bplayer-progress-bar.absolute');
        const isCcH5 = !!document.querySelector('.ccH5ProgressBar');

        if (isBPlayer && !isCcH5) {
            playbackSpeed = CONFIG.BPLAYER_SPEED;
        } else if (isCcH5) {
            playbackSpeed = CONFIG.DEFAULT_SPEED;
        } else return;

        setVideoSpeed(playbackSpeed);
        updateSpeed(playbackSpeed);
        speedChecked = true;
        clearDetectorTimer();
    };

    // 启动倍速检测定时器
    const startDetectorTimer = () => {
        clearDetectorTimer();
        if (speedChecked) return;
        detectorTimerId = setInterval(() => {
            try { detectAndSetPlaybackSpeed(); }
            catch (e) { log(`倍速检测出错: ${e.message}`); }
        }, CONFIG.DETECT_INTERVAL_MS);
    };

    // 清除倍速检测定时器
    const clearDetectorTimer = () => {
        if (detectorTimerId) {
            clearInterval(detectorTimerId);
            detectorTimerId = null;
        }
    };

    // 设置视频倍速
    const setVideoSpeed = (speed) => {
        const videoElem = document.querySelector('video') || document.querySelector('.bplayer-video video');
        if (videoElem) {
            try { videoElem.playbackRate = speed; }
            catch (e) { log(`倍速设置失败: ${e.message}`); }
        }
    };

    // 时间转秒数
    const timeToSeconds = (timeStr) => {
        if (!timeStr) return 0;
        const parts = timeStr.split(':').map(Number);
        return parts.length === 2
            ? parts[0] * 60 + parts[1]
            : parts[0] * 3600 + parts[1] * 60 + parts[2];
    };

    // 重置进度监控
    const resetProgressMonitoring = () => {
        initialProgress = getVideoProgress();
        lastProgress = initialProgress;
        noProgressCount = 0;
        countdownTimeRemaining = CONFIG.MAX_NO_PROGRESS_COUNT;
        updateCountdown(countdownTimeRemaining);
        clearCountdownTimer();
        startCountdownTimer();
    };

    // 启动进度检查定时器
    const startProgressCheckTimer = () => {
        resetProgressMonitoring();
        startupProgressTimerId = setTimeout(() => {
            const currentProgress = getVideoProgress();
            if (Math.abs(currentProgress - initialProgress) < CONFIG.PROGRESS_CHANGE_THRESHOLD) {
                log(`启动进度检查: 进度无变化,准备切换视频`);
                markCourseAsAbnormal(currentCourseId);
                playNextVideo(false);
            }
        }, CONFIG.STARTUP_PROGRESS_CHECK_TIME);
    };

    // 清除进度检查定时器
    const clearProgressCheckTimer = () => {
        if (startupProgressTimerId) {
            clearTimeout(startupProgressTimerId);
            startupProgressTimerId = null;
        }
    };

    // 启动无进度倒计时
    const startCountdownTimer = () => {
        clearCountdownTimer();
        countdownTimerId = setInterval(() => {
            if (!isLearnedCountdownActive && getCurrentPlayStatus()) {
                countdownTimeRemaining--;
                updateCountdown(countdownTimeRemaining);

                if (countdownTimeRemaining <= 0) {
                    log(`倒计时结束,准备切换视频`);
                    markCourseAsAbnormal(currentCourseId);
                    clearCountdownTimer();
                    playNextVideo(false);
                }
            }
        }, 1000);
    };

    // 清除无进度倒计时
    const clearCountdownTimer = () => {
        if (countdownTimerId) {
            clearInterval(countdownTimerId);
            countdownTimerId = null;
        }
    };

    // 启动已学习倒计时
    const startLearnedCountdown = () => {
        clearLearnedCountdownTimer();
        isLearnedCountdownActive = true;
        learnedCountdownTimeRemaining = CONFIG.LEARNED_COUNTDOWN_SECONDS;
        updateLearnedCountdown(learnedCountdownTimeRemaining);
        learnedCountdownTimerId = setInterval(() => {
            learnedCountdownTimeRemaining--;
            updateLearnedCountdown(learnedCountdownTimeRemaining);
            if (learnedCountdownTimeRemaining <= 0) {
                log(`已学完倒计时结束,准备切换视频`);
                clearLearnedCountdownTimer();
                playNextVideo(true);
            }
        }, 1000);
    };

    // 清除已学习倒计时
    const clearLearnedCountdownTimer = () => {
        if (learnedCountdownTimerId) {
            clearInterval(learnedCountdownTimerId);
            learnedCountdownTimerId = null;
        }
        isLearnedCountdownActive = false;
        updateLearnedCountdown(0);
    };

    // 自动播放视频
    const autoPlayVideo = () => {
        // 首次加载增加初始化延迟
        const delay = isFirstLoad ? CONFIG.INITIAL_OPERATION_DELAY : 0;

        setTimeout(() => {
            // 等待资源加载完成后再执行自动播放
            waitForResources(() => {
                const videoElem = document.querySelector('video');
                const bplayerBtn = getBplayerPlayBtn();
                const isPaused = !getCurrentPlayStatus();

                if (isPaused) {
                    if (bplayerBtn) bplayerBtn.click();
                    if (videoElem) videoElem.play().catch(err => log(`自动播放出错: ${err.message}`));
                }

                if (CONFIG.AUTO_MUTE_ON_PLAY && !isGlobalMuted && !userMuteToggle) {
                    muteAttempts = 0;
                    ensureMuted();
                }

                detectAndSetPlaybackSpeed();
                updateStatus(getCurrentPlayStatus());
                updateSpeed(playbackSpeed);
                updateNextVideo();
                startCountdownTimer();
                startProgressCheckTimer();
                startAutoPlayMonitor();

                // 首次播放后标记为非首次
                isFirstLoad = false;
            });
        }, delay);
    };

    // 播放下一个视频
    const playNextVideo = (shouldMarkAsLearned) => {
        clearAllResources();
        stopAutoPlayMonitor();
        clearCountdownTimer();
        clearProgressCheckTimer();

        // 无论是否异常,只要完成学习就标记为已学习
        if (shouldMarkAsLearned && currentCourseId) {
            markCurrentCourseAsLearned();
        }

        const unlearnedCourses = pendingCourses.filter(c => !c.isLearned);
        let nextCourse = null;

        if (unlearnedCourses.length > 0) {
            const currentIndex = unlearnedCourses.findIndex(c => c.id === currentCourseId);
            if (currentIndex !== -1 && currentIndex + 1 < unlearnedCourses.length) {
                nextCourse = unlearnedCourses[currentIndex + 1];
            } else {
                nextCourse = unlearnedCourses[0];
            }
        }

        if (!nextCourse) {
            log('没有可播放的未学习课程');
            isJumping = false;
            return;
        }

        log(`准备跳转至下一课,将关闭当前页面`);
        smartJumpToCourse(nextCourse);
    };

    // 处理视频结束事件
    const handleVideoEnded = () => {
        log(`视频播放结束`);
        // 视频自然结束时,强制标记为已学习,无论是否异常
        playNextVideo(true);
    };

    // 绑定视频结束事件
    const bindVideoEndedHandler = () => {
        const videoElem = document.querySelector('video');
        if (videoElem) {
            videoElem.removeEventListener('ended', handleVideoEnded);
            videoElem.addEventListener('ended', handleVideoEnded);
        }
    };

    // 绑定列表点击事件
    const bindListClickHandler = () => {
        const listGroup = document.querySelector('div.listGroup');
        if (listGroup) {
            listGroup.addEventListener('click', (e) => {
                if (e.target?.classList.contains('text')) {
                    speedChecked = false;
                    startDetectorTimer();
                    clearProgressCheckTimer();
                    clearLearnedCountdownTimer();
                    setTimeout(updateNextVideo, 500);
                }
            });
        }
    };

    // 监控视频进度
    const monitorVideoProgress = () => {
        if (isJumping) return;

        const isPlaying = getCurrentPlayStatus();
        if (!isPlaying) {
            log("状态面板显示暂停,自动恢复播放");
            ensurePlaying();
            updateStatus(getCurrentPlayStatus());
        }

        if (CONFIG.AUTO_MUTE_ON_PLAY && isPlaying && !isGlobalMuted && !userMuteToggle) {
            ensureMuted();
        }

        if (isLearnedCountdownActive) return;

        // 检查是否已学完(即使是异常课程也会检查)
        const currentProgress = getVideoProgress();
        const isLearned = currentProgress >= 99.9;

        if (isLearned) {
            if (!isLearnedCountdownActive) {
                log(`检测到课程已学完(进度: ${currentProgress.toFixed(2)}%),即使标记为异常也会标记为已学习`);
                clearCountdownTimer();
                startLearnedCountdown();
            }
            return;
        } else if (isLearnedCountdownActive) {
            clearLearnedCountdownTimer();
        }

        // 获取进度信息并更新UI
        let currentTimeElem, totalTimeElem;
        const durationContainer = document.querySelector('.text.duration');
        if (durationContainer) {
            currentTimeElem = durationContainer.querySelector('.played-time');
            totalTimeElem = durationContainer.querySelector('.total-time');
        }
        if (!currentTimeElem || !totalTimeElem) {
            currentTimeElem = document.querySelector('.ccH5TimeCurrent');
            totalTimeElem = document.querySelector('.ccH5TimeTotal');
        }

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

        if (currentTimeElem && totalTimeElem) {
            const currentTime = currentTimeElem.textContent.trim();
            const totalTime = totalTimeElem.textContent.trim();
            const currentSec = timeToSeconds(currentTime);
            const totalSec = timeToSeconds(totalTime);

            if (totalSec > 0) {
                updateProgress(currentProgress, currentTime, totalTime);

                if (currentProgress >= 99.9 && !hasPlayedNext) {
                    hasPlayedNext = true;
                    playNextVideo(true);
                }
                if (currentProgress < 99.9) hasPlayedNext = false;
            }
        } else if (videoElem) {
            const cur = videoElem.currentTime || 0;
            const total = videoElem.duration || 0;
            if (total > 0) {
                const curStr = new Date(cur * 1000).toISOString().substr(11, 8);
                const totalStr = new Date(total * 1000).toISOString().substr(11, 8);
                updateProgress(currentProgress, curStr, totalStr);

                if (currentProgress >= 99.9 && !hasPlayedNext) {
                    hasPlayedNext = true;
                    playNextVideo(true);
                }
                if (currentProgress < 99.9) hasPlayedNext = false;
            }
        }

        // 检查进度变化,更新无进度计数器
        if (Math.abs(currentProgress - lastProgress) >= CONFIG.PROGRESS_CHANGE_THRESHOLD) {
            lastProgress = currentProgress;
            noProgressCount = 0;
            countdownTimeRemaining = CONFIG.MAX_NO_PROGRESS_COUNT;
            updateCountdown(countdownTimeRemaining);
        } else {
            noProgressCount++;
            if (noProgressCount === CONFIG.PROGRESS_STALL_RETRY_SECONDS) {
                log(`进度已${noProgressCount}秒无变化,尝试恢复播放`);
                ensurePlaying();
                updateStatus(getCurrentPlayStatus());
            }
        }

        // 更新UI状态
        updateStatus(isPlaying);
        if (!isPlaying) ensurePlaying();
        updateSpeed(playbackSpeed);
        if (isPlaying && speedChecked && videoElem) setVideoSpeed(playbackSpeed);
        updateNextVideo();
    };

    // 获取视频进度
    const getVideoProgress = () => {
        const videoElem = document.querySelector('video');
        const currentTimeElem = document.querySelector('.text.duration .played-time') || document.querySelector('.ccH5TimeCurrent');
        const totalTimeElem = document.querySelector('.text.duration .total-time') || document.querySelector('.ccH5TimeTotal');

        if (currentTimeElem && totalTimeElem) {
            const currentSec = timeToSeconds(currentTimeElem.textContent.trim());
            const totalSec = timeToSeconds(totalTimeElem.textContent.trim());
            return totalSec > 0 ? (currentSec / totalSec) * 100 : 0;
        } else if (videoElem) {
            return videoElem.duration > 0 ? (videoElem.currentTime / videoElem.duration) * 100 : 0;
        }
        return 0;
    };

    // 启动监控
    const startMonitoring = () => {
        createMainPanel();
        autoPlayVideo();
        setInterval(monitorVideoProgress, 1000);
        setInterval(bindVideoEndedHandler, 2000);
        startDetectorTimer();
        bindListClickHandler();
    };

    // 标记当前课程为已学习(即使是异常课程也能被标记)
    const markCurrentCourseAsLearned = () => {
        if (!currentCourseId) return;

        const curCourseIndex = pendingCourses.findIndex(c => c.id === currentCourseId);
        if (curCourseIndex === -1) return;

        // 无论是否为异常课程,只要调用此方法就标记为已学习
        if (!pendingCourses[curCourseIndex].isLearned) {
            pendingCourses[curCourseIndex].isLearned = true;
            // 标记为已学习后清除异常标记
            pendingCourses[curCourseIndex].isAbnormal = false;
            syncToLocalStorage();
            updatePendingListUI();
            updateNextVideo();
            log(`标记课程 ${currentCourseId} 为已学习(即使之前标记为异常)`);
        }
    };

    // 同步到本地存储
    const syncToLocalStorage = () => {
        localStorage.setItem('huayiPendingCourses', JSON.stringify(pendingCourses));
        localStorage.setItem('huayiCurrentCourseId', currentCourseId || '');
        localStorage.setItem('huayiCourseListExpanded', isCourseListExpanded);
    };

    // 从本地存储获取
    const getFromLocalStorage = () => {
        const stored = localStorage.getItem('huayiPendingCourses');
        return stored ? JSON.parse(stored) : [];
    };

    // 初始化函数:记录页面加载开始时间
    const init = () => {
        // 记录页面加载开始时间,用于控制最小加载时间
        pageLoadStartTime = Date.now();
        pendingCourses = getFromLocalStorage();
        currentCourseId = localStorage.getItem('huayiCurrentCourseId') || '';
        hasAutoJumped = false;
        resourceCheckAttempts = 0;

        createMainPanel();

        const isHomePage = window.location.href.includes('ExerciseHome/index');
        const isPlayPage = window.location.href.includes('courseware_id');

        if (isHomePage) {
            // 首次加载列表页增加延迟
            const delay = isFirstLoad ? CONFIG.FIRST_LOAD_DELAY : CONFIG.INIT_DELAY;
            setTimeout(() => {
                // 等待资源加载完成后再执行
                waitForResources(() => {
                    clickPostCourse();
                    startUpdateCourseList();
                });
            }, delay);
        } else if (isPlayPage) {
            // 首次加载播放页增加延迟
            const delay = isFirstLoad ? CONFIG.FIRST_LOAD_DELAY : 3000;
            setTimeout(() => {
                // 等待资源加载完成后再执行
                waitForResources(() => {
                    startMonitoring();
                    isJumping = false;
                    if (CONFIG.AUTO_MUTE_ON_PLAY && !userMuteToggle) {
                        setTimeout(ensureMuted, 1000);
                    }
                });
            }, delay);

            const urlParams = new URLSearchParams(window.location.search);
            const urlCourseId = urlParams.get('courseware_id');
            if (urlCourseId && !currentCourseId) {
                currentCourseId = urlCourseId;
                syncToLocalStorage();
                updateCurrentCourseId();
            }
            updatePendingListUI();
        }

        window.addEventListener('beforeunload', clearAllResources);
    };

    init();
})();