ENAEA自动刷课助手

中国教育干部网络学院(enaea.edu.cn)自动刷课工具 - 自动连续刷课、多页检测、倍速播放、自动静音、智能跳转

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         ENAEA自动刷课助手
// @namespace    https://greasyfork.org/
// @version      1.0
// @description  中国教育干部网络学院(enaea.edu.cn)自动刷课工具 - 自动连续刷课、多页检测、倍速播放、自动静音、智能跳转
// @author       Liontooth
// @match        https://study.enaea.edu.cn/*
// @match        https://*.ttcdw.cn/*
// @match        https://*.ertcloud.net/*
// @grant        none
// @run-at       document-start
// @license      MIT
// @homepage     https://github.com/chnlion/enaea-auto-study
// @supportURL   https://github.com/chnlion/enaea-auto-study/issues
// ==/UserScript==

(function() {
    'use strict';

    console.log('🚀 ENAEA自动刷课助手已启动');

    // ==================== 配置项 ====================
    let TARGET_SPEED = parseInt(localStorage.getItem('enaea_target_speed')) || 4;
    let AUTO_MUTE = localStorage.getItem('enaea_auto_mute') !== 'false';
    let AUTO_JUMP = true; // 播放页自动跳转到未完成课程
    let AUTO_SELECT_COURSE = true; // 列表页自动选择未完成课程
    let AUTO_CONTINUOUS = localStorage.getItem('enaea_auto_continuous') !== 'false'; // 自动连续刷课
    let CHECK_INTERVAL = parseInt(localStorage.getItem('enaea_check_interval')) || 15; // 检测间隔(秒)
    let MAX_CONTINUOUS_COUNT = 50; // 最大连续刷课次数
    
    let processedVideos = new WeakSet();
    let checkTimer = null;
    let lastCheckTime = 0;

    // ==================== 核心功能:劫持播放速度 ====================
    
    function hijackPlaybackRate() {
        const originalDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate');
        Object.defineProperty(HTMLMediaElement.prototype, 'playbackRate', {
            get: function() {
                return originalDescriptor.get.call(this);
            },
            set: function(value) {
                originalDescriptor.set.call(this, TARGET_SPEED);
                console.log(`🎯 拦截并强制设置播放速度为${TARGET_SPEED}倍速`);
            },
            configurable: true
        });
    }

    function setVideoSpeed(video) {
        if (!video || processedVideos.has(video)) return false;
        try {
            video.playbackRate = TARGET_SPEED;
            processedVideos.add(video);
            console.log(`✅ 视频播放速度已设置为${TARGET_SPEED}倍速`);
            if (AUTO_MUTE) {
                video.muted = true;
                video.volume = 0;
                console.log('🔇 视频已静音');
            }
            return true;
        } catch (e) {
            console.error('❌ 设置视频速度失败:', e);
            return false;
        }
    }

    function setAllVideos() {
        const videos = document.querySelectorAll('video');
        let count = 0;
        videos.forEach(video => {
            if (setVideoSpeed(video)) {
                count++;
            }
        });
        if (count > 0) {
            console.log(`🎬 找到并设置了 ${count} 个视频`);
        }
    }

    // ==================== MutationObserver监控新增视频 ====================
    
    function startObserver() {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                mutation.addedNodes.forEach((node) => {
                    if (node.tagName === 'VIDEO') {
                        setVideoSpeed(node);
                    } else if (node.querySelectorAll) {
                        const videos = node.querySelectorAll('video');
                        videos.forEach(video => setVideoSpeed(video));
                    }
                });
            });
        });

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

        console.log('👀 视频监控器已启动');
    }

    // ==================== 播放页:自动识别并跳转到未完成课程 ====================
    
    function findAndJumpToUnfinishedCourse() {
        console.log('🔍 正在查找未完成的课程...');
        console.log('📍 当前URL:', window.location.href);
        console.log('📍 document.readyState:', document.readyState);
        console.log('📍 document.body存在:', !!document.body);
        
        let allCourses = [];
        
        if (!document.body) {
            console.log('⚠️ 页面body还未加载,延迟1秒后重试...');
            setTimeout(findAndJumpToUnfinishedCourse, 1000);
            return false;
        }
        
        let courseContents = document.querySelectorAll('.cvtb-MCK-course-content, .cvtb-NCK-course-content');
        console.log(`📌 方法1:找到 ${courseContents.length} 个课程元素`);
        
        if (courseContents.length === 0) {
            courseContents = document.querySelectorAll('[class*="course-content"]');
            console.log(`📌 方法2:找到 ${courseContents.length} 个包含 course-content 的元素`);
        }
        
        if (courseContents.length === 0) {
            courseContents = document.querySelectorAll('li');
            console.log(`📌 方法3:找到 ${courseContents.length} 个 li 元素`);
        }
        
        courseContents.forEach((item, index) => {
            try {
                const progressElement = item.querySelector('.cvtb-MCK-CsCt-studyProgress, .cvtb-NCK-CsCt-studyProgress');
                const titleElement = item.querySelector('.cvtb-MCK-CsCt-title, .cvtb-NCK-CsCt-title, [class*="title"]');
                
                if (!progressElement || !titleElement) {
                    return;
                }
                
                const progressText = progressElement.textContent.trim();
                const progressMatch = progressText.match(/(\d+)%/);
                const progress = progressMatch ? parseInt(progressMatch[1]) : 0;
                const title = titleElement.textContent.trim() || `课程${index + 1}`;
                const linkElement = item.querySelector('a, [onclick], .cvtb-MCK-CsCt-title, .cvtb-NCK-CsCt-title') || item;
                
                if (!linkElement) return;
                
                allCourses.push({
                    element: item,
                    title: title,
                    progress: progress,
                    link: linkElement,
                    index: allCourses.length + 1
                });
            } catch (e) {
                console.error(`❌ 解析课程 ${index + 1} 时出错:`, e);
            }
        });
        
        if (allCourses.length === 0) {
            console.log('⚠️ 未找到任何课程');
            return false;
        }
        
        console.log(`📚 共找到 ${allCourses.length} 门课程`);
        
        const unfinishedCourse = allCourses.find(course => course.progress < 100);
        
        if (!unfinishedCourse) {
            console.log('🎉 当前课程所有视频都已完成!');
            return false;
        }
        
        console.log(`✅ 找到未完成视频: "${unfinishedCourse.title}" (${unfinishedCourse.progress}%)`);
        
        unfinishedCourse.element.style.outline = '3px solid rgb(74, 222, 128)';
        unfinishedCourse.element.style.outlineOffset = '2px';
        unfinishedCourse.element.style.transition = 'all 0.3s ease';
        
        setTimeout(() => {
            console.log(`🚀 正在跳转到: ${unfinishedCourse.title}`);
            try {
                unfinishedCourse.link.click();
            } catch (e) {
                console.error('❌ 点击失败:', e);
            }
        }, 500);
        
        return true;
    }
    
    function autoJumpOnLoadInVideoPage() {
        if (!AUTO_JUMP) {
            console.log('⏸️ 自动跳转功能已关闭');
            return;
        }
        
        if (window.self !== window.top) {
            console.log('⏸️ 当前在iframe中,跳过自动跳转');
            return;
        }
        
        const url = window.location.href;
        if (!url.includes('study.enaea.edu.cn')) {
            console.log('⏸️ 当前不在study.enaea.edu.cn域名,跳过自动跳转');
            return;
        }
        
        console.log('⏳ 将在3秒、5秒、8秒后尝试自动查找未完成课程...');
        
        let jumpSuccess = false;
        
        function tryAutoFind(attemptNum) {
            if (jumpSuccess) {
                console.log(`⏭️ 第${attemptNum}次尝试取消(已成功跳转)`);
                return;
            }
            
            console.log(`🤖 第${attemptNum}次自动执行"查找未完成课程"功能...`);
            
            const courseElements = document.querySelectorAll('.cvtb-MCK-course-content, .cvtb-NCK-course-content');
            console.log(`   预检查:找到 ${courseElements.length} 个课程元素`);
            
            if (courseElements.length > 0) {
                console.log('✅ 找到课程列表,准备分析并跳转到未完成课程...');
            }
            
            const result = findAndJumpToUnfinishedCourse();
            
            if (result === true) {
                jumpSuccess = true;
                console.log('🎉 自动跳转成功,后续尝试已取消');
            }
        }
        
        setTimeout(() => {
            console.log('⏰ 第1次尝试(页面加载后3秒)');
            tryAutoFind(1);
        }, 3000);
        
        setTimeout(() => {
            console.log('⏰ 第2次尝试(页面加载后5秒)');
            tryAutoFind(2);
        }, 5000);
        
        setTimeout(() => {
            console.log('⏰ 第3次尝试(页面加载后8秒)');
            tryAutoFind(3);
        }, 8000);
    }

    // ==================== 播放页:定时检测所有视频进度 ====================
    
    function checkAllVideosCompleted() {
        // 只在课程播放页执行
        const url = window.location.href;
        if (!url.includes('viewerforccvideo.do') && !url.includes('viewerforicourse.do')) {
            return;
        }
        
        if (!AUTO_CONTINUOUS) {
            return;
        }
        
        // 避免频繁检测
        const now = Date.now();
        if (now - lastCheckTime < CHECK_INTERVAL * 1000 - 1000) {
            return;
        }
        lastCheckTime = now;
        
        console.log('═══════════════════════════════════════');
        console.log(`🔍 检测课程完成状态 (间隔${CHECK_INTERVAL}秒)`);
        
        const courseContents = document.querySelectorAll('.cvtb-MCK-course-content, .cvtb-NCK-course-content');
        
        if (courseContents.length === 0) {
            console.log('⚠️ 未找到课程列表元素');
            return;
        }
        
        console.log(`📊 找到 ${courseContents.length} 个课程视频`);
        
        let allCompleted = true;
        let completedCount = 0;
        let courseDetails = [];
        
        courseContents.forEach((item, index) => {
            try {
                const progressElement = item.querySelector('.cvtb-MCK-CsCt-studyProgress, .cvtb-NCK-CsCt-studyProgress');
                const titleElement = item.querySelector('.cvtb-MCK-CsCt-title, .cvtb-NCK-CsCt-title, [class*="title"]');
                
                if (!progressElement) return;
                
                const progressText = progressElement.textContent.trim();
                const progressMatch = progressText.match(/(\d+)%/);
                const progress = progressMatch ? parseInt(progressMatch[1]) : 0;
                const title = titleElement ? titleElement.textContent.trim() : `视频${index + 1}`;
                
                courseDetails.push({ title, progress });
                
                if (progress === 100) {
                    completedCount++;
                } else {
                    allCompleted = false;
                }
            } catch (e) {
                console.error(`❌ 解析视频 ${index + 1} 时出错:`, e);
            }
        });
        
        console.log(`📈 完成进度: ${completedCount}/${courseContents.length}`);
        courseDetails.forEach((detail, idx) => {
            const status = detail.progress === 100 ? '✅' : '⏳';
            console.log(`   ${status} ${idx + 1}. ${detail.title}: ${detail.progress}%`);
        });
        
        if (allCompleted && courseContents.length > 0) {
            console.log('🎉🎉🎉 当前课程所有视频已完成!');
            console.log('📨 准备发送完成信号到列表页...');
            
            // 发送完成信号
            const signal = {
                timestamp: Date.now(),
                courseUrl: window.location.href,
                totalVideos: courseContents.length
            };
            
            localStorage.setItem('enaea_course_completed_signal', JSON.stringify(signal));
            
            // 增加连续刷课计数
            let count = parseInt(localStorage.getItem('enaea_continuous_count') || '0');
            count++;
            localStorage.setItem('enaea_continuous_count', count.toString());
            
            console.log(`✅ 完成信号已发送!这是第 ${count} 门连续完成的课程`);
            
            // 停止检测定时器
            if (checkTimer) {
                clearInterval(checkTimer);
                checkTimer = null;
                console.log('⏸️ 已停止课程完成检测定时器');
            }
        } else {
            console.log(`⏳ 课程尚未完成,将在 ${CHECK_INTERVAL} 秒后再次检测`);
        }
    }
    
    function startCourseCompletionCheck() {
        const url = window.location.href;
        if (!url.includes('viewerforccvideo.do') && !url.includes('viewerforicourse.do')) {
            return;
        }
        
        if (!AUTO_CONTINUOUS) {
            console.log('⏸️ 自动连续刷课功能已关闭');
            return;
        }
        
        console.log(`🔄 启动课程完成检测 (间隔: ${CHECK_INTERVAL}秒)`);
        
        // 清除旧的定时器
        if (checkTimer) {
            clearInterval(checkTimer);
        }
        
        // 启动新的定时器
        checkTimer = setInterval(checkAllVideosCompleted, CHECK_INTERVAL * 1000);
        
        // 立即执行一次(延迟10秒,等页面加载)
        setTimeout(checkAllVideosCompleted, 10000);
    }

    // ==================== 列表页:自动选择未完成课程 ====================
    
    function findAndClickUnfinishedCourseInList() {
        console.log('═══════════════════════════════════════');
        console.log('📋 正在课程列表页面查找未完成的课程...');
        
        let url = '';
        try {
            url = window.location.href;
            console.log('📍 当前URL:', url);
        } catch (e) {
            console.log('⚠️ 无法获取URL');
        }
        
        if (!document.body) {
            console.log('⚠️ 页面body还未加载,延迟1秒后重试...');
            setTimeout(findAndClickUnfinishedCourseInList, 1000);
            return false;
        }
        
        const table = document.querySelector('#J_myOptionRecords');
        console.log('🔍 查找表格 #J_myOptionRecords:', table ? '找到' : '未找到');
        
        if (!table) {
            console.log('⚠️ 未找到课程列表表格,页面可能还在加载');
            return false;
        }
        
        const allRows = document.querySelectorAll('#J_myOptionRecords tbody tr');
        console.log(`📊 找到 ${allRows.length} 行数据`);
        
        let allCourses = [];
        
        allRows.forEach((row, index) => {
            try {
                const categoryTitle = row.querySelector('td[colspan="6"]');
                if (categoryTitle) {
                    console.log(`📂 分类: ${categoryTitle.textContent.trim()}`);
                    return;
                }
                
                const progressElement = row.querySelector('.progressvalue');
                if (!progressElement) {
                    return;
                }
                
                const progressText = progressElement.textContent.trim();
                const progressMatch = progressText.match(/(\d+)%/);
                const progress = progressMatch ? parseInt(progressMatch[1]) : 0;
                
                const titleElement = row.querySelector('.course-title');
                const title = titleElement ? titleElement.getAttribute('title') || titleElement.textContent.trim() : `课程${index}`;
                
                const learnButton = row.querySelector('a.golearn');
                
                if (!learnButton) {
                    return;
                }
                
                allCourses.push({
                    row: row,
                    title: title,
                    progress: progress,
                    button: learnButton,
                    index: allCourses.length + 1
                });
                
                console.log(`✅ 课程 ${allCourses.length}: "${title}" - 进度: ${progress}%`);
            } catch (e) {
                console.error(`❌ 解析行 ${index + 1} 时出错:`, e);
            }
        });
        
        if (allCourses.length === 0) {
            console.log('⚠️ 未找到任何课程');
            return false;
        }
        
        console.log(`\n📚 共找到 ${allCourses.length} 门课程`);
        
        const unfinishedCourse = allCourses.find(course => course.progress < 100);
        
        if (!unfinishedCourse) {
            // 当前页全部完成,检查是否有下一页
            console.log('✅ 当前页所有课程已完成');
            
            // 获取分页信息
            const nextBtn = document.querySelector('#J_myOptionRecords_next');
            const isNextDisabled = nextBtn && nextBtn.classList.contains('paginate_button_disabled');
            
            if (nextBtn && !isNextDisabled) {
                // 有下一页,获取当前页码信息
                const activePageBtn = document.querySelector('.paginate_active');
                const currentPage = activePageBtn ? activePageBtn.textContent.trim() : '?';
                
                console.log(`📄 当前第 ${currentPage} 页已完成,准备翻到下一页...`);
                
                // 点击下一页按钮
                nextBtn.click();
                
                console.log('⏳ 等待页面加载(2秒)...');
                
                // 等待页面加载后继续检测
                setTimeout(() => {
                    console.log('🔄 页面加载完成,继续检测下一页...');
                    findAndClickUnfinishedCourseInList();
                }, 2000);
                
                return true; // 返回true表示正在处理
            } else {
                // 没有下一页了,真的全部完成
                console.log('🎉🎉🎉 太棒了!所有页面的课程都已完成 100%!');
                console.log('🏆 学习任务全部完成!');
                
                // 清除连续刷课计数
                localStorage.removeItem('enaea_continuous_count');
                
                // 弹窗提示
                alert('🎉 恭喜!所有课程已完成!\n\n所有页面的课程已全部学习完毕。');
                
                return false;
            }
        }
        
        console.log(`\n✅ 找到未完成课程: "${unfinishedCourse.title}" (${unfinishedCourse.progress}%)`);
        console.log(`🎯 这是第 ${unfinishedCourse.index} 门课程,即将打开...`);
        
        unfinishedCourse.row.style.backgroundColor = 'rgba(74, 222, 128, 0.2)';
        unfinishedCourse.row.style.transition = 'all 0.3s ease';
        
        setTimeout(() => {
            console.log(`🚀 正在打开课程: ${unfinishedCourse.title}`);
            try {
                unfinishedCourse.button.click();
                return true;
            } catch (e) {
                console.error('❌ 点击失败:', e);
                const vurl = unfinishedCourse.button.getAttribute('data-vurl');
                if (vurl) {
                    console.log('🔄 尝试直接跳转到:', vurl);
                    window.location.href = vurl;
                    return true;
                }
            }
            return false;
        }, 1000);
        
        return true;
    }
    
    function autoSelectCourseInList() {
        if (!AUTO_SELECT_COURSE) {
            console.log('⏸️ 列表页自动选课功能已关闭');
            return;
        }
        
        let isInIframe = false;
        try {
            isInIframe = (window.self !== window.top);
        } catch (e) {
            isInIframe = true;
        }
        
        if (isInIframe) {
            console.log('⏸️ 当前在iframe中,跳过列表页自动选课');
            return;
        }
        
        let url = '';
        try {
            url = window.location.href;
        } catch (e) {
            console.log('⚠️ 无法获取当前URL');
            return;
        }
        
        console.log('🔍 列表页自动选课检测:');
        console.log('  当前URL:', url);
        
        if (url.includes('viewerforccvideo.do') || url.includes('viewerforicourse.do')) {
            console.log('⏸️ 当前在视频播放页面,跳过列表页自动选课');
            return;
        }
        
        if (!url.includes('study.enaea.edu.cn')) {
            console.log('⏸️ 当前不在study.enaea.edu.cn域名,跳过列表页自动选课');
            return;
        }
        
        console.log('✅ 页面检测通过,将在3秒、5秒、8秒后尝试在列表页自动选择未完成课程...');
        
        let selectSuccess = false;
        
        function tryAutoSelect(attemptNum) {
            if (selectSuccess) {
                console.log(`⏭️ 第${attemptNum}次尝试取消(已成功选择)`);
                return;
            }
            
            console.log(`🤖 第${attemptNum}次自动执行"列表页选课"功能...`);
            
            const table = document.querySelector('#J_myOptionRecords');
            if (!table) {
                console.log('⚠️ 未找到课程列表表格');
                return;
            }
            
            console.log('✅ 找到课程列表表格,准备分析...');
            
            const result = findAndClickUnfinishedCourseInList();
            
            if (result === true) {
                selectSuccess = true;
                console.log('🎉 列表页自动选课成功,后续尝试已取消');
            }
        }
        
        setTimeout(() => {
            console.log('⏰ 第1次尝试(页面加载后3秒)');
            tryAutoSelect(1);
        }, 3000);
        
        setTimeout(() => {
            console.log('⏰ 第2次尝试(页面加载后5秒)');
            tryAutoSelect(2);
        }, 5000);
        
        setTimeout(() => {
            console.log('⏰ 第3次尝试(页面加载后8秒)');
            tryAutoSelect(3);
        }, 8000);
    }

    // ==================== 列表页:监听课程完成信号 ====================
    
    function setupStorageListener() {
        const url = window.location.href;
        
        // 只在列表页监听
        if (url.includes('viewerforccvideo.do') || url.includes('viewerforicourse.do')) {
            return;
        }
        
        if (!AUTO_CONTINUOUS) {
            console.log('⏸️ 自动连续刷课功能已关闭,不监听完成信号');
            return;
        }
        
        console.log('👂 开始监听课程完成信号...');
        
        // 监听 storage 事件
        window.addEventListener('storage', (e) => {
            if (e.key === 'enaea_course_completed_signal') {
                console.log('═══════════════════════════════════════');
                console.log('📨 收到课程完成信号!');
                
                try {
                    const signal = JSON.parse(e.newValue);
                    console.log('📊 信号详情:', signal);
                    
                    handleCourseCompleted();
                } catch (err) {
                    console.error('❌ 解析信号失败:', err);
                }
            }
        });
        
        // 兜底:定时检查信号(每5秒)
        setInterval(() => {
            const signal = localStorage.getItem('enaea_course_completed_signal');
            if (signal) {
                try {
                    const data = JSON.parse(signal);
                    // 检查信号是否是最近10秒内的(避免处理旧信号)
                    if (Date.now() - data.timestamp < 10000) {
                        console.log('📨 定时检查到课程完成信号');
                        handleCourseCompleted();
                    }
                } catch (e) {
                    // 忽略解析错误
                }
            }
        }, 5000);
    }
    
    function handleCourseCompleted() {
        if (!AUTO_CONTINUOUS) {
            console.log('⏸️ 自动连续刷课功能已关闭');
            return;
        }
        
        // 检查连续刷课次数
        let count = parseInt(localStorage.getItem('enaea_continuous_count') || '0');
        console.log(`📊 当前连续刷课计数: ${count}`);
        
        if (count >= MAX_CONTINUOUS_COUNT) {
            console.log(`⚠️ 已连续刷课 ${count} 门课程,达到上限 ${MAX_CONTINUOUS_COUNT}`);
            alert(`已连续自动刷课 ${count} 门课程!\n\n为了安全,自动刷课已暂停。\n请检查学习进度,如需继续请手动开启。`);
            AUTO_CONTINUOUS = false;
            localStorage.setItem('enaea_auto_continuous', 'false');
            // 更新控制面板
            const checkbox = document.getElementById('auto-continuous');
            if (checkbox) checkbox.checked = false;
            return;
        }
        
        console.log('⏳ 等待2秒后刷新列表页...');
        console.log('💡 刷新后将自动选择下一门未完成的课程');
        
        // 清除完成信号(避免重复处理)
        localStorage.removeItem('enaea_course_completed_signal');
        
        // 延迟2秒后刷新页面
        setTimeout(() => {
            console.log('🔄 正在刷新页面...');
            location.reload();
        }, 2000);
    }

    // ==================== 自动点击继续学习 ====================
    
    function autoClickContinue() {
        setInterval(() => {
            const continueBtn = document.querySelector('.el-dialog__footer button.el-button--primary');
            if (continueBtn && continueBtn.textContent.includes('继续')) {
                console.log('🔄 检测到"继续学习"按钮,自动点击');
                continueBtn.click();
            }
        }, 2000);
    }

    // ==================== 监听事件 ====================
    
    function setupEventListeners() {
        document.addEventListener('play', function(e) {
            if (e.target.tagName === 'VIDEO') {
                setVideoSpeed(e.target);
            }
        }, true);

        document.addEventListener('loadedmetadata', function(e) {
            if (e.target.tagName === 'VIDEO') {
                setVideoSpeed(e.target);
            }
        }, true);
    }

    function checkIframes() {
        const iframes = document.querySelectorAll('iframe');
        if (iframes.length === 0) return;
        
        iframes.forEach((iframe, index) => {
            try {
                const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
                if (iframeDoc) {
                    const videos = iframeDoc.querySelectorAll('video');
                    videos.forEach(video => setVideoSpeed(video));
                }
            } catch (e) {
                // 静默处理跨域错误
            }
        });
    }

    // ==================== 浮动控制面板 ====================
    
    function createPanel() {
        // 判断当前页面类型
        const url = window.location.href;
        const isVideoPage = url.includes('viewerforccvideo.do') || url.includes('viewerforicourse.do');
        const pageType = isVideoPage ? '播放页' : '列表页';
        
        const panel = document.createElement('div');
        panel.id = 'enaea-control-panel';
        
        // 根据页面类型生成不同的面板内容
        let panelContent = '';
        
        if (isVideoPage) {
            // ========== 播放页面板 ==========
            panelContent = `
            <div class="panel-header" id="panel-header">
                <span style="font-weight: bold; font-size: 13px;">🎓 ENAEA自动刷课助手 v1.0</span>
                <button id="panel-minimize" style="background: none; border: none; color: white; cursor: pointer; font-size: 18px; padding: 0 5px;">−</button>
            </div>
            <div class="panel-content" id="panel-content">
                <div class="control-group" style="text-align: center; font-size: 11px; color: rgba(255,255,255,0.7); margin-bottom: 10px;">
                    📺 当前:播放页
                </div>
                <div class="control-group">
                    <label>
                        倍速:
                        <select id="speed-select">
                            <option value="1" ${TARGET_SPEED === 1 ? 'selected' : ''}>1x</option>
                            <option value="2" ${TARGET_SPEED === 2 ? 'selected' : ''}>2x</option>
                            <option value="4" ${TARGET_SPEED === 4 ? 'selected' : ''}>4x</option>
                        </select>
                    </label>
                </div>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="auto-mute" ${AUTO_MUTE ? 'checked' : ''}>
                        自动静音
                    </label>
                </div>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="auto-jump" ${AUTO_JUMP ? 'checked' : ''}>
                        播放页自动跳转
                    </label>
                </div>
                <div class="control-group" style="border-top: 1px solid rgba(255,255,255,0.2); padding-top: 10px; margin-top: 10px;">
                    <label>
                        <input type="checkbox" id="auto-continuous" ${AUTO_CONTINUOUS ? 'checked' : ''}>
                        <strong>🔥 自动连续刷课</strong>
                    </label>
                </div>
                <div class="control-group">
                    <label>
                        检测间隔:
                        <select id="check-interval">
                            <option value="5" ${CHECK_INTERVAL === 5 ? 'selected' : ''}>5秒</option>
                            <option value="10" ${CHECK_INTERVAL === 10 ? 'selected' : ''}>10秒</option>
                            <option value="15" ${CHECK_INTERVAL === 15 ? 'selected' : ''}>15秒</option>
                            <option value="20" ${CHECK_INTERVAL === 20 ? 'selected' : ''}>20秒</option>
                            <option value="30" ${CHECK_INTERVAL === 30 ? 'selected' : ''}>30秒</option>
                        </select>
                    </label>
                </div>
                <div class="control-group" style="font-size: 11px; color: rgba(255,255,255,0.8); margin-top: -5px;">
                    下次检测: <span id="next-check-time">--</span>
                </div>
                <div class="control-group">
                    <button id="find-course-btn" style="width: 100%; padding: 8px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: bold;">
                        🔍 检测未完成视频
                    </button>
                </div>
                <div class="control-group">
                    <button id="pause-check-btn" style="width: 100%; padding: 6px; background: rgba(255,255,255,0.2); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
                        ⏸️ 暂停检测
                    </button>
                </div>
                <div class="control-group">
                    <button id="manual-check-btn" style="width: 100%; padding: 6px; background: rgba(255,255,255,0.2); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
                        🔄 立即检测完成度
                    </button>
                </div>
                <div class="control-group" style="text-align: center; font-size: 11px; color: rgba(255,255,255,0.7); margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.2);">
                    <a href="https://github.com/chnlion/enaea-auto-study" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: none; display: flex; align-items: center; justify-content: center; gap: 5px;">
                        <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" style="vertical-align: middle;">
                            <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
                        </svg>
                        <span>在 GitHub 上查看此项目</span>
                    </a>
                </div>
            </div>
            `;
        } else {
            // ========== 列表页面板 ==========
            panelContent = `
            <div class="panel-header" id="panel-header">
                <span style="font-weight: bold; font-size: 13px;">🎓 ENAEA自动刷课助手 v1.0</span>
                <button id="panel-minimize" style="background: none; border: none; color: white; cursor: pointer; font-size: 18px; padding: 0 5px;">−</button>
            </div>
            <div class="panel-content" id="panel-content">
                <div class="control-group" style="text-align: center; font-size: 11px; color: rgba(255,255,255,0.7); margin-bottom: 10px;">
                    📋 当前:列表页
                </div>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="auto-select" ${AUTO_SELECT_COURSE ? 'checked' : ''}>
                        列表页自动选课
                    </label>
                </div>
                <div class="control-group" style="border-top: 1px solid rgba(255,255,255,0.2); padding-top: 10px; margin-top: 10px;">
                    <label>
                        <input type="checkbox" id="auto-continuous" ${AUTO_CONTINUOUS ? 'checked' : ''}>
                        <strong>🔥 自动连续刷课</strong>
                    </label>
                </div>
                <div class="control-group">
                    <label>
                        检测间隔:
                        <select id="check-interval">
                            <option value="5" ${CHECK_INTERVAL === 5 ? 'selected' : ''}>5秒</option>
                            <option value="10" ${CHECK_INTERVAL === 10 ? 'selected' : ''}>10秒</option>
                            <option value="15" ${CHECK_INTERVAL === 15 ? 'selected' : ''}>15秒</option>
                            <option value="20" ${CHECK_INTERVAL === 20 ? 'selected' : ''}>20秒</option>
                            <option value="30" ${CHECK_INTERVAL === 30 ? 'selected' : ''}>30秒</option>
                        </select>
                    </label>
                </div>
                <div class="control-group" style="font-size: 11px; color: rgba(255,255,255,0.8); margin-top: -5px;">
                    已连续完成: <span id="continuous-count">0</span> 门 (上限: ${MAX_CONTINUOUS_COUNT})
                </div>
                <div class="control-group" style="border-top: 1px solid rgba(255,255,255,0.2); padding-top: 10px; margin-top: 10px;">
                    <label>
                        倍速设置:
                        <select id="speed-select">
                            <option value="1" ${TARGET_SPEED === 1 ? 'selected' : ''}>1x</option>
                            <option value="2" ${TARGET_SPEED === 2 ? 'selected' : ''}>2x</option>
                            <option value="4" ${TARGET_SPEED === 4 ? 'selected' : ''}>4x</option>
                        </select>
                    </label>
                </div>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="auto-mute" ${AUTO_MUTE ? 'checked' : ''}>
                        自动静音
                    </label>
                </div>
                <div class="control-group">
                    <button id="select-course-btn" style="width: 100%; padding: 8px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: bold;">
                        📋 立即选择课程
                    </button>
                </div>
                <div class="control-group">
                    <button id="refresh-page-btn" style="width: 100%; padding: 6px; background: rgba(255,255,255,0.2); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
                        🔄 刷新进度
                    </button>
                </div>
                <div class="control-group">
                    <button id="clear-signal-btn" style="width: 100%; padding: 6px; background: rgba(255,255,255,0.2); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
                        🧹 清除完成信号
                    </button>
                </div>
                <div class="control-group" style="text-align: center; font-size: 11px; color: rgba(255,255,255,0.7); margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.2);">
                    <a href="https://github.com/chnlion/enaea-auto-study" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: none; display: flex; align-items: center; justify-content: center; gap: 5px;">
                        <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" style="vertical-align: middle;">
                            <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
                        </svg>
                        <span>在 GitHub 上查看此项目</span>
                    </a>
                </div>
            </div>
            `;
        }
        
        panel.innerHTML = panelContent;

        const style = document.createElement('style');
        style.textContent = `
            #enaea-control-panel {
                position: fixed;
                top: 20px;
                left: 20px;
                width: 240px;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                border-radius: 12px;
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
                z-index: 999999;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
                color: white;
                backdrop-filter: blur(10px);
                cursor: move;
            }
            .panel-header {
                padding: 12px 15px;
                background: rgba(0, 0, 0, 0.2);
                border-radius: 12px 12px 0 0;
                display: flex;
                justify-content: space-between;
                align-items: center;
                cursor: move;
            }
            .panel-content {
                padding: 15px;
            }
            .control-group {
                margin-bottom: 12px;
            }
            .control-group label {
                display: flex;
                align-items: center;
                font-size: 13px;
                cursor: pointer;
            }
            .control-group input[type="checkbox"] {
                margin-right: 8px;
                cursor: pointer;
            }
            .control-group select {
                margin-left: 8px;
                padding: 4px 8px;
                border-radius: 4px;
                border: none;
                background: rgba(255, 255, 255, 0.9);
                cursor: pointer;
                flex: 1;
                font-size: 12px;
            }
            #panel-minimize:hover {
                opacity: 0.7;
            }
            .control-group button:hover {
                opacity: 0.9;
                transform: translateY(-1px);
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
            }
            .control-group button:active {
                transform: translateY(0);
            }
            #continuous-count {
                font-weight: bold;
                color: #4ade80;
            }
        `;

        document.documentElement.appendChild(style);
        document.body.appendChild(panel);

        // 更新连续计数显示
        function updateCountDisplay() {
            const countSpan = document.getElementById('continuous-count');
            if (countSpan) {
                const count = localStorage.getItem('enaea_continuous_count') || '0';
                countSpan.textContent = count;
            }
        }
        updateCountDisplay();
        setInterval(updateCountDisplay, 5000);

        // 拖拽功能
        let isDragging = false;
        let currentX, currentY, initialX, initialY;
        const header = document.getElementById('panel-header');
        
        header.addEventListener('mousedown', (e) => {
            if (e.target.id === 'panel-minimize') return;
            initialX = e.clientX - panel.offsetLeft;
            initialY = e.clientY - panel.offsetTop;
            isDragging = true;
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                panel.style.left = currentX + 'px';
                panel.style.top = currentY + 'px';
                panel.style.right = 'auto';
            }
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
        });

        // 最小化功能
        const minimizeBtn = document.getElementById('panel-minimize');
        const content = document.getElementById('panel-content');
        let isMinimized = false;

        minimizeBtn.addEventListener('click', () => {
            isMinimized = !isMinimized;
            content.style.display = isMinimized ? 'none' : 'block';
            minimizeBtn.textContent = isMinimized ? '+' : '−';
        });

        // 控件事件监听
        document.getElementById('auto-mute').addEventListener('change', (e) => {
            AUTO_MUTE = e.target.checked;
            localStorage.setItem('enaea_auto_mute', AUTO_MUTE);
            console.log(`🔇 自动静音: ${AUTO_MUTE ? '开启' : '关闭'}`);
            setAllVideos();
        });

        document.getElementById('speed-select').addEventListener('change', (e) => {
            TARGET_SPEED = parseFloat(e.target.value);
            localStorage.setItem('enaea_target_speed', TARGET_SPEED);
            console.log(`⚡ 播放速度已调整为: ${TARGET_SPEED}x`);
            processedVideos = new WeakSet();
            setAllVideos();
        });

        document.getElementById('auto-jump').addEventListener('change', (e) => {
            AUTO_JUMP = e.target.checked;
            console.log(`🚀 播放页自动跳转: ${AUTO_JUMP ? '开启' : '关闭'}`);
        });

        document.getElementById('auto-select').addEventListener('change', (e) => {
            AUTO_SELECT_COURSE = e.target.checked;
            console.log(`📋 列表页自动选课: ${AUTO_SELECT_COURSE ? '开启' : '关闭'}`);
        });

        document.getElementById('auto-continuous').addEventListener('change', (e) => {
            AUTO_CONTINUOUS = e.target.checked;
            localStorage.setItem('enaea_auto_continuous', AUTO_CONTINUOUS);
            console.log(`🔥 自动连续刷课: ${AUTO_CONTINUOUS ? '开启' : '关闭'}`);
            
            if (AUTO_CONTINUOUS) {
                // 如果在课程页,启动检测
                const url = window.location.href;
                if (url.includes('viewerforccvideo.do') || url.includes('viewerforicourse.do')) {
                    startCourseCompletionCheck();
                }
            } else {
                // 停止检测
                if (checkTimer) {
                    clearInterval(checkTimer);
                    checkTimer = null;
                    console.log('⏸️ 已停止课程完成检测');
                }
            }
        });

        document.getElementById('check-interval').addEventListener('change', (e) => {
            CHECK_INTERVAL = parseInt(e.target.value);
            localStorage.setItem('enaea_check_interval', CHECK_INTERVAL);
            console.log(`⏱️ 检测间隔已调整为: ${CHECK_INTERVAL}秒`);
            
            // 如果正在检测,重启定时器
            const url = window.location.href;
            if ((url.includes('viewerforccvideo.do') || url.includes('viewerforicourse.do')) && AUTO_CONTINUOUS) {
                if (checkTimer) {
                    clearInterval(checkTimer);
                }
                checkTimer = setInterval(checkAllVideosCompleted, CHECK_INTERVAL * 1000);
                console.log('🔄 已重启课程完成检测定时器');
            }
        });

        // 播放页按钮事件
        const findCourseBtn = document.getElementById('find-course-btn');
        if (findCourseBtn) {
            findCourseBtn.addEventListener('click', () => {
                console.log('🔍 手动触发检测未完成视频...');
                findAndJumpToUnfinishedCourse();
            });
        }

        const pauseCheckBtn = document.getElementById('pause-check-btn');
        if (pauseCheckBtn) {
            let isPaused = false;
            pauseCheckBtn.addEventListener('click', () => {
                isPaused = !isPaused;
                if (isPaused) {
                    // 暂停检测
                    if (checkTimer) {
                        clearInterval(checkTimer);
                        checkTimer = null;
                    }
                    pauseCheckBtn.textContent = '▶️ 恢复检测';
                    pauseCheckBtn.style.background = 'rgba(74, 222, 128, 0.3)';
                    console.log('⏸️ 已暂停自动检测');
                } else {
                    // 恢复检测
                    checkTimer = setInterval(checkAllVideosCompleted, CHECK_INTERVAL * 1000);
                    pauseCheckBtn.textContent = '⏸️ 暂停检测';
                    pauseCheckBtn.style.background = 'rgba(255,255,255,0.2)';
                    console.log('▶️ 已恢复自动检测');
                }
            });
        }

        const manualCheckBtn = document.getElementById('manual-check-btn');
        if (manualCheckBtn) {
            manualCheckBtn.addEventListener('click', () => {
                console.log('🔄 手动触发立即检测完成度...');
                checkAllVideosCompleted();
            });
        }

        // 列表页按钮事件
        const selectCourseBtn = document.getElementById('select-course-btn');
        if (selectCourseBtn) {
            selectCourseBtn.addEventListener('click', () => {
                console.log('📋 手动触发列表页选课...');
                findAndClickUnfinishedCourseInList();
            });
        }

        const refreshPageBtn = document.getElementById('refresh-page-btn');
        if (refreshPageBtn) {
            refreshPageBtn.addEventListener('click', () => {
                console.log('🔄 刷新页面...');
                location.reload();
            });
        }

        const clearSignalBtn = document.getElementById('clear-signal-btn');
        if (clearSignalBtn) {
            clearSignalBtn.addEventListener('click', () => {
                localStorage.removeItem('enaea_course_completed_signal');
                localStorage.removeItem('enaea_continuous_count');
                console.log('🧹 已清除完成信号和连续计数');
                updateCountDisplay();
                alert('已清除完成信号和连续计数!');
            });
        }

        console.log('✅ 控制面板已创建');
    }

    // ==================== 初始化 ====================
    
    function init() {
        console.log('═══════════════════════════════════════');
        console.log('📚 ENAEA自动刷课助手 v1.0');
        console.log('✨ 自动连续刷课 + 列表页自动选课 + 播放页自动刷课');
        console.log('👤 作者: Liontooth');
        console.log('═══════════════════════════════════════');
        
        // 诊断信息
        console.log('🔍 环境诊断:');
        try {
            console.log('  - 当前URL:', window.location.href);
            console.log('  - 是否在iframe:', window.self !== window.top);
            console.log('  - document.readyState:', document.readyState);
            console.log('  - 自动连续刷课:', AUTO_CONTINUOUS ? '✅ 开启' : '⏸️ 关闭');
            console.log('  - 检测间隔:', CHECK_INTERVAL + '秒');
        } catch (e) {
            console.log('  - 诊断出错:', e.message);
        }

        // 视频控制初始化
        setTimeout(setAllVideos, 100);
        setTimeout(setAllVideos, 500);
        setTimeout(setAllVideos, 1000);
        setTimeout(setAllVideos, 2000);

        hijackPlaybackRate();
        startObserver();
        setInterval(setAllVideos, 2000);
        setupEventListeners();

        // 判断当前页面类型并执行相应的自动化功能
        const currentUrl = window.location.href;
        console.log('📍 检测到当前页面类型...');
        
        if (currentUrl.includes('viewerforccvideo.do') || currentUrl.includes('viewerforicourse.do')) {
            console.log('📺 识别为:视频播放页面');
        } else if (currentUrl.includes('study.enaea.edu.cn')) {
            console.log('📋 识别为:课程列表页面');
        }
        
        window.addEventListener('load', function() {
            setTimeout(setAllVideos, 500);
            setTimeout(setAllVideos, 1500);
            
            // 判断当前页面类型并执行相应的自动化功能
            const url = window.location.href;
            
            if (url.includes('viewerforccvideo.do') || url.includes('viewerforicourse.do')) {
                // 视频播放页面
                console.log('🎬 启动视频播放页功能...');
                autoJumpOnLoadInVideoPage();
                
                // 启动课程完成检测
                if (AUTO_CONTINUOUS) {
                    startCourseCompletionCheck();
                }
            } else if (url.includes('study.enaea.edu.cn')) {
                // 课程列表页面
                console.log('📋 启动课程列表页功能...');
                autoSelectCourseInList();
                
                // 监听课程完成信号
                setupStorageListener();
            }
        });

        setTimeout(checkIframes, 3000);
        setInterval(checkIframes, 5000);

        autoClickContinue();

        if (document.body) {
            createPanel();
        } else {
            setTimeout(() => {
                if (document.body) createPanel();
            }, 1000);
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();