cela-自动学习脚本API版

[API版] cela自动学习脚本,支持浦东分院课程列表页面,支持专栏详情页面课程获取,修复进度上报API端点,基于真实API分析优化。

当前为 2025-07-11 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         cela-自动学习脚本API版
// @namespace    https://github.com/Moker32/
// @version      3.37.4
// @description  [API版] cela自动学习脚本,支持浦东分院课程列表页面,支持专栏详情页面课程获取,修复进度上报API端点,基于真实API分析优化。
// @author       Moker32
// @license      MIT
// @changelog    v3.37.4: 移除UI配置功能,使用固定默认配置:智能学习模式、跳过已完成课程、启用学习记录、关闭调试模式
// @grant        GM_getValue
// @grant        GM_setValue
// @match        *://cela.e-celap.cn/*
// @match        *://pudong.e-celap.cn/*
// @match        *://pd.cela.cn/*
// @match        *://*.e-celap.cn/*
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @connect      cela.e-celap.cn
// @connect      pudong.e-celap.cn
// @connect      pd.cela.cn
// @connect      zpyapi.shsets.com
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // --- 常量集中管理 ---
    const CONSTANTS = {
        API_ENDPOINTS: {
            GET_PLAY_TREND: '/inc/nc/course/play/getPlayTrend',
            PULSE_SAVE_RECORD: '/inc/nc/course/play/pulseSaveRecord',
            REPORT_PROGRESS: '/inc/nc/course/play/reportProgress',
            UPDATE_PROGRESS: '/inc/nc/course/play/updateProgress',
            START_STUDY: '/inc/nc/course/play/startStudy',
            FINISH_STUDY: '/inc/nc/course/play/finishStudy',
            COMPLETE_COURSE: '/inc/nc/course/completeCourse',
            FINISH_LEARNING: '/inc/nc/course/finishLearning',
            UPDATE_STATUS: '/inc/nc/course/updateStatus',
            SUBMIT_LEARNING_RECORD: '/inc/nc/course/submitLearningRecord',
            GRADUATE: '/inc/nc/course/graduate',
            GET_STUDY_RECORD: '/inc/nc/course/getStudyRecord',
            CREATE_SESSION: '/inc/nc/course/createSession',
            INIT_STUDY_RECORD: '/inc/nc/course/initStudyRecord',
            COMPLETE_LEARNING: '/inc/nc/course/completeLearning',
            SAVE_COMPLETION_STATUS: '/inc/nc/course/saveCompletionStatus',
            SAVE_STUDY_RECORD: '/inc/nc/course/saveStudyRecord',
            GET_COURSEWARE_DETAIL: '/inc/nc/course/play/getCoursewareDetail',
            GET_PACK_BY_ID: '/inc/nc/pack/getById',
            GET_COURSE_LIST: '/api/course/list',
            PUSH_COURSE: '/dsfa/nc/cela/api/pushCourse',
            GET_COURSE_BY_USER: '/dsfa/nc/cela/api/getCourseByUserAndCourse',
            VIDEO_PROGRESS: '/api/player/progress'
        },
        SELECTORS: {
            PANEL: '#api-learner-panel',
            STATUS_DISPLAY: '#learner-status',
            PROGRESS_INNER: '#learner-progress-inner',
            TOGGLE_BTN: '#toggle-learning-btn',
            LOG_CONTAINER: '#api-learner-panel .log-container',
            STAT_TOTAL: '#stat-total',
            STAT_COMPLETED: '#stat-completed',
            STAT_LEARNED: '#stat-learned',
            STAT_SKIPPED: '#stat-skipped',
            APP: '#app'
        },
        STORAGE_KEYS: {
            TOKEN: 'token',
            AUTH_TOKEN: 'authToken',
            ACCESS_TOKEN: 'access_token',
            USER_ID: 'userId',
            USER_ID_ALT: 'user_id'
        },
        COURSE_SELECTORS: [
            '[class*="course"]', '[data-course]', '.course-item', '.lesson-item',
            '.el-card', '.el-card__body', '.course-card', '.course-box',
            '.nc-course-item', '.study-item', '.learn-item',
            '[class*="item"]', '[class*="card"]', '[data-id]',
            '.pudong-course', '.pd-course', '.dsf-course'
        ],
        FALLBACK_SELECTORS: [
            'div[class*="list"] > div',
            'ul > li',
            '.content div',
            '#app div[class]',
            '[class*="container"] > div'
        ],
        VIDEO_SELECTORS: [
            'video', '[api-base-url]', '[class*="video"]', 'iframe[src*="play"]'
        ],
        COOKIE_PATTERNS: {
            USER_ID: /userId=([^;]+)/,
            TOKEN: /token=([^;]+)/,
            P_PARAM: /_p=([^;]+)/
        },
        TIME_FORMATS: {
            DEFAULT_DURATION: 1800, // 30分钟
            MOCK_DURATION: 1800
        },
        UI_LIMITS: {
            MAX_LOG_ENTRIES: 50,
            LOG_FLUSH_DELAY: 100
        }
    };

    // --- 事件驱动机制 (v2.0优化) ---
    const EventBus = {
        events: {},
        
        subscribe(event, listener) {
            if (!this.events[event]) {
                this.events[event] = [];
            }
            this.events[event].push(listener);
            return () => {
                // 返回取消订阅函数
                const index = this.events[event].indexOf(listener);
                if (index > -1) {
                    this.events[event].splice(index, 1);
                }
            };
        },
        
        publish(event, data) {
            if (!this.events[event]) return;
            this.events[event].forEach(listener => {
                try {
                    listener(data);
                } catch (error) {
                    console.error(`EventBus error in ${event}:`, error);
                }
            });
        },
        
        // 一次性事件监听
        once(event, listener) {
            const unsubscribe = this.subscribe(event, (data) => {
                unsubscribe();
                listener(data);
            });
            return unsubscribe;
        }
    };

    // --- 配置管理模块 (v2.0优化) ---
    const Settings = {
        defaultConfig: {
            // === 固定配置 (不可修改) ===
            LEARNING_STRATEGY: 'smart',                    // 智能学习模式
            SKIP_COMPLETED_COURSES: true,                  // 跳过已完成课程
            STUDY_RECORD_ENABLED: true,                    // 启用学习记录
            FALLBACK_MODE: true,                           // 启用兜底模式
            DEBUG_MODE: false,                             // 关闭调试模式
            HEARTBEAT_INTERVAL: 10,                        // 进度上报间隔(秒)
            COMPLETION_THRESHOLD: 95,                      // 完成度阈值(%)
            
            // === 技术配置 (高级选项) ===
            MAX_RETRY_ATTEMPTS: 10,                        // 最大重试次数
            RETRY_DELAY: 3000,                            // 重试延迟(毫秒)
            COURSE_COMPLETION_DELAY: 5,                    // 课程完成延迟(秒)
            
            // === 自动配置 (系统检测) ===
            PUDONG_MODE: false,                           // 浦东分院模式(自动检测)
            PUDONG_API_BASE: '',                          // 浦东API基础URL(自动设置)
            
            // === 向后兼容配置 (将被迁移) ===
            FAST_LEARNING_MODE: true,                     // 兼容旧版本
            ULTRA_FAST_MODE: true,                        // 兼容旧版本
            SMART_LEARNING_MODE: true                     // 兼容旧版本
        },
        
        config: {},
        
        load() {
            // 使用固定配置,不再从存储加载
            this.config = { ...this.defaultConfig };
            EventBus.publish('log', { message: '✅ 使用固定配置:智能学习模式', type: 'success' });
        },
        
        get(key) {
            return this.config[key];
        }
    };

    // --- 学习策略枚举 (v3.37.3优化) ---
    const LEARNING_STRATEGIES = {
        SLOW: 'slow',           // 慢启动 - 模拟真实学习
        NORMAL: 'normal',       // 正常速度 - 平衡效率与安全
        FAST: 'fast',          // 快速学习 - 提高学习效率
        ULTRA: 'ultra',        // 超快速 - 直接完成
        SMART: 'smart'         // 智能自适应 - 根据环境调整
    };

    // --- 配置区域 (v3.37.4简化) ---
    const CONFIG = new Proxy({}, {
        get(target, prop) {
            // 优先从Settings获取配置,如果不存在则使用默认值
            return Settings.get(prop) ?? target[prop];
        },
        set(target, prop, value) {
            // 固定配置模式,直接设置到target
            target[prop] = value;
            return true;
        }
    });

    // 自动检测当前环境
    const detectEnvironment = () => {
        const hostname = window.location.hostname;
        const href = window.location.href;
        
        // 检测是否为浦东分院
        if (hostname.includes('pudong') || 
            hostname.includes('pd.') || 
            href.includes('浦东分院') ||
            href.includes('pudong') ||
            document.title.includes('浦东')) {
            CONFIG.PUDONG_MODE = true;
            console.log('🏢 检测到浦东分院环境');
        }
        
        // 设置API基础URL
        if (CONFIG.PUDONG_MODE) {
            if (hostname.includes('pudong.e-celap.cn')) {
                CONFIG.PUDONG_API_BASE = `https://${hostname}`;
            } else if (hostname.includes('pd.cela.cn')) {
                CONFIG.PUDONG_API_BASE = `https://${hostname}`;
            } else {
                // 默认使用主站API
                CONFIG.PUDONG_API_BASE = 'https://cela.e-celap.cn';
            }
        }
        
        console.log(`🌐 当前环境: ${CONFIG.PUDONG_MODE ? '浦东分院' : '主站'} (${hostname})`);
        console.log(`🔗 API基础URL: ${CONFIG.PUDONG_API_BASE || 'https://cela.e-celap.cn'}`);
    };

    // --- UI和日志(优化版) ---
    const UI = {
        logs: [],
        logBuffer: [], // 日志缓冲区
        logUpdateTimeout: null,
        statistics: {
            total: 0,
            completed: 0,
            learned: 0,
            failed: 0,
            skipped: 0
        },
        createPanel: () => {
            const panel = document.createElement('div');
            panel.id = 'api-learner-panel';
            panel.innerHTML = `
                <div class="header">
                    API学习助手 v3.37.4
                </div>
                <div class="content">
                    <div class="status">状态: <span id="learner-status">待命</span></div>
                    <div class="statistics">
                        <div class="stat-item">总计: <span id="stat-total">0</span></div>
                        <div class="stat-item">已完成: <span id="stat-completed">0</span></div>
                        <div class="stat-item">新学习: <span id="stat-learned">0</span></div>
                        <div class="stat-item">跳过: <span id="stat-skipped">0</span></div>
                    </div>
                    <div class="progress-bar"><div id="learner-progress-inner"></div></div>
                    
                    <div class="log-container"></div>
                </div>
                <div class="footer">
                    <button id="toggle-learning-btn" data-state="stopped">开始学习</button>
                    <div class="feature-note">✨ 智能学习模式 + 自动记录</div>
                </div>
            `;
            document.body.appendChild(panel);
            UI.addStyles();
            UI.initEventListeners();
        },
        // 优化后的日志函数 - 使用批量更新策略
        log: function(message, type = 'info') {
            const timestamp = new Date().toLocaleTimeString();
            const logMessage = `[${timestamp}] ${message}`;
            
            // 添加到缓冲区
            this.logBuffer.push({ message: logMessage, type });

            // 使用防抖处理,批量更新DOM
            if (this.logUpdateTimeout) clearTimeout(this.logUpdateTimeout);
            this.logUpdateTimeout = setTimeout(() => this.flushLogBuffer(), CONSTANTS.UI_LIMITS.LOG_FLUSH_DELAY);
            
            if (CONFIG.DEBUG_MODE) {
                const debugMessage = `[API Learner Debug] ${logMessage}`;
                console.log(debugMessage);
                this.logs.push(debugMessage);
            }
        },

        // 初始化事件监听器 (v2.0新增)
        initEventListeners: function() {
            // 订阅事件
            EventBus.subscribe('log', ({ message, type }) => this.log(message, type));
            EventBus.subscribe('statusUpdate', status => this.updateStatus(status));
            EventBus.subscribe('progressUpdate', progress => this.updateProgress(progress));
            EventBus.subscribe('statisticsUpdate', stats => this.updateStatistics(stats));
        },
        // 批量刷新日志缓冲区
        flushLogBuffer: function() {
            const logContainer = document.querySelector(CONSTANTS.SELECTORS.LOG_CONTAINER);
            if (!logContainer || this.logBuffer.length === 0) return;

            const fragment = document.createDocumentFragment();
            this.logBuffer.forEach(log => {
                const logEntry = document.createElement('div');
                logEntry.className = `log-entry ${log.type}`;
                logEntry.textContent = log.message;
                fragment.appendChild(logEntry);
            });

            logContainer.appendChild(fragment);
            logContainer.scrollTop = logContainer.scrollHeight;
            
            // 限制日志条数,避免占用过多内存
            const entries = logContainer.querySelectorAll('.log-entry');
            if (entries.length > CONSTANTS.UI_LIMITS.MAX_LOG_ENTRIES) {
                for (let i = 0; i < entries.length - CONSTANTS.UI_LIMITS.MAX_LOG_ENTRIES; i++) {
                    entries[i].remove();
                }
            }
            
            this.logBuffer = []; // 清空缓冲区
        },
        updateStatus: (status) => {
            const statusEl = document.getElementById(CONSTANTS.SELECTORS.STATUS_DISPLAY.replace('#', ''));
            if (statusEl) statusEl.textContent = status;
        },
        updateProgress: (percentage) => {
            const progressInner = document.getElementById(CONSTANTS.SELECTORS.PROGRESS_INNER.replace('#', ''));
            if (progressInner) progressInner.style.width = `${percentage}%`;
        },
        updateStatistics: (stats) => {
            Object.assign(UI.statistics, stats);
            document.getElementById(CONSTANTS.SELECTORS.STAT_TOTAL.replace('#', '')).textContent = UI.statistics.total;
            document.getElementById(CONSTANTS.SELECTORS.STAT_COMPLETED.replace('#', '')).textContent = UI.statistics.completed;
            document.getElementById(CONSTANTS.SELECTORS.STAT_LEARNED.replace('#', '')).textContent = UI.statistics.learned;
            document.getElementById(CONSTANTS.SELECTORS.STAT_SKIPPED.replace('#', '')).textContent = UI.statistics.skipped;
        },
        addStyles: () => {
            const styles = `
                #api-learner-panel { position: fixed; bottom: 20px; right: 20px; width: 400px; background: #fff; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 99999; font-family: sans-serif; font-size: 14px; color: #333; }
                #api-learner-panel .header { background: #f7f7f7; padding: 10px 15px; font-weight: bold; border-bottom: 1px solid #ddd; border-radius: 8px 8px 0 0; }
                #api-learner-panel .content { padding: 15px; }
                #api-learner-panel .status { margin-bottom: 10px; }
                #api-learner-panel .statistics { display: flex; justify-content: space-between; margin-bottom: 10px; padding: 8px; background: #f9f9f9; border-radius: 4px; font-size: 12px; }
                #api-learner-panel .stat-item { text-align: center; }
                #api-learner-panel .progress-bar { height: 8px; background: #eee; border-radius: 4px; overflow: hidden; margin-bottom: 10px; }
                #api-learner-panel #learner-progress-inner { height: 100%; width: 0%; background: #4caf50; transition: width 0.3s ease; }
                #api-learner-panel .log-container { max-height: 120px; overflow-y: auto; background: #fafafa; padding: 8px; border: 1px solid #eee; border-radius: 4px; font-size: 11px; line-height: 1.4; }
                #api-learner-panel .log-entry.error { color: #f44336; }
                #api-learner-panel .log-entry.success { color: #4caf50; }
                #api-learner-panel .log-entry.warn { color: #ff9800; }
                #api-learner-panel .footer { padding: 10px 15px; border-top: 1px solid #ddd; text-align: right; }
                #api-learner-panel button { padding: 6px 10px; border: none; border-radius: 4px; cursor: pointer; margin-left: 8px; font-size: 12px; }
                #api-learner-panel button#toggle-learning-btn { 
                    background: #2196f3; 
                    color: white; 
                    transition: background-color 0.3s ease;
                }
                #api-learner-panel button#toggle-learning-btn[data-state="running"] { 
                    background: #f44336; 
                }
                #api-learner-panel button:disabled { background: #ccc; }
                #api-learner-panel .feature-note { font-size: 11px; color: #666; margin-top: 8px; text-align: center; }
            `;
            const styleSheet = document.createElement("style");
            styleSheet.type = "text/css";
            styleSheet.innerText = styles;
            document.head.appendChild(styleSheet);
        },
        exportLogs: () => {
            if (UI.logs.length === 0) {
                alert('没有可导出的调试日志。');
                return;
            }
            const blob = new Blob([UI.logs.join('\r\n')], { type: 'text/plain;charset=utf-8' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `api_learner_debug_log_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.txt`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }
    };

    // --- 学习策略模式 (v2.0优化) ---
    const LearningStrategies = {
        // 策略执行的辅助函数,封装重复逻辑
        async _executeSteps(context, steps, delay) {
            for (const targetTime of steps) {
                // 检查停止请求
                if (Learner.stopRequested) {
                    EventBus.publish('log', { message: '⏹️ 收到停止请求,中断策略执行', type: 'warn' });
                    return false;
                }
                
                await new Promise(resolve => setTimeout(resolve, delay));
                const success = await API.reportProgress(context.playInfo, targetTime);
                if (!success) return false;
                
                context.currentTime = targetTime;
                const progress = Math.floor((context.currentTime / context.duration) * 100);
                EventBus.publish('log', { message: `[策略执行] 进度: ${progress}%`, type: 'info' });
            }
            return true;
        },

        // 慢启动策略:分3步到50%,然后快速完成
        async slow_start(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[慢启动策略] 开始执行', type: 'info' });
            
            const steps = [0.2, 0.35, 0.5].map(p => Math.floor(duration * p));
            const success = await this._executeSteps(context, steps, 8000);
            
            if (success && !Learner.stopRequested) {
                // 最后冲刺到完成
                await new Promise(resolve => setTimeout(resolve, 5000));
                context.currentTime = duration - 10;
                return await API.reportProgress(context.playInfo, context.currentTime);
            }
            return success;
        },

        // 渐进式策略:分5步完成
        async progressive(context) {
            const { duration, currentTime } = context;
            EventBus.publish('log', { message: '[渐进式策略] 开始执行', type: 'info' });
            
            const remaining = duration - currentTime;
            const stepSize = Math.floor(remaining / 5);
            const steps = [];
            
            for (let i = 1; i <= 5; i++) {
                const nextTime = Math.min(currentTime + (stepSize * i), duration - 10);
                steps.push(nextTime);
            }
            
            return await this._executeSteps(context, steps, 6000);
        },

        // 快速完成策略:直接跳到95%然后完成
        async fast_finish(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[快速完成策略] 开始执行', type: 'info' });
            
            if (Learner.stopRequested) return false;
            
            const target95 = Math.floor(duration * 0.95);
            await new Promise(resolve => setTimeout(resolve, 3000));
            let success = await API.reportProgress(context.playInfo, target95);
            
            if (success && !Learner.stopRequested) {
                await new Promise(resolve => setTimeout(resolve, 5000));
                context.currentTime = duration - 10;
                success = await API.reportProgress(context.playInfo, context.currentTime);
            }
            return success;
        },

        // 最后冲刺策略:直接完成
        async final_push(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[最后冲刺策略] 开始执行', type: 'info' });
            
            if (Learner.stopRequested) return false;
            
            await new Promise(resolve => setTimeout(resolve, 3000));
            context.currentTime = duration - 10;
            return await API.reportProgress(context.playInfo, context.currentTime);
        },

        // 超快速策略:直接跳到结束
        async ultra_fast(context) {
            const { duration } = context;
            EventBus.publish('log', { message: '[超快速策略] 开始执行', type: 'info' });
            
            if (Learner.stopRequested) return false;
            
            await new Promise(resolve => setTimeout(resolve, 2000));
            context.currentTime = duration - 5;
            return await API.reportProgress(context.playInfo, context.currentTime);
        },

        // 策略选择器
        selectStrategy(currentProgress) {
            if (Settings.get('ULTRA_FAST_MODE')) {
                return 'ultra_fast';
            } else if (currentProgress < 10) {
                return 'slow_start';
            } else if (currentProgress < 50) {
                return 'progressive';
            } else if (currentProgress < 80) {
                return 'fast_finish';
            } else {
                return 'final_push';
            }
        }
    };

    // --- 工具函数 ---
    const Utils = {
        formatTime: function(seconds) {
            if (!seconds || seconds < 0) return '00:00:00';
            
            const hours = Math.floor(seconds / 3600);
            const minutes = Math.floor((seconds % 3600) / 60);
            const secs = Math.floor(seconds % 60);
            
            return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        }
    };

    // --- API 核心(优化版) ---
    const API = {
        _baseUrl: 'https://cela.e-celap.cn',
        _videoApiBaseUrl: 'https://zpyapi.shsets.com',
        abortController: null, // AbortController 支持
        
        // 动态获取基础URL
        getBaseUrl: function() {
            if (CONFIG.PUDONG_MODE && CONFIG.PUDONG_API_BASE) {
                return CONFIG.PUDONG_API_BASE;
            }
            return this._baseUrl;
        },
        
        // 通用API端点尝试策略 (根据审查报告建议优化)
        async _tryApiEndpoints(apiCalls, successMessage, failureMessage) {
            for (let i = 0; i < apiCalls.length; i++) {
                // 检查是否被中止
                if (this.abortController && this.abortController.signal.aborted) {
                    throw new DOMException('Aborted', 'AbortError');
                }
                
                try {
                    const result = await apiCalls[i]();
                    if (this._isSuccessResponse(result)) {
                        EventBus.publish('log', { message: `${successMessage} (方法${i+1})`, type: 'success' });
                        return result;
                    }
                    EventBus.publish('log', { message: `[API Strategy] 方法${i+1}失败: ${result?.message || 'unknown error'}`, type: 'debug' });
                } catch (error) {
                    EventBus.publish('log', { message: `[API Strategy] 方法${i+1}异常: ${error.message}`, type: 'debug' });
                    if (error.name === 'AbortError') {
                        throw error; // 重新抛出中止错误
                    }
                }
            }
            EventBus.publish('log', { message: failureMessage, type: 'warn' });
            return null;
        },

        // 统一的成功响应判断逻辑
        _isSuccessResponse(result) {
            return result && (
                result.success === true || 
                result.code === 200 || 
                result.state === 20000 ||
                result.status === 'success' ||
                (result.code >= 200 && result.code < 300)
            );
        },
        
        _request: async function(options) {
            return new Promise((resolve, reject) => {
                // 检查是否被中止
                if (this.abortController && this.abortController.signal.aborted) {
                    return reject(new DOMException('Aborted', 'AbortError'));
                }
                
                // 提取Cookie和其他认证信息
                const cookies = document.cookie;
                const token = this._extractToken();
                
                // 构建请求头 - 根据数据类型设置Content-Type
                const headers = {
                    'Accept': 'application/json, text/plain, */*',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'X-Requested-With': 'XMLHttpRequest',
                    'Referer': window.location.href,
                    'Origin': this.getBaseUrl(),
                    'Cookie': cookies,
                    ...options.headers
                };
                
                // 根据数据类型设置Content-Type
                if (options.data instanceof FormData) {
                    // FormData会自动设置Content-Type,包括boundary
                    // 不要手动设置Content-Type
                } else if (typeof options.data === 'string' && options.data.includes('=')) {
                    // URL编码的表单数据
                    headers['Content-Type'] = 'application/x-www-form-urlencoded';
                } else {
                    // JSON数据
                    headers['Content-Type'] = 'application/json';
                }
                
                // 如果有token,添加到请求头
                if (token) {
                    headers['Authorization'] = `Bearer ${token}`;
                    headers['X-Auth-Token'] = token;
                }
                
                // 精简日志输出
                if (CONFIG.DEBUG_MODE) {
                    UI.log(`[API] ${options.method || 'GET'} ${options.url}`);
                }
                
                const req = GM_xmlhttpRequest({
                    method: options.method || 'GET',
                    url: options.url,
                    headers: headers,
                    data: options.data,
                    timeout: 30000,
                    onload: function(response) {
                        if (CONFIG.DEBUG_MODE) {
                            UI.log(`[API] ${response.status} ${response.responseText?.substring(0, 100)}...`);
                        }
                        
                        try {
                            if (response.responseText && response.responseText.trim()) {
                                const data = JSON.parse(response.responseText);
                                resolve(data);
                            } else {
                                resolve({ status: response.status, message: 'Empty response' });
                            }
                        } catch (parseError) {
                            UI.log(`❌ JSON解析失败: ${parseError.message}`, 'error');
                            resolve({ error: 'JSON解析失败', raw: response.responseText });
                        }
                    },
                    onerror: function(error) {
                        UI.log(`❌ 请求失败: ${error.message}`, 'error');
                        reject(error);
                    },
                    ontimeout: function() {
                        UI.log(`❌ 请求超时`, 'error');
                        reject(new Error('请求超时'));
                    }
                });
                
                // 支持AbortController
                if (this.abortController) {
                    this.abortController.signal.addEventListener('abort', () => {
                        if (req.abort) {
                            req.abort();
                        }
                        reject(new DOMException('Aborted', 'AbortError'));
                    });
                }
            });
        },
        
        _extractToken: function() {
            // 尝试从多个位置提取认证token
            const sources = [
                () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.TOKEN),
                () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.AUTH_TOKEN),
                () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.ACCESS_TOKEN),
                () => sessionStorage.getItem(CONSTANTS.STORAGE_KEYS.TOKEN),
                () => sessionStorage.getItem(CONSTANTS.STORAGE_KEYS.AUTH_TOKEN),
                () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'),
                () => window.token,
                () => window.authToken,
                () => {
                    const match = document.cookie.match(CONSTANTS.COOKIE_PATTERNS.TOKEN);
                    return match ? match[1] : null;
                }
            ];
            
            for (const source of sources) {
                try {
                    const token = source();
                    if (token && token.length > 10) {
                        UI.log(`[Token] 找到认证token: ${token.substring(0, 20)}...`, 'debug');
                        return token;
                    }
                } catch (e) {
                    // 忽略提取错误
                }
            }
            
            UI.log('[Token] 未找到认证token', 'debug');
            return null;
        },

        /**
         * 进度上报 - 增强版,根据深度分析报告优化
         * 支持真实API优先,智能降级到模拟模式
         */
        reportProgress: async function(playInfo, currentTime) {
            try {
                EventBus.publish('log', { message: `[进度上报] 课程: ${playInfo.courseId}, 当前时间: ${currentTime}秒`, type: 'debug' });
                
                const isMockData = playInfo.videoId && playInfo.videoId.startsWith('mock_');
                const progressPercent = Math.round((currentTime / playInfo.duration) * 100);
                
                if (isMockData) {
                    EventBus.publish('log', { message: `[进度上报] 检测到模拟数据,将尝试真实API后再模拟`, type: 'debug' });
                }
                
                // 构建真实API调用方法(即使是模拟数据也要尝试)
                const reportMethods = [
                    // 方法1: 脉冲式进度上报(最接近真实用户行为)
                    () => this.pulseSaveRecord(playInfo, currentTime),
                    
                    // 方法2: 课程播放进度上报API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.REPORT_PROGRESS}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            locationSiteId: playInfo.coursewareId,
                            videoId: isMockData ? playInfo.courseId : playInfo.videoId, // 模拟数据时使用courseId
                            currentTime: currentTime,
                            watchPoint: this.secondsToTimeString(currentTime),
                            duration: playInfo.duration,
                            progress: progressPercent,
                            _t: Date.now()
                        }),
                        headers: {
                            'Content-Type': 'application/json',
                            'Accept': 'application/json, text/plain, */*'
                        }
                    }),
                    
                    // 方法3: 视频播放进度更新API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.UPDATE_PROGRESS}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            coursewareId: playInfo.coursewareId,
                            progress: progressPercent,
                            currentTime: currentTime,
                            lastWatchPoint: this.secondsToTimeString(currentTime),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),
                    
                    // 方法4: 学习记录保存API(适合模拟数据)
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.SAVE_STUDY_RECORD}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            videoId: isMockData ? playInfo.courseId : playInfo.videoId,
                            studyTime: currentTime,
                            totalTime: playInfo.duration,
                            finishRate: progressPercent,
                            watchPoint: this.secondsToTimeString(currentTime),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];
                
                // 如果不是模拟数据,添加视频服务API
                if (!isMockData) {
                    reportMethods.push(
                        () => this._request({
                            method: 'POST',
                            url: `${this._videoApiBaseUrl}${CONSTANTS.API_ENDPOINTS.VIDEO_PROGRESS}`,
                            data: JSON.stringify({
                                videoId: playInfo.videoId,
                                currentTime: currentTime,
                                duration: playInfo.duration,
                                progress: progressPercent,
                                _t: Date.now()
                            }),
                            headers: { 'Content-Type': 'application/json' }
                        })
                    );
                }
                
                // 尝试真实API调用
                const result = await this._tryApiEndpoints(
                    reportMethods,
                    `[进度上报] 成功 (${progressPercent}%)`,
                    `[进度上报] 所有API方法失败`
                );
                
                if (result) {
                    // 真实API成功
                    if (isMockData) {
                        EventBus.publish('log', { message: `[进度上报] 模拟数据成功调用真实API`, type: 'success' });
                    }
                    return result;
                }
                
                // 所有真实API都失败,使用兜底策略
                if (CONFIG.FALLBACK_MODE) {
                    if (isMockData) {
                        EventBus.publish('log', { message: `[模拟上报] 模拟进度上报: ${currentTime}秒 (${progressPercent}%)`, type: 'info' });
                    } else {
                        EventBus.publish('log', { message: `[进度上报] 启用兜底模式: ${currentTime}秒 (${progressPercent}%)`, type: 'warn' });
                    }
                    
                    // 模拟网络延迟
                    await new Promise(resolve => setTimeout(resolve, 500 + Math.random() * 1000));
                    return { 
                        code: 200, 
                        success: true,
                        msg: isMockData ? 'mock success' : 'fallback success',
                        data: { currentTime, progress: progressPercent }
                    };
                } else {
                    throw new Error('所有进度上报方法失败且兜底模式已禁用');
                }
                
            } catch (error) {
                if (error.name === 'AbortError') {
                    EventBus.publish('log', { message: `[进度上报] 已中止`, type: 'warn' });
                    throw error;
                }
                EventBus.publish('log', { message: `[进度上报] 发生错误: ${error.message}`, type: 'error' });
                
                // 最终兜底
                if (CONFIG.FALLBACK_MODE) {
                    return { code: 200, success: true, msg: 'error fallback success' };
                } else {
                    throw error;
                }
            }
        },

        // 使用优化后的API策略重构学习记录创建
        _createStudyRecord: async function(courseId) {
            try {
                UI.log(`[学习记录] 创建课程 ${courseId} 的学习记录`, 'debug');
                
                const createMethods = [
                    // 方法1: 开始学习记录
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.START_STUDY}`,
                        data: new URLSearchParams({
                            courseId: courseId,
                            startTime: new Date().toISOString(),
                            _t: Date.now()
                        }).toString(),
                        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                    }),
                    
                    // 方法2: 创建学习会话
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.CREATE_SESSION}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            sessionStart: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),
                    
                    // 方法3: 学习记录初始化
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.INIT_STUDY_RECORD}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            initTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];
                
                const result = await this._tryApiEndpoints(
                    createMethods,
                    `[学习记录] 创建成功`,
                    `[学习记录] 所有创建方法失败,将跳过记录创建`
                );
                
                return !!result;
                
            } catch (error) {
                if (error.name === 'AbortError') {
                    throw error;
                }
                UI.log(`[学习记录] 创建异常: ${error.message}`, 'warn');
                return false;
            }
        },

        // 使用优化后的API策略重构学习记录完成
        finishStudyRecord: async function(playInfo, finalTime) {
            try {
                UI.log(`[学习记录] 完成课程 ${playInfo.courseId} 的学习记录`, 'debug');
                
                const finishMethods = [
                    // 方法1: 完成学习API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.FINISH_STUDY}`,
                        data: new URLSearchParams({
                            courseId: playInfo.courseId,
                            coursewareId: playInfo.coursewareId,
                            studyTime: finalTime,
                            finishTime: new Date().toISOString(),
                            isFinished: true,
                            _t: Date.now()
                        }).toString(),
                        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                    }),
                    
                    // 方法2: 完成课程学习
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.COMPLETE_LEARNING}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            finalTime: finalTime,
                            duration: playInfo.duration,
                            completionRate: 100,
                            endTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),
                    
                    // 方法3: 保存完成状态
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.SAVE_COMPLETION_STATUS}`,
                        data: JSON.stringify({
                            courseId: playInfo.courseId,
                            status: 'completed',
                            completedAt: new Date().toISOString(),
                            totalTime: finalTime,
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];
                
                const result = await this._tryApiEndpoints(
                    finishMethods,
                    `[学习记录] 完成成功`,
                    `[学习记录] 所有完成方法失败,学习记录可能未正确保存`
                );
                
                return !!result;
                
            } catch (error) {
                if (error.name === 'AbortError') {
                    throw error;
                }
                UI.log(`[学习记录] 完成异常: ${error.message}`, 'warn');
                return false;
            }
        },

        // 使用优化后的API策略重构课程完成
        completeCourse: async function(courseInfo) {
            try {
                const courseId = courseInfo.id || courseInfo.courseId;
                UI.log(`[课程完成] 开始完成课程: ${courseId}`, 'debug');

                const completionMethods = [
                    // 方法1: 课程学习完成API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.COMPLETE_COURSE}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            completionTime: new Date().toISOString(),
                            studyDuration: courseInfo.durationStr || '00:30:00',
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法2: 学习进度完成API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.FINISH_LEARNING}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            status: 'completed',
                            progress: 100,
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法3: 课程状态更新API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.UPDATE_STATUS}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            status: 'finished',
                            finishTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法4: 学习记录提交API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.SUBMIT_LEARNING_RECORD}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            learningTime: courseInfo.durationStr || '00:30:00',
                            isCompleted: true,
                            submitTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    }),

                    // 方法5: 课程结业API
                    () => this._request({
                        method: 'POST',
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GRADUATE}`,
                        data: JSON.stringify({
                            courseId: courseId,
                            graduateTime: new Date().toISOString(),
                            _t: Date.now()
                        }),
                        headers: { 'Content-Type': 'application/json' }
                    })
                ];

                const result = await this._tryApiEndpoints(
                    completionMethods,
                    `[课程完成] 完成成功`,
                    `[课程完成] 所有方法失败,课程可能已通过其他方式完成`
                );

                return !!result;

            } catch (error) {
                if (error.name === 'AbortError') {
                    throw error;
                }
                UI.log(`[课程完成] 发生错误: ${error.message}`, 'error');
                return false;
            }
        },

        // 剩余的API方法(使用常量优化)
        
        getCourseListFromSpecialDetail: async () => {
            try {
                UI.log('检测到专栏详情页面,尝试获取课程列表...', 'info');
                
                const urlParams = new URLSearchParams(window.location.hash.split('?')[1]);
                const channelId = urlParams.get('id');
                
                if (!channelId) {
                    UI.log('未找到专栏ID', 'error');
                    return [];
                }
                
                UI.log(`专栏ID: ${channelId}`, 'debug');
                
                const url = `${CONSTANTS.API_ENDPOINTS.GET_PACK_BY_ID}?id=${channelId}&_t=${Date.now()}`;
                const response = await API._request({
                    method: 'GET',
                    url: `${API.getBaseUrl()}${url}`
                });
                
                if (!response.success || !response.data) {
                    UI.log('专栏API返回数据异常', 'error');
                    return [];
                }
                
                const channelData = response.data;
                UI.log(`专栏标题: ${channelData.title}`, 'info');
                
                const courseList = [];
                if (channelData.pdChannelUnitList) {
                    for (const unit of channelData.pdChannelUnitList) {
                        UI.log(`单元: ${unit.unitName} (${unit.totalPeriod}学时)`, 'debug');
                        
                        if (unit.subList) {
                            for (const course of unit.subList) {
                                if (course.typeValue === 'course') {
                                    courseList.push({
                                        id: course.businessId,
                                        courseId: course.businessId,
                                        dsUnitId: `unit_${unit.order}_${course.order}`,
                                        title: course.title,
                                        courseName: course.title,
                                        teacher: course.teacher,
                                        teacherJob: course.teacherjob,
                                        durationStr: course.duration,
                                        period: course.period,
                                        unit: unit.unitName,
                                        category: course.categoryText,
                                        publishTime: course.publishTime,
                                        order: course.order,
                                        unitOrder: unit.order,
                                        status: 'not_started'
                                    });
                                }
                            }
                        }
                    }
                }
                
                UI.log(`[专栏课程] 成功获取 ${courseList.length} 门课程`, 'info');
                return courseList;
                
            } catch (error) {
                UI.log(`获取专栏课程列表失败: ${error.message}`, 'error');
                return [];
            }
        },

        getCourseListFromChannel: async function(channelId) {
            try {
                UI.log(`正在从频道API获取课程列表 (ID: ${channelId})...`, 'info');
                
                const apiEndpoints = [
                    `${CONSTANTS.API_ENDPOINTS.GET_PACK_BY_ID}?id=${channelId}&_t=${Date.now()}`,
                    `/api/nc/channel/detail?id=${channelId}&_t=${Date.now()}`,
                    `/inc/nc/course/list?channelId=${channelId}&_t=${Date.now()}`,
                    `${CONSTANTS.API_ENDPOINTS.GET_COURSE_LIST}?channelId=${channelId}&_t=${Date.now()}`
                ];
                
                for (const endpoint of apiEndpoints) {
                    try {
                        UI.log(`尝试API端点: ${endpoint}`, 'debug');
                        const response = await API._request({
                            method: 'GET',
                            url: `${API.getBaseUrl()}${endpoint}`
                        });
                        
                        if (response && response.success && response.data) {
                            const courseList = [];
                            const data = response.data;
                            
                            if (data.pdChannelUnitList) {
                                for (const unit of data.pdChannelUnitList) {
                                    UI.log(`单元: ${unit.unitName} (${unit.totalPeriod}学时)`, 'debug');
                                    
                                    if (unit.subList) {
                                        for (const course of unit.subList) {
                                            if (course.typeValue === 'course') {
                                                courseList.push({
                                                    id: course.businessId,
                                                    courseId: course.businessId,
                                                    dsUnitId: `unit_${unit.order}_${course.order}`,
                                                    title: course.title,
                                                    courseName: course.title,
                                                    teacher: course.teacher,
                                                    teacherJob: course.teacherjob,
                                                    durationStr: course.duration,
                                                    period: course.period,
                                                    unit: unit.unitName,
                                                    category: course.categoryText,
                                                    publishTime: course.publishTime,
                                                    order: course.order,
                                                    unitOrder: unit.order,
                                                    status: 'not_started'
                                                });
                                            }
                                        }
                                    }
                                }
                            } else {
                                let courses = [];
                                if (data.courseList) {
                                    courses = data.courseList;
                                } else if (data.courses) {
                                    courses = data.courses;
                                } else if (data.list) {
                                    courses = data.list;
                                } else if (Array.isArray(data)) {
                                    courses = data;
                                }
                                
                                courses.forEach(course => {
                                    courseList.push({
                                        courseId: course.id || course.courseId,
                                        courseName: course.name || course.title || course.courseName,
                                        durationStr: course.duration || course.durationStr || '00:30:00',
                                        status: course.status || 'not_started'
                                    });
                                });
                            }
                            
                            if (courseList.length > 0) {
                                UI.log(`✅ 从频道API获取到 ${courseList.length} 门课程`, 'info');
                                return courseList;
                            }
                        }
                    } catch (error) {
                        UI.log(`API端点 ${endpoint} 失败: ${error.message}`, 'debug');
                    }
                }
                
                UI.log('❌ 所有频道API端点都失败了', 'warn');
                return [];
            } catch (error) {
                UI.log(`❌ 获取频道课程列表失败: ${error.message}`, 'error');
                return [];
            }
        },

        getCourseList: async () => {
            try {
                UI.log('正在获取课程列表...', 'info');
                
                const waitForVueApp = async () => {
                    let attempts = 0;
                    const maxAttempts = 15;
                    
                    while (attempts < maxAttempts) {
                        const app = document.querySelector(CONSTANTS.SELECTORS.APP);
                        if (app && (window.Vue || app.__vue__ || app._vnode)) {
                            UI.log('✅ 检测到Vue.js应用已加载', 'debug');
                            return true;
                        }
                        
                        const hasContent = document.querySelectorAll('.el-card, [class*="course"], [class*="item"], [class*="card"]').length > 0;
                        if (hasContent) {
                            UI.log('✅ 检测到动态内容已加载', 'debug');
                            return true;
                        }
                        
                        UI.log(`⏳ 等待Vue.js应用加载... (${attempts + 1}/${maxAttempts})`, 'debug');
                        await new Promise(resolve => setTimeout(resolve, 1000));
                        attempts++;
                    }
                    
                    UI.log('⚠️ Vue.js应用加载超时,继续尝试获取课程', 'warn');
                    return false;
                };
                
                await waitForVueApp();
                
                const currentUrl = window.location.href;
                UI.log(`当前页面URL: ${currentUrl}`, 'debug');
                
                if (currentUrl.includes('/specialdetail')) {
                    UI.log('检测到专栏详情页面,尝试从API获取课程列表...', 'info');
                    return await API.getCourseListFromSpecialDetail();
                }
                
                if (currentUrl.includes('channelDetail')) {
                    UI.log('检测到频道详情页面,尝试从API获取课程列表...', 'info');
                    
                    let channelId = null;
                    try {
                        const hash = window.location.hash;
                        if (hash.includes('?')) {
                            const urlParams = new URLSearchParams(hash.split('?')[1]);
                            channelId = urlParams.get('id');
                        }
                        
                        if (!channelId && currentUrl.includes('?')) {
                            const urlParams = new URLSearchParams(currentUrl.split('?')[1]);
                            channelId = urlParams.get('id');
                        }
                        
                        if (!channelId) {
                            const match = currentUrl.match(/[?&]id=([^&]+)/);
                            if (match) {
                                channelId = match[1];
                            }
                        }
                    } catch (error) {
                        UI.log(`解析频道ID失败: ${error.message}`, 'debug');
                    }
                    
                    if (channelId) {
                        UI.log(`频道ID: ${channelId}`, 'debug');
                        return await API.getCourseListFromChannel(channelId);
                    } else {
                        UI.log('❌ 无法从URL中提取频道ID', 'warn');
                    }
                }
                
                let courseList = [];
                let courseElements = [];
                
                await new Promise(resolve => setTimeout(resolve, 3000));
                
                UI.log(`🔍 页面调试信息:`, 'debug');
                UI.log(`- 页面标题: ${document.title}`, 'debug');
                UI.log(`- ${CONSTANTS.SELECTORS.APP}元素: ${document.querySelector(CONSTANTS.SELECTORS.APP) ? '存在' : '不存在'}`, 'debug');
                UI.log(`- Vue实例: ${window.Vue ? '存在' : '不存在'}`, 'debug');
                UI.log(`- 所有元素数量: ${document.querySelectorAll('*').length}`, 'debug');
                
                for (const selector of CONSTANTS.COURSE_SELECTORS) {
                    const elements = document.querySelectorAll(selector);
                    UI.log(`🔍 选择器 "${selector}": ${elements.length} 个元素`, 'debug');
                    if (elements.length > 0) {
                        courseElements = elements;
                        UI.log(`📋 使用选择器 "${selector}" 找到 ${elements.length} 个课程元素`, 'info');
                        break;
                    }
                }
                
                if (courseElements.length === 0) {
                    for (const selector of CONSTANTS.FALLBACK_SELECTORS) {
                        const elements = document.querySelectorAll(selector);
                        if (elements.length > 0) {
                            courseElements = elements;
                            UI.log(`📋 使用备用选择器 "${selector}" 找到 ${elements.length} 个元素`, 'info');
                            break;
                        }
                    }
                }
                
                UI.log(`[DOM解析] 找到 ${courseElements.length} 个课程元素`, 'debug');
                
                courseElements.forEach((el, index) => {
                    const courseData = {
                        courseId: el.getAttribute('data-course-id') || 
                                 el.getAttribute('data-id') || 
                                 el.querySelector('[data-course-id]')?.getAttribute('data-course-id') ||
                                 `course_${index}`,
                        dsUnitId: el.getAttribute('data-unit-id') || 
                                 el.getAttribute('data-dsunit') ||
                                 el.querySelector('[data-unit-id]')?.getAttribute('data-unit-id') ||
                                 `unit_${index}`,
                        courseName: el.getAttribute('title') || 
                                   el.querySelector('.course-title, .lesson-title, h3, h4')?.textContent?.trim() ||
                                   el.textContent?.trim()?.split('\n')[0] ||
                                   `课程${index + 1}`,
                        durationStr: el.querySelector('.duration, .time, [class*="time"]')?.textContent?.trim() || '00:30:00',
                        status: el.getAttribute('data-status') || 'not_started'
                    };
                    
                    if (courseData.courseName && courseData.courseName.length > 5) {
                        courseList.push(courseData);
                    }
                });
                
                if (courseList.length === 0) {
                    try {
                        const apiUrl = `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_COURSE_LIST}`;
                        const apiResponse = await API._request({
                            method: 'GET',
                            url: apiUrl + '?' + new URLSearchParams({
                                _t: Date.now(),
                                page: 1,
                                size: 50
                            }).toString()
                        });
                        
                        if (apiResponse.success && apiResponse.data) {
                            const apiCourses = Array.isArray(apiResponse.data) ? apiResponse.data : 
                                              apiResponse.data.list || apiResponse.data.records || [];
                            courseList = apiCourses.map((course, index) => ({
                                courseId: course.id || course.courseId || `api_course_${index}`,
                                dsUnitId: course.dsUnitId || course.unitId || `api_unit_${index}`,
                                courseName: course.name || course.title || course.courseName || `API课程${index + 1}`,
                                durationStr: course.duration || course.timeLength || '00:30:00',
                                status: course.status || 'not_started'
                            }));
                        }
                    } catch (apiError) {
                        UI.log(`[API获取课程] 失败: ${apiError.message}`, 'debug');
                    }
                }
                
                if (courseList.length === 0) {
                    const videoElements = document.querySelectorAll(CONSTANTS.VIDEO_SELECTORS.join(', '));
                    UI.log(`[视频元素分析] 找到 ${videoElements.length} 个视频元素`, 'debug');
                    
                    videoElements.forEach((el, index) => {
                        const courseData = {
                            courseId: el.getAttribute('data-course-id') || `video_course_${index}`,
                            dsUnitId: el.getAttribute('data-unit-id') || `video_unit_${index}`,
                            courseName: document.title || `视频课程${index + 1}`,
                            durationStr: el.getAttribute('duration') || '00:30:00',
                            status: 'not_started',
                            videoElement: el
                        };
                        courseList.push(courseData);
                    });
                }
                
                const uniqueCourses = courseList.filter((course, index, self) => 
                    index === self.findIndex(c => c.courseId === course.courseId)
                );
                
                UI.log(`[课程列表] 获取到 ${uniqueCourses.length} 门课程`, 'info');
                uniqueCourses.forEach(course => {
                    UI.log(`- ${course.courseName} (${course.courseId})`, 'debug');
                });
                
                return uniqueCourses;
                
            } catch (error) {
                UI.log(`获取课程列表失败: ${error.message}`, 'error');
                return [];
            }
        },

        /**
         * 获取课程播放信息 - 增强版,支持多种数据源和智能降级
         * 根据深度分析报告优化:处理API空数据、增强videoId提取、智能时长解析
         */
        getPlayInfo: async (courseId, dsUnitId, courseDuration) => {
            try {
                EventBus.publish('log', { message: `[getPlayInfo] 开始获取课程 ${courseId} 的播放信息`, type: 'debug' });
                
                // 策略1: 尝试从播放趋势API获取数据
                let playTrendData = null;
                try {
                    const playTrendParams = new URLSearchParams({
                        courseId: courseId,
                        _t: Date.now()
                    });
                    playTrendData = await API._request({
                        method: 'GET',
                        url: `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_PLAY_TREND}?${playTrendParams.toString()}`
                    });
                    EventBus.publish('log', { message: `[getPlayTrend] 响应数据: ${JSON.stringify(playTrendData)}`, type: 'debug' });
                } catch (error) {
                    EventBus.publish('log', { message: `[getPlayTrend] API调用失败: ${error.message}`, type: 'warn' });
                }

                let duration = 0;
                let lastLearnedTime = 0;
                let videoId = null;
                let coursewareId = null;
                let dataSource = 'unknown';

                // 检查API是否返回有效数据(根据深度分析报告的建议)
                if (playTrendData?.success && playTrendData?.data && 
                    Object.keys(playTrendData.data).length > 0 && playTrendData.data.locationSite) {
                    
                    dataSource = 'api';
                    const trendData = playTrendData.data;
                    const locationSite = trendData.locationSite;
                    
                    // 提取时长信息
                    duration = locationSite.sumDurationLong || locationSite.duration || 0;
                    
                    // 提取学习进度
                    if (locationSite.lastWatchPoint) {
                        lastLearnedTime = API.parseTimeToSeconds(locationSite.lastWatchPoint);
                    } else if (locationSite.lastLearnedTime) {
                        lastLearnedTime = locationSite.lastLearnedTime;
                    }
                    
                    coursewareId = locationSite.id;
                    
                    // 增强的videoId提取逻辑(根据学习记录API修复报告)
                    const videoIdSources = [
                        // 方法1: 从fileUrl中提取
                        () => {
                            if (locationSite.fileUrl) {
                                try {
                                    const fileUrlData = JSON.parse(locationSite.fileUrl);
                                    if (fileUrlData.file?.[0]?.id) {
                                        return fileUrlData.file[0].id;
                                    }
                                    if (fileUrlData.videoId) {
                                        return fileUrlData.videoId;
                                    }
                                } catch (e) {
                                    EventBus.publish('log', { message: `[videoId提取] fileUrl解析失败: ${e.message}`, type: 'debug' });
                                }
                            }
                            return null;
                        },
                        
                        // 方法2: 从fileAdditionUrl中提取
                        () => {
                            if (locationSite.fileAdditionUrl) {
                                try {
                                    const additionData = JSON.parse(locationSite.fileAdditionUrl);
                                    if (additionData.videoId) {
                                        return additionData.videoId;
                                    }
                                } catch (e) {
                                    EventBus.publish('log', { message: `[videoId提取] fileAdditionUrl解析失败: ${e.message}`, type: 'debug' });
                                }
                            }
                            return null;
                        },
                        
                        // 方法3: 从playTree中查找
                        () => {
                            if (trendData.playTree?.children) {
                                for (const child of trendData.playTree.children) {
                                    if (child.rTypeValue === 'video' && child.id) {
                                        coursewareId = child.id;
                                        return child.videoId || child.id;
                                    }
                                }
                            }
                            return null;
                        }
                    ];
                    
                    // 尝试各种方法提取videoId
                    for (const source of videoIdSources) {
                        try {
                            const extractedId = source();
                            if (extractedId) {
                                videoId = extractedId;
                                EventBus.publish('log', { message: `[videoId提取] 成功提取: ${videoId}`, type: 'debug' });
                                break;
                            }
                        } catch (e) {
                            EventBus.publish('log', { message: `[videoId提取] 方法失败: ${e.message}`, type: 'debug' });
                        }
                    }
                    
                    EventBus.publish('log', { message: `[getPlayInfo] 使用API数据源`, type: 'info' });
                } else {
                    // 策略2: API返回空数据,使用课程列表数据(根据深度分析报告)
                    dataSource = 'courselist';
                    EventBus.publish('log', { message: `[getPlayInfo] API返回空数据,使用课程列表数据源`, type: 'warn' });
                }

                // 时长处理:优先使用API数据,否则解析课程列表时长
                if (duration === 0 && courseDuration) {
                    duration = API.parseDuration(courseDuration);
                    EventBus.publish('log', { message: `[getPlayInfo] 从课程列表解析时长: ${courseDuration} → ${duration}秒`, type: 'debug' });
                }

                // 兜底时长
                if (duration === 0) {
                    duration = CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
                    EventBus.publish('log', { message: `[getPlayInfo] 使用默认时长: ${duration}秒`, type: 'debug' });
                }

                // 尝试通过课件详情API获取videoId
                if (!videoId && coursewareId) {
                    try {
                        const coursewareDetail = await API._request({
                            method: 'GET',
                            url: `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_COURSEWARE_DETAIL}?coursewareId=${coursewareId}&_t=${Date.now()}`
                        });
                        
                        if (coursewareDetail?.success && coursewareDetail?.data?.videoId) {
                            videoId = coursewareDetail.data.videoId;
                            EventBus.publish('log', { message: `[getPlayInfo] 从课件详情获取videoId: ${videoId}`, type: 'debug' });
                        }
                    } catch (e) {
                        EventBus.publish('log', { message: `[getPlayInfo] 查询课件详情失败: ${e.message}`, type: 'debug' });
                    }
                }

                // 生成模拟videoId(但会尝试真实API)
                if (!videoId) {
                    videoId = `mock_video_${courseId}`;
                    EventBus.publish('log', { message: `[getPlayInfo] 生成模拟videoId: ${videoId}`, type: 'debug' });
                }

                const playInfo = {
                    courseId: courseId,
                    coursewareId: coursewareId || dsUnitId,
                    videoId: videoId,
                    duration: duration,
                    lastLearnedTime: lastLearnedTime,
                    playURL: videoId.startsWith('mock_') ? 'mock://play.url' : `https://zpyapi.shsets.com/player/get?videoId=${videoId}`,
                    dataSource: dataSource // 添加数据源标识
                };

                EventBus.publish('log', { message: `[getPlayInfo] 最终播放信息 (${dataSource}): ${JSON.stringify(playInfo)}`, type: 'debug' });
                return playInfo;

            } catch (error) {
                EventBus.publish('log', { message: `[getPlayInfo] 错误: ${error.message}`, type: 'error' });
                
                // 完全兜底方案
                const duration = courseDuration ? API.parseDuration(courseDuration) : CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
                return {
                    courseId: courseId,
                    coursewareId: dsUnitId,
                    videoId: `mock_video_${courseId}`,
                    duration: duration,
                    lastLearnedTime: 0,
                    playURL: 'mock://play.url',
                    dataSource: 'fallback'
                };
            }
        },

        parseTimeToSeconds: (timeStr) => {
            try {
                const parts = timeStr.split(':').map(part => parseInt(part, 10));
                if (parts.length === 3) {
                    return parts[0] * 3600 + parts[1] * 60 + parts[2];
                }
                return 0;
            } catch (e) {
                return 0;
            }
        },

        parseDuration: (durationStr) => {
            if (!durationStr || typeof durationStr !== 'string') return CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
            const timeParts = durationStr.split(':');
            if (timeParts.length === 3) {
                const hours = parseInt(timeParts[0]) || 0;
                const minutes = parseInt(timeParts[1]) || 0;
                const seconds = parseInt(timeParts[2]) || 0;
                return hours * 3600 + minutes * 60 + seconds;
            }
            return CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
        },

        pulseSaveRecord: async (playInfo, currentTime) => {
            const watchPoint = API.secondsToTimeString(currentTime);
            const payload = new URLSearchParams({
                courseId: playInfo.courseId,
                coursewareId: playInfo.coursewareId || playInfo.videoId,
                watchPoint: watchPoint,
                pulseTime: 10,
                pulseRate: 1
            }).toString();
            
            UI.log(`[脉冲上报] ${watchPoint} (${Math.round((currentTime / playInfo.duration) * 100)}%)`, 'info');
            
            return await API._request({
                method: 'POST',
                url: `${API.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.PULSE_SAVE_RECORD}`,
                data: payload,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            });
        },

        secondsToTimeString: (seconds) => {
            const hours = Math.floor(seconds / 3600);
            const minutes = Math.floor((seconds % 3600) / 60);
            const secs = seconds % 60;
            return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        },

        antiCheatCheck: async (courseId, userId) => {
            try {
                UI.log(`[防刷课检查] 课程ID: ${courseId}`, 'debug');
                
                const pushUrl = `${CONSTANTS.API_ENDPOINTS.PUSH_COURSE}?user_id=${userId}&course_id=${courseId}&_t=${Date.now()}`;
                const pushResponse = await API._request({
                    method: 'GET',
                    url: `${API.getBaseUrl()}${pushUrl}`
                });
                
                const checkUrl = `${CONSTANTS.API_ENDPOINTS.GET_COURSE_BY_USER}?user_id=${userId}&course_id=${courseId}&_t=${Date.now()}`;
                const checkResponse = await API._request({
                    method: 'GET',
                    url: `${API.getBaseUrl()}${checkUrl}`
                });
                
                UI.log(`[防刷课检查结果] Push: ${pushResponse?.message || '未知'}, Check: ${checkResponse?.message || '未知'}`, 'debug');
                
                return {
                    pushOk: pushResponse?.success === true,
                    checkOk: checkResponse?.success === true
                };
            } catch (error) {
                UI.log(`[防刷课检查失败] ${error.message}`, 'error');
                return { pushOk: false, checkOk: false };
            }
        },

        extractUserId: () => {
            try {
                const cookieMatch = document.cookie.match(CONSTANTS.COOKIE_PATTERNS.USER_ID);
                if (cookieMatch) {
                    UI.log(`[用户ID提取] 从Cookie获取: ${cookieMatch[1]}`, 'debug');
                    return cookieMatch[1];
                }
                
                const userIdElement = document.querySelector('[data-user-id]');
                if (userIdElement) {
                    const userId = userIdElement.getAttribute('data-user-id');
                    UI.log(`[用户ID提取] 从DOM获取: ${userId}`, 'debug');
                    return userId;
                }
                
                const urlParams = new URLSearchParams(window.location.search);
                const userId = urlParams.get('user_id');
                if (userId) {
                    UI.log(`[用户ID提取] 从URL获取: ${userId}`, 'debug');
                    return userId;
                }
                
                const storedUserId = localStorage.getItem(CONSTANTS.STORAGE_KEYS.USER_ID) || localStorage.getItem(CONSTANTS.STORAGE_KEYS.USER_ID_ALT);
                if (storedUserId) {
                    UI.log(`[用户ID提取] 从localStorage获取: ${storedUserId}`, 'debug');
                    return storedUserId;
                }
                
                const pMatch = document.cookie.match(CONSTANTS.COOKIE_PATTERNS.P_PARAM);
                if (pMatch) {
                    UI.log(`[用户ID提取] 从_p参数获取: ${pMatch[1]}`, 'debug');
                    return pMatch[1];
                }
                
                UI.log('[用户ID提取] 未找到用户ID', 'warn');
                return null;
            } catch (error) {
                UI.log(`[用户ID提取失败] ${error.message}`, 'error');
                return null;
            }
        },

        async checkCourseCompletion(courseId) {
            try {
                UI.log(`[完成度检查] 检查课程 ${courseId} 的完成状态`);
                
                const playTrend = await this._request({
                    url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_PLAY_TREND}?courseId=${courseId}&_t=${Date.now()}`,
                    method: 'GET'
                });
                
                if (playTrend && playTrend.success && playTrend.data) {
                    const { locationSite } = playTrend.data;
                    if (locationSite && locationSite.finishedRate !== undefined) {
                        const finishedRate = parseInt(locationSite.finishedRate);
                        UI.log(`[完成度检查] 课程完成度: ${finishedRate}%`);
                        
                        if (finishedRate >= CONFIG.COMPLETION_THRESHOLD) {
                            UI.log(`[完成度检查] 课程已完成 (${finishedRate}% >= ${CONFIG.COMPLETION_THRESHOLD}%)`, 'success');
                            return { isCompleted: true, finishedRate, method: 'playTrend' };
                        }
                        
                        return { isCompleted: false, finishedRate, method: 'playTrend' };
                    }
                }
                
                try {
                    const studyRecord = await this._request({
                        url: `${this.getBaseUrl()}${CONSTANTS.API_ENDPOINTS.GET_STUDY_RECORD}?courseId=${courseId}&_t=${Date.now()}`,
                        method: 'GET'
                    });
                    
                    if (studyRecord && studyRecord.success && studyRecord.data) {
                        const isFinished = studyRecord.data.isFinished === true || studyRecord.data.status === 'completed';
                        if (isFinished) {
                            UI.log(`[完成度检查] 学习记录显示课程已完成`, 'success');
                            return { isCompleted: true, finishedRate: 100, method: 'studyRecord' };
                        }
                    }
                } catch (error) {
                    UI.log(`[完成度检查] 学习记录API检查失败: ${error.message}`, 'warn');
                }
                
                return { isCompleted: false, finishedRate: 0, method: 'default' };
                
            } catch (error) {
                UI.log(`[完成度检查] 检查失败: ${error.message}`, 'error');
                return { isCompleted: false, finishedRate: 0, method: 'error' };
            }
        },

        /**
         * 智能学习课程 - 根据当前进度自动选择最佳学习策略
         * 
         * @param {Object} courseInfo - 课程信息对象
         * @param {string} courseInfo.courseId - 课程ID
         * @param {string} courseInfo.coursewareId - 课件ID
         * @param {string} courseInfo.videoId - 视频ID
         * @param {number} courseInfo.duration - 课程总时长(秒)
         * @param {number} courseInfo.lastLearnedTime - 上次学习时间点(秒)
         * @param {string} courseInfo.title - 课程标题
         * @returns {Promise<boolean>} 学习是否成功
         */
        async smartLearnCourse(courseInfo) {
            const { courseId, coursewareId, videoId, duration, lastLearnedTime } = courseInfo;
            const currentProgress = Math.floor((lastLearnedTime / duration) * 100);
            
            EventBus.publish('log', { message: `[智能学习] 课程: ${courseInfo.title || courseId}`, type: 'info' });
            EventBus.publish('log', { message: `[智能学习] 当前进度: ${currentProgress}% (${Utils.formatTime(lastLearnedTime)}/${Utils.formatTime(duration)})`, type: 'info' });
            
            let strategy = 'normal';
            
            if (currentProgress < 10) {
                strategy = 'slow_start';
                UI.log(`[智能学习] 策略: 慢启动 - 从头开始学习`);
            } else if (currentProgress < 50) {
                strategy = 'progressive';
                UI.log(`[智能学习] 策略: 渐进式 - 从${currentProgress}%继续`);
            } else if (currentProgress < 80) {
                strategy = 'fast_finish';
                UI.log(`[智能学习] 策略: 快速完成 - 直接跳跃到结束`);
            } else {
                strategy = 'final_push';
                UI.log(`[智能学习] 策略: 最后冲刺 - 完成剩余部分`);
            }
            
            await this._createStudyRecord(courseId);
            
            let currentTime = lastLearnedTime;
            let success = false;
            
            switch (strategy) {
                case 'slow_start':
                    const step1 = Math.floor(duration * 0.2);
                    const step2 = Math.floor(duration * 0.35);
                    const step3 = Math.floor(duration * 0.5);
                    
                    for (const targetTime of [step1, step2, step3]) {
                        if (Learner.stopRequested) {
                            UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                            return false;
                        }
                        
                        await new Promise(resolve => setTimeout(resolve, 8000));
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, targetTime);
                        if (!success) break;
                        currentTime = targetTime;
                        UI.log(`[慢启动] 进度: ${Math.floor((currentTime/duration)*100)}%`);
                    }
                    
                    if (success && !Learner.stopRequested) {
                        await new Promise(resolve => setTimeout(resolve, 5000));
                        currentTime = duration - 10;
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, currentTime);
                    }
                    break;
                    
                case 'progressive':
                    const remaining = duration - currentTime;
                    const stepSize = Math.floor(remaining / 5);
                    
                    for (let i = 1; i <= 5; i++) {
                        if (Learner.stopRequested) {
                            UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                            return false;
                        }
                        
                        await new Promise(resolve => setTimeout(resolve, 6000));
                        const nextTime = Math.min(currentTime + (stepSize * i), duration - 10);
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, nextTime);
                        if (!success) break;
                        currentTime = nextTime;
                        UI.log(`[渐进式] 步骤 ${i}/5: ${Math.floor((currentTime/duration)*100)}%`);
                    }
                    break;
                    
                case 'fast_finish':
                    if (Learner.stopRequested) {
                        UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                        return false;
                    }
                    
                    const target95 = Math.floor(duration * 0.95);
                    await new Promise(resolve => setTimeout(resolve, 3000));
                    success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, target95);
                    
                    if (success && !Learner.stopRequested) {
                        await new Promise(resolve => setTimeout(resolve, 5000));
                        currentTime = duration - 10;
                        success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, currentTime);
                    }
                    break;
                    
                case 'final_push':
                    if (Learner.stopRequested) {
                        UI.log('⏹️ 收到停止请求,中断智能学习', 'warn');
                        return false;
                    }
                    
                    await new Promise(resolve => setTimeout(resolve, 3000));
                    currentTime = duration - 10;
                    success = await this.reportProgress({ courseId, coursewareId, videoId, duration }, currentTime);
                    break;
            }
            
            if (success) {
                UI.log(`✅ 智能学习完成: ${courseInfo.title || courseId}`, 'success');
                
                try {
                    await this.finishStudyRecord(courseInfo, currentTime);
                    await this.completeCourse(courseInfo);
                    UI.log(`✅ 学习记录已保存`, 'success');
                } catch (error) {
                    UI.log(`⚠️ 学习记录保存失败: ${error.message}`, 'warn');
                }
                
                return true;
            } else {
                UI.log(`❌ 智能学习失败`, 'error');
                return false;
            }
        }
    };

    // --- 主控制逻辑(增强版) ---
    const Learner = {
        isRunning: false,
        stopRequested: false,
        
        stop: function() {
            this.isRunning = false;
            this.stopRequested = true;
            
            // 使用AbortController真正中止所有正在进行的请求
            if (API.abortController) {
                API.abortController.abort();
                UI.log('🛑 正在中止所有网络请求...', 'info');
            }
            
            const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
            if (toggleBtn) {
                toggleBtn.setAttribute('data-state', 'stopped');
                toggleBtn.textContent = '开始学习';
            }
            UI.updateStatus('已停止');
            UI.log('⏹️ 学习流程已停止', 'warn');
        },
        
        // 跳过已完成课程的独立功能
        async skipCompletedCourses() {
            try {
                UI.log('🔍 开始检查并跳过已完成的课程...');
                UI.updateStatus('检查已完成课程');
                
                // 获取课程列表
                const courses = await API.getCourseList();
                if (!courses || courses.length === 0) {
                    UI.log('❌ 未找到课程列表', 'error');
                    return;
                }
                
                let completedCount = 0;
                
                for (let i = 0; i < courses.length; i++) {
                    const course = courses[i];
                    const courseId = course.id || course.courseId;
                    
                    UI.log(`检查第 ${i + 1}/${courses.length} 门课程: ${course.title}`);
                    
                    try {
                        const completionCheck = await API.checkCourseCompletion(courseId);
                        if (completionCheck.isCompleted) {
                            UI.log(`✅ 已完成: ${course.title} (${completionCheck.finishedRate}%)`, 'success');
                            completedCount++;
                        } else {
                            UI.log(`📖 未完成: ${course.title} (${completionCheck.finishedRate}%)`);
                        }
                    } catch (error) {
                        UI.log(`❌ 检查失败: ${course.title} - ${error.message}`, 'error');
                    }
                    
                    // 更新进度
                    const progress = Math.floor(((i + 1) / courses.length) * 100);
                    UI.updateProgress(progress);
                }
                
                UI.log(`\n📊 检查完成: ${completedCount}/${courses.length} 门课程已完成`, 'success');
                UI.updateStatus(`检查完成 - ${completedCount}/${courses.length} 已完成`);
                
            } catch (error) {
                UI.log(`❌ 检查过程出错: ${error.message}`, 'error');
                UI.updateStatus('检查失败');
            }
        },

        // 更新主学习流程 - 增强已完成课程检查和统计信息
        async processCourses(courses) {
            UI.log(`发现 ${courses.length} 门课程,开始处理...`);
            UI.updateStatus('处理课程列表');
            
            // 初始化统计信息
            const stats = {
                total: courses.length,
                completed: 0,
                learned: 0,
                failed: 0,
                skipped: 0
            };
            
            UI.updateStatistics(stats);
            
            for (let i = 0; i < courses.length; i++) {
                // 检查是否收到停止请求
                if (this.stopRequested) {
                    UI.log('⏹️ 收到停止请求,中断学习流程', 'warn');
                    break;
                }
                
                const course = courses[i];
                UI.log(`\n📚 处理第 ${i + 1}/${courses.length} 门课程: ${course.title}`);
                UI.updateStatus(`学习课程 ${i + 1}/${courses.length}`);
                
                try {
                    const courseId = course.id || course.courseId;
                    
                    // 首先检查课程是否已完成
                    if (CONFIG.SKIP_COMPLETED_COURSES) {
                        const completionCheck = await API.checkCourseCompletion(courseId);
                        if (completionCheck.isCompleted) {
                            UI.log(`✅ 课程已完成,跳过: ${course.title} (${completionCheck.finishedRate}%)`, 'success');
                            stats.completed++;
                            UI.updateStatistics(stats);
                            continue;
                        }
                    }
                    
                    // 获取课程播放信息
                    const playInfo = await API.getPlayInfo(courseId, course.dsUnitId, course.durationStr);
                    if (!playInfo) {
                        UI.log(`❌ 无法获取课程播放信息,跳过: ${course.title}`, 'error');
                        stats.failed++;
                        UI.updateStatistics(stats);
                        continue;
                    }
                    
                    // 双重检查:通过播放信息再次确认完成状态
                    const progressPercent = Math.floor((playInfo.lastLearnedTime / playInfo.duration) * 100);
                    if (progressPercent >= CONFIG.COMPLETION_THRESHOLD) {
                        UI.log(`✅ 播放信息确认课程已完成,跳过: ${course.title} (${progressPercent}%)`, 'success');
                        stats.completed++;
                        UI.updateStatistics(stats);
                        continue;
                    }
                    
                    // 开始学习课程
                    const courseInfoWithPlayInfo = {
                        ...course,
                        ...playInfo,
                        title: course.title || course.courseName,
                        courseId: courseId
                    };
                    
                    // 使用智能学习策略
                    const success = await API.smartLearnCourse(courseInfoWithPlayInfo);
                    
                    if (success) {
                        UI.log(`✅ 课程学习完成: ${course.title}`, 'success');
                        stats.learned++;
                    } else {
                        UI.log(`❌ 课程学习失败: ${course.title}`, 'error');
                        stats.failed++;
                    }
                    
                    UI.updateStatistics(stats);
                    
                    // 更新总体进度
                    const overallProgress = Math.floor(((i + 1) / courses.length) * 100);
                    UI.updateProgress(overallProgress);
                    
                    // 课程间隔等待(分段检查停止请求)
                    if (i < courses.length - 1) {
                        const delay = Math.random() * 8000 + 12000; // 12-20秒随机间隔
                        UI.log(`⏳ 等待 ${Math.round(delay/1000)} 秒后处理下一门课程...`);
                        
                        // 分段等待,每秒检查一次停止请求
                        const delaySeconds = Math.round(delay / 1000);
                        for (let j = 0; j < delaySeconds; j++) {
                            if (this.stopRequested) {
                                UI.log('⏹️ 等待期间收到停止请求,中断学习流程', 'warn');
                                return;
                            }
                            await new Promise(resolve => setTimeout(resolve, 1000));
                        }
                    }
                    
                } catch (error) {
                    UI.log(`❌ 处理课程 ${course.title} 时出错: ${error.message}`, 'error');
                    stats.failed++;
                    UI.updateStatistics(stats);
                    continue;
                }
            }
            
            // 显示学习统计
            UI.log(`\n🎉 所有课程处理完成!`, 'success');
            UI.log(`📊 学习统计:`);
            UI.log(`   ✅ 已完成课程: ${stats.completed} 门`);
            UI.log(`   📚 新学完课程: ${stats.learned} 门`);
            UI.log(`   ❌ 失败课程: ${stats.failed} 门`);
            UI.log(`   📖 总课程数: ${stats.total} 门`);
            
            UI.updateStatus(`完成 - ${stats.completed + stats.learned}/${stats.total} 门课程`);
            UI.updateProgress(100);
        },
        
        // 主流程(增强版)
        async startLearning() {
            try {
                // 重置停止标志并创建新的AbortController
                this.stopRequested = false;
                API.abortController = new AbortController();
                UI.log('开始学习流程...');
                
                // 获取课程列表
                const courses = await API.getCourseList();
                if (!courses || courses.length === 0) {
                    UI.log('❌ 未找到课程列表', 'error');
                    return;
                }
                
                // 使用新的课程处理方法
                await this.processCourses(courses);
                
                // 学习完成后重置按钮状态
                if (!this.stopRequested) {
                    const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
                    toggleBtn.setAttribute('data-state', 'stopped');
                    toggleBtn.textContent = '开始学习';
                    UI.updateStatus('学习完成');
                }
                
            } catch (error) {
                UI.log(`❌ 学习流程出错: ${error.message}`, 'error');
                console.error('学习流程错误:', error);
                
                // 出错时也要重置按钮状态
                const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
                toggleBtn.setAttribute('data-state', 'stopped');
                toggleBtn.textContent = '开始学习';
                UI.updateStatus('学习出错');
            }
        }
    };

    // --- 初始化 (v2.0优化) ---
    function init() {
        // 1. 加载用户配置
        Settings.load();
        
        // 2. 创建UI面板(会自动初始化事件监听器)
        UI.createPanel();
        
        // 3. 注册菜单命令
        GM_registerMenuCommand('导出调试日志', UI.exportLogs, 'e');
        
        // 4. 绑定主要控制按钮
        const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace('#', ''));
        toggleBtn.addEventListener('click', () => {
            const isRunning = toggleBtn.getAttribute('data-state') === 'running';
            if (isRunning) {
                Learner.stop();
            } else {
                // 更新按钮状态
                toggleBtn.setAttribute('data-state', 'running');
                toggleBtn.textContent = '停止学习';
                
                // 使用事件驱动更新状态
                EventBus.publish('statusUpdate', '学习中...');
                
                // 启动学习流程
                Learner.startLearning().catch(error => {
                    EventBus.publish('log', { message: `❌ 启动学习流程失败: ${error.message}`, type: 'error' });
                    Learner.stop();
                });
            }
        });
        
        // 5. 发布初始化完成事件
        EventBus.publish('log', { message: '🚀 API学习助手 v3.37.0 初始化完成', type: 'success' });
        EventBus.publish('log', { message: '✨ 新特性: 事件驱动架构 + 用户配置界面 + 策略模式', type: 'info' });
    }

    // 初始化环境检测和脚本
    function initScript() {
        detectEnvironment();
        init();
    }
    
    setTimeout(initScript, 2000);

})();