Greasy Fork

来自缓存

Greasy Fork is available in English.

优学院DGUT版

适配DGUT优学院(自动静音播放、自动做练习题、自动翻页、修改播放速率)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         优学院DGUT版
// @version      1.6
// @description  适配DGUT优学院(自动静音播放、自动做练习题、自动翻页、修改播放速率)
// @author       Linus
// @match        https://ua.dgut.edu.cn/learnCourse/learnCourse.html*
// @icon         https://lms.dgut.edu.cn/ulearning/favicon.ico
// @grant        GM_xmlhttpRequest
// @license      MIT
// @namespace http://greasyfork.icu/users/1540778
// ==/UserScript==

(function () {
    'use strict';

    /*  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     *  优学院自动静音播放、自动做练习题、自动翻页、修改播放速率脚本(适配东莞理工学院)
     *  重要提醒:使用风险自负,避免高倍速/长时间挂机,建议非核心课程使用,仅限个人学习使用,禁止商用
     *  基于作者Brush-JIM的脚本“优学院自动静音播放、自动做练习题、自动翻页、修改播放速率(改)”
     *  和作者 luluzzy. 的脚本“DGUT Ulearning Tool”(MIT协议)二次开发重构
     *  原脚本链接:http://greasyfork.icu/zh-CN/scripts/555722-dgut-ulearning-tool
     *  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     */

    // 配置管理中心
    const AppConfig = {
        playbackRate: 1.5,
        autoPlay: true,
        autoMute: true,
        autoAdjustRate: true,
        autoFillAnswers: true,
        showAnswers: true,
        autoAnswerSingle: true,
        autoAnswerMulti: true,
        autoAnswerJudge: true,
        autoAnswerBlank: true,
        maxRetryCount: 7,
        getStorageKey: (key) => `ulearn_${key}`,
        load: function () {
            const prefix = 'ulearn_';
            Object.keys(this).forEach(key => {
                if (typeof this[key] !== 'function' && key !== 'maxRetryCount') {
                    const stored = localStorage.getItem(prefix + key);
                    if (stored !== null) {
                        this[key] = stored === 'true' || (stored !== 'false' && stored);
                    }
                }
            });
        },
        save: function () {
            const prefix = 'ulearn_';
            Object.keys(this).forEach(key => {
                if (typeof this[key] !== 'function' && key !== 'maxRetryCount') {
                    localStorage.setItem(prefix + key, this[key]);
                }
            });
        }
    };

    // 状态管理
    const AppState = {
        isPaused: false,
        isRestarting: false,
        isNavigating: false,
        answerInProgress: false,
        modalChecking: false,
        nextPageRetry: 0,
        reset: function () {
            this.nextPageRetry = 0;
            this.isNavigating = false;
        }
    };

    // 日志工具
    const Logger = {
        element: null,
        init: function (el) {
            this.element = el;
        },
        log: function (message) {
            const timestamp = new Date().toLocaleTimeString();
            const logMsg = `[${timestamp}] ${message}`;
            console.log(`DGUT助手: ${logMsg}`);
            if (this.element) {
                this.element.innerHTML += `${logMsg}<br>`;
                this.element.scrollTop = this.element.scrollHeight;
            }
        }
    };

    // 答案处理服务
    class AnswerService {
        getQuestionType(questionEl) {
            const typeTag = questionEl.querySelector('.question-type-tag');
            if (!typeTag) return null;

            const typeText = typeTag.textContent.trim();
            if (typeText.includes('单选题')) return 'single';
            if (typeText.includes('多选题')) return 'multiple';
            if (typeText.includes('判断题')) return 'judge';
            if (typeText.includes('填空题')) return 'blank';
            return null;
        }

        processQuestion(questionId, answers) {
            if (AppState.isPaused || AppState.isRestarting) return;

            const questionEl = document.querySelector(`#question${questionId}`);
            if (!questionEl) {
                Logger.log(`未找到问题容器: ${questionId}`);
                return;
            }

            const type = this.getQuestionType(questionEl);
            if (!type) {
                Logger.log(`无法识别题型: ${questionId}`);
                return;
            }

            const handlers = {
                single: () => this.handleChoice(questionEl, answers),
                multiple: () => this.handleChoice(questionEl, answers),
                judge: () => this.handleJudge(questionEl, answers),
                blank: () => this.handleBlank(questionEl, answers)
            };

            if (handlers[type]) {
                handlers[type]();
            } else {
                Logger.log(`不支持的题型: ${type}`);
            }
        }

        handleChoice(questionEl, answers) {
            const options = questionEl.querySelectorAll('.choice-item, .option-item, .question-option');
            if (!options.length) {
                Logger.log(`选择题选项未找到: ${questionEl.id}`);
                return;
            }

            options.forEach(option => {
                const optionLabel = option.querySelector('.option')?.textContent?.trim().replace('.', '') ||
                                   option.querySelector('.option-letter')?.textContent?.trim() ||
                                   option.querySelector('span:first-child')?.textContent?.trim().replace('.', '');

                if (optionLabel && answers.includes(optionLabel)) {
                    const selector = option.querySelector('.checkbox, .option-checkbox, .radio');
                    if (selector && !selector.classList.contains('selected')) {
                        option.click();
                        if (!selector.classList.contains('selected')) {
                            selector.click();
                        }
                        Logger.log(`选中选项: ${optionLabel}`);
                    }
                }
            });
        }

        handleJudge(questionEl, answers) {
            const isCorrect = String(answers) === 'true';
            const btnSelector = isCorrect ? '.right-btn' : '.wrong-btn';
            const judgeBtn = questionEl.querySelector(btnSelector);

            if (judgeBtn && !judgeBtn.classList.contains('selected')) {
                judgeBtn.click();
                Logger.log(`判断题选择: ${isCorrect ? '正确' : '错误'}`);
            }
        }

        handleBlank(questionEl, answers) {
            const inputs = questionEl.querySelectorAll('textarea, .blank-input');
            answers.forEach((ans, idx) => {
                if (inputs[idx]) {
                    const cleanedAns = this.cleanHtml(this.escapeHtml(ans));
                    inputs[idx].value = cleanedAns;
                    $(inputs[idx]).trigger('change');
                }
            });
            Logger.log(`填空题已填充: ${answers.join('; ')}`);
        }

        escapeHtml(str) {
            const entities = { 'lt': '<', 'gt': '>', 'nbsp': ' ', 'amp': '&', 'quot': '"' };
            return str.replace(/&(lt|gt|nbsp|amp|quot);/ig, (_, key) => entities[key]);
        }

        cleanHtml(str) {
            return str.replace(/(<[^>]+>|\\n|\\r)/g, ' ');
        }
    }

    // 视频控制服务
    class VideoController {
        constructor() {
            this.observer = null;
        }

        init() {
            this.setupVideoMonitoring();
        }

        setupVideoMonitoring() {
            this.observer = new MutationObserver((mutations) => {
                if (!AppState.isPaused && !AppState.isRestarting) {
                    this.processVideos();
                    this.checkModals();
                }
            });
            this.observer.observe(document.body, { childList: true, subtree: true });
        }

        processVideos(slept = false) {
            if (AppState.isPaused || AppState.isRestarting || !AppConfig.autoPlay) return;

            if (AppState.answerInProgress) {
                setTimeout(() => this.processVideos(true), 1000);
                return;
            }

            if (!slept) {
                setTimeout(() => this.processVideos(true), 3000);
                return;
            }

            const video = document.querySelector("video, mediaelementwrapper video:first-child");
            if (video) {
                video.playbackRate = AppConfig.playbackRate;
                if (AppConfig.autoMute && !video.muted) {
                    video.muted = true;
                    Logger.log("视频已静音");
                }
            }

            const videoWrappers = $('mediaelementwrapper video:first-child');
            const statusIndicators = $('.video-bottom span:first-child');

            if (videoWrappers.length === 0) {
                PageNavigator.goNext();
                return;
            }

            if (videoWrappers.length !== statusIndicators.length) {
                PageNavigator.goNext();
                return;
            }

            const videoStates = [];
            $(videoWrappers).each((idx, el) => {
                const state = $(statusIndicators[idx]).attr('data-bind');
                videoStates.push({
                    element: el,
                    completed: state === 'text: $root.i18nMessageText().finished',
                    currentTime: 0
                });
            });

            videoStates.forEach((state, idx) => {
                state.element.addEventListener('ended', () => {
                    videoStates[idx].completed = true;
                    Logger.log("视频播放完成");
                    PageNavigator.goNext();
                }, { once: true });
            });

            this.controlPlayback(videoStates);
        }

        controlPlayback(videoStates) {
            if (AppState.isPaused || AppState.isRestarting) return;

            if (videoStates.length !== $('mediaelementwrapper video:first-child').length) {
                this.processVideos();
                return;
            }

            videoStates.forEach(state => {
                state.element.playbackRate = AppConfig.playbackRate;
            });

            for (let i = 0; i < videoStates.length; i++) {
                if (videoStates[i].element !== $('mediaelementwrapper video:first-child')[i]) {
                    this.processVideos();
                    return;
                }

                if (!videoStates[i].completed) {
                    const targetVideo = (i > 0 && !videoStates[i - 1].completed) ? videoStates[i - 1] : videoStates[i];

                    if (targetVideo.element.paused || targetVideo.currentTime === targetVideo.element.currentTime) {
                        targetVideo.element.currentTime = Math.max(0, targetVideo.element.currentTime - 3);
                        targetVideo.element.play().catch(err => {
                            Logger.log(`播放失败: ${err.message}`);
                            AppRestarter.restart();
                        });
                    }

                    targetVideo.currentTime = targetVideo.element.currentTime;

                    if (AppConfig.autoMute && !targetVideo.element.muted) {
                        targetVideo.element.muted = true;
                    }

                    if (AppConfig.autoAdjustRate && targetVideo.element.playbackRate !== AppConfig.playbackRate) {
                        targetVideo.element.playbackRate = AppConfig.playbackRate;
                    }

                    setTimeout(() => this.controlPlayback(videoStates), 500);
                    return;
                }
            }

            PageNavigator.goNext();
        }

        checkModals(slept = false) {
            if (AppState.isPaused || AppState.isRestarting || AppState.answerInProgress) return;

            if (!slept) {
                setTimeout(() => this.checkModals(true), 2000);
                return;
            }

            AppState.modalChecking = true;

            const questionPanel = $('.question-wrapper');
            if (questionPanel.length > 0 && AppConfig.autoFillAnswers) {
                AnswerProcessor.processQuiz();
                AppState.modalChecking = false;
                return;
            }

            const statModal = $('#statModal');
            if (statModal.length > 0) {
                const buttons = statModal[0].getElementsByTagName('button');
                if (buttons.length >= 2) buttons[1].click();
            }

            const errorIndicator = $('.mobile-video-error');
            if (errorIndicator && errorIndicator.css('display') !== 'none') {
                $('.try-again').click();
                Logger.log("检测到视频错误,已尝试重试");
            }

            const alertModal = document.getElementById('alertModal');
            if (alertModal && alertModal.className.includes('in')) {
                const operations = $('.modal-operation').children();
                if (operations.length >= 2) {
                    operations[AppConfig.autoFillAnswers ? 0 : 1].click();
                } else {
                    const continueBtn = $('.btn-submit');
                    continueBtn.each((_, btn) => {
                        if ($(btn).text() !== '提交') $(btn).click();
                    });
                }
                if (AppConfig.autoFillAnswers) AnswerProcessor.processQuiz();
            }

            AppState.modalChecking = false;
        }
    }

    // 页面导航器
    const PageNavigator = {
        goNext: function () {
            if (AppState.isNavigating || AppState.isPaused || AppState.isRestarting ||
                AppState.answerInProgress || !AppConfig.autoPlay || AppState.modalChecking) {
                return;
            }

            Logger.log("尝试导航至下一页");
            const nextButtons = $('.mobile-next-page-btn, .next-btn, .btn-next, .nextVideoBtn');

            if (nextButtons.length === 0) {
                AppState.nextPageRetry++;
                Logger.log(`未找到下一页按钮(${AppState.nextPageRetry}/${AppConfig.maxRetryCount})`);

                if (AppState.nextPageRetry >= AppConfig.maxRetryCount) {
                    AppState.isPaused = true;
                    document.getElementById('toggleScript').innerText = '▶️ 继续运行';
                    document.getElementById('toggleScript').style.backgroundColor = 'rgba(46, 204, 113, 0.5)';
                    Logger.log(`连续${AppConfig.maxRetryCount}次未找到下一页,已暂停`);
                }
                return;
            }

            AppState.nextPageRetry = 0;
            AppState.isNavigating = true;
            Logger.log("锁定导航状态,防止重复操作");

            nextButtons.each((_, btn) => {
                if (!$(btn).hasClass('disabled')) {
                    btn.click();
                    Logger.log("已点击下一页按钮");
                }
            });

            setTimeout(() => {
                Logger.log("导航完成,解除锁定");
                AppState.isNavigating = false;

                setTimeout(() => {
                    if (!AppState.isPaused && !AppState.isRestarting) {
                        videoController.processVideos();
                        videoController.checkModals();
                    }
                }, 1000);
            }, 3000);
        }
    };

    // 答案处理器
    const AnswerProcessor = {
        processQuiz: function () {
            if (AppState.isPaused || AppState.isRestarting || AppState.answerInProgress || !AppConfig.autoFillAnswers) {
                return;
            }

            AppState.answerInProgress = true;
            Logger.log("检测到测验页面,开始处理答案");

            let questionIds = [];
            const questionPanels = $('.question-wrapper');
            const pageItems = $('.page-item');

            const waitForQuestions = setInterval(() => {
                const currentPanels = $('.question-wrapper');
                if (currentPanels.length > 0) {
                    clearInterval(waitForQuestions);

                    currentPanels.each((_, panel) => {
                        const id = $(panel).attr('id');
                        if (id && id.startsWith('question')) {
                            questionIds.push(id.replace('question', ''));
                        } else {
                            Logger.log("发现无效问题ID,已跳过");
                        }
                    });

                    questionIds = [...new Set(questionIds)];
                    Logger.log(`共检测到 ${questionIds.length} 道题目`);

                    let pageId = '';
                    let found = false;
                    pageItems.each((_, item) => {
                        if (found) return;
                        const pageName = $(item).find('.page-name');
                        if (pageName.length > 0 && pageName[0].className.includes('active')) {
                            const idAttr = $(item).attr('id');
                            pageId = idAttr.slice(idAttr.search(/\d/g));
                            found = true;
                        }
                    });

                    if (!found) {
                        AppState.answerInProgress = false;
                        PageNavigator.goNext();
                        return;
                    }

                    if (questionIds.length === 0) {
                        Logger.log("未发现有效题目,跳转至下一页");
                        AppState.answerInProgress = false;
                        PageNavigator.goNext();
                        return;
                    }

                    const answerService = new AnswerService();
                    const total = questionIds.length;
                    let processed = 0;

                    const processNext = (index) => {
                        if (index >= total) {
                            Logger.log(`所有 ${total} 道题目处理完毕`);
                            setTimeout(() => {
                                if (AppConfig.autoPlay) {
                                    $('textarea, .blank-input').trigger('change');
                                    const submitBtn = $('.btn-submit');
                                    if (submitBtn.length > 0) {
                                        submitBtn.click();
                                        Logger.log("已提交答案");
                                    }

                                    const videos = $('video').filter((_, v) => v.src !== "");
                                    if (videos.length === 0) {
                                        AppState.answerInProgress = false;
                                        PageNavigator.goNext();
                                        return;
                                    }
                                }
                                AppState.answerInProgress = false;
                            }, 1000);
                            return;
                        }

                        const qId = questionIds[index];
                        Logger.log(`处理第 ${index + 1}/${total} 题 (ID: ${qId})`);
                        this.fetchAnswer(qId, pageId, answerService, () => {
                            processed++;
                            Logger.log(`第 ${index + 1} 题处理完成 (${processed}/${total})`);
                            processNext(index + 1);
                        });
                    };

                    processNext(0);
                }
            }, 500);

            setTimeout(() => {
                clearInterval(waitForQuestions);
                if (questionIds.length === 0) {
                    Logger.log("超时未检测到题目,跳转至下一页");
                    AppState.answerInProgress = false;
                    PageNavigator.goNext();
                }
            }, 5000);
        },

        fetchAnswer: function (questionId, parentId, answerService, callback) {
            if (AppState.isPaused || AppState.isRestarting) {
                callback();
                return;
            }

            const auth = this.getAuthorization();
            if (!auth) {
                Logger.log("获取认证信息失败,无法请求答案");
                callback();
                return;
            }

            GM_xmlhttpRequest({
                method: "GET",
                url: `https://ua.dgut.edu.cn/uaapi/questionAnswer/${questionId}?parentId=${parentId}`,
                headers: {
                    "UA-AUTHORIZATION": auth,
                    "X-Requested-With": "XMLHttpRequest",
                    "Referer": window.location.href
                },
                onload: (res) => {
                    try {
                        const data = JSON.parse(res.responseText);
                        const answers = data.correctAnswerList || data.answer || [];
                        Logger.log(`题目 ${questionId} 答案: ${answers}`);

                        if (answers.length > 0) {
                            answerService.processQuestion(questionId, answers);
                        } else {
                            Logger.log(`题目 ${questionId} 未找到答案`);
                        }
                    } catch (err) {
                        Logger.log(`解析答案失败: ${err.message}`);
                        console.error("答案解析错误:", err);
                    } finally {
                        callback();
                    }
                },
                onerror: (err) => {
                    Logger.log(`请求答案失败: ${err.message}`);
                    console.error("答案请求错误:", err);
                    callback();
                }
            });
        },

        getAuthorization: function () {
            return document.cookie.split(";")
                .map(c => c.trim().split("="))
                .find(([k]) => k === "AUTHORIZATION")?.[1] || "";
        }
    };

    // 应用重启器
    const AppRestarter = {
        restart: function () {
            if (AppState.isRestarting) return;

            AppState.isRestarting = true;
            Logger.log("检测到异常,尝试重启服务...");

            const toggleBtn = document.getElementById('toggleScript');
            if (!AppState.isPaused) {
                toggleBtn.click();
            }

            setTimeout(() => {
                Logger.log("重启中,恢复服务...");
                toggleBtn.click();

                setTimeout(() => {
                    AppState.isRestarting = false;
                    Logger.log("服务重启完成");
                }, 1000);
            }, 2000);
        }
    };

    // UI组件
    class UIController {
        constructor() {
            this.panel = null;
        }

        render() {
            this.loadStyles();
            this.createPanel();
            this.bindEvents();
            AppConfig.load();
            this.syncConfigToUI();
        }

        loadStyles() {
            const style = document.createElement('style');
            style.textContent = `
                .ulearn-panel {position:fixed;top:100px;right:30px;z-index:999999;background:rgba(0,0,0,0.7);color:#fff;padding:10px 12px;border-radius:8px;font-size:14px;width:320px;cursor:move}
                .ulearn-panel:hover {opacity:0.95}
                .drag-handle {cursor:move;padding:5px;text-align:center;background:rgba(255,255,255,0.1);border-radius:4px;margin-bottom:8px}
                .panel-title {text-align:center;margin:5px 0;font-weight:bold}
                .control-btn {margin:4px;padding:3px 8px;cursor:pointer;border:none;border-radius:3px;background:rgba(255,255,255,0.2);color:white}
                .control-btn:hover {background:rgba(255,255,255,0.3)}
                .log-container {height:150px;overflow:auto;background:#111;padding:4px;border-radius:4px;font-size:12px;line-height:1.5;margin-top:8px}
                .setting-group {padding-left:15px;margin:5px 0}
                .setting-item {position:relative;list-style:none;margin:5px 0}
                .setting-input {position:absolute;right:5px}
                .section-title {font-weight:bold;margin-top:10px;margin-bottom:5px;padding-bottom:3px;border-bottom:1px solid rgba(255,255,255,0.2)}
                .pause-state {background:rgba(231, 76, 60, 0.5)!important;margin-top:5px;}
                .resume-state {background:rgba(46, 204, 113, 0.5)!important;margin-top:5px;}
            `;
            document.head.appendChild(style);
        }

        createPanel() {
            const panel = document.createElement('div');
            panel.className = 'ulearn-panel';
            panel.innerHTML = `
                <div class="drag-handle">拖动调整位置</div>
                <div class="panel-title">DGUT 优学院助手</div>

                <button id="toggleScript" class="control-btn pause-state" style="width:100%;">⏸️ 暂停脚本</button>

                <div class="section-title">视频控制</div>
                <div>播放倍速: <span id="rateDisplay">${AppConfig.playbackRate}</span>x(1-15x)</div>
                <button id="speedUp" class="control-btn">➕ 加速</button>
                <button id="speedDown" class="control-btn">➖ 减速</button>

                <div class="section-title">视频设置</div>
                <p style="color:#ff9999;font-size:12px;">提示:播放失败时将自动重启</p>
                <ul class="setting-group">
                    <li class="setting-item">自动播放/翻页?<input class="setting-input" id="autoPlay" type="checkbox" checked></li>
                    <li class="setting-item">自动静音?<input class="setting-input" id="autoMute" type="checkbox" checked></li>
                    <li class="setting-item">自动调整速率?<input class="setting-input" id="autoAdjustRate" type="checkbox" checked></li>
                    <li class="setting-item">播放速率<input class="setting-input" id="rateInput" type="number" value="${AppConfig.playbackRate}" step="1" min="1" max="15"></li>
                </ul>

                <div class="section-title">自动作答</div>
                <ul class="setting-group">
                    <li class="setting-item">自动作答(总开关)?<input class="setting-input" id="autoFillAnswers" type="checkbox" checked></li>
                    <li class="setting-item">显示答案?<input class="setting-input" id="showAnswers" type="checkbox" checked></li>
                    <li class="setting-item">自动答选择题?<input class="setting-input" id="autoAnswerSingle" type="checkbox" checked></li>
                    <li class="setting-item">自动答判断题?<input class="setting-input" id="autoAnswerJudge" type="checkbox" checked></li>
                    <li class="setting-item">自动答填空题?<input class="setting-input" id="autoAnswerBlank" type="checkbox" checked></li>
                </ul>

                <button id="saveSettings" class="control-btn" style="width:100%;margin-top:10px;">保存设置</button>

                <div style="margin-top:8px;">操作日志:</div>
                <div id="logContainer" class="log-container"></div>
            `;
            document.body.appendChild(panel);
            this.panel = panel;
            Logger.init(document.getElementById('logContainer'));
        }

        bindEvents() {
            // 拖动功能
            this.panel.onmousedown = (e) => {
                const offsetX = e.clientX - this.panel.offsetLeft;
                const offsetY = e.clientY - this.panel.offsetTop;

                const moveHandler = (e) => {
                    this.panel.style.left = `${e.clientX - offsetX}px`;
                    this.panel.style.top = `${e.clientY - offsetY}px`;
                    this.panel.style.right = 'auto';
                };

                document.addEventListener('mousemove', moveHandler);
                document.addEventListener('mouseup', () => {
                    document.removeEventListener('mousemove', moveHandler);
                }, { once: true });
            };

            // 速率控制
            document.getElementById('speedUp').addEventListener('click', () => {
                if (AppState.isRestarting) return;
                AppConfig.playbackRate = Math.min(15, AppConfig.playbackRate + 1);
                this.updateRateDisplay();
            });

            document.getElementById('speedDown').addEventListener('click', () => {
                if (AppState.isRestarting) return;
                AppConfig.playbackRate = Math.max(1, AppConfig.playbackRate - 1);
                this.updateRateDisplay();
            });

            // 速率输入框
            const rateInput = document.getElementById('rateInput');
            rateInput.addEventListener('change', () => {
                if (AppState.isRestarting) return;
                let val = parseFloat(rateInput.value);
                val = Math.max(1, Math.min(15, val));
                rateInput.value = val;
                AppConfig.playbackRate = val;
                this.updateRateDisplay();
            });

            // 暂停/继续按钮
            const toggleBtn = document.getElementById('toggleScript');
            toggleBtn.addEventListener('click', () => {
                if (AppState.isRestarting) {
                    Logger.log("重启中,暂不支持操作");
                    return;
                }

                AppState.isPaused = !AppState.isPaused;
                if (AppState.isPaused) {
                    toggleBtn.innerText = '▶️ 继续运行';
                    toggleBtn.className = 'control-btn resume-state';
                    Logger.log("脚本已暂停");
                } else {
                    toggleBtn.innerText = '⏸️ 暂停脚本';
                    toggleBtn.className = 'control-btn pause-state';
                    Logger.log("脚本已恢复运行");
                    videoController.processVideos();
                    videoController.checkModals();
                }
            });

            // 自动播放开关联动
            const autoPlayToggle = document.getElementById('autoPlay');
            autoPlayToggle.addEventListener('change', () => {
                if (AppState.isRestarting) return;
                const autoMute = document.getElementById('autoMute');
                const autoAdjust = document.getElementById('autoAdjustRate');
                if (!autoPlayToggle.checked) {
                    autoMute.checked = false;
                    autoAdjust.checked = false;
                } else {
                    autoMute.checked = true;
                    autoAdjust.checked = true;
                }
            });

            // 自动作答总开关联动
            const autoAnswerToggle = document.getElementById('autoFillAnswers');
            autoAnswerToggle.addEventListener('change', () => {
                if (AppState.isRestarting) return;
                const showAns = document.getElementById('showAnswers');
                const single = document.getElementById('autoAnswerSingle');
                const judge = document.getElementById('autoAnswerJudge');
                const blank = document.getElementById('autoAnswerBlank');

                if (!autoAnswerToggle.checked) {
                    showAns.checked = false;
                    single.checked = false;
                    judge.checked = false;
                    blank.checked = false;
                } else {
                    showAns.checked = true;
                    single.checked = true;
                    judge.checked = true;
                    blank.checked = true;
                }
            });

            // 保存设置
            document.getElementById('saveSettings').addEventListener('click', () => {
                if (AppState.isRestarting) {
                    Logger.log("重启中,暂不支持保存");
                    return;
                }

                AppConfig.autoPlay = document.getElementById('autoPlay').checked;
                AppConfig.autoMute = document.getElementById('autoMute').checked;
                AppConfig.autoAdjustRate = document.getElementById('autoAdjustRate').checked;
                AppConfig.autoFillAnswers = document.getElementById('autoFillAnswers').checked;
                AppConfig.showAnswers = document.getElementById('showAnswers').checked;
                AppConfig.autoAnswerSingle = document.getElementById('autoAnswerSingle').checked;
                AppConfig.autoAnswerJudge = document.getElementById('autoAnswerJudge').checked;
                AppConfig.autoAnswerBlank = document.getElementById('autoAnswerBlank').checked;
                AppConfig.playbackRate = parseFloat(document.getElementById('rateInput').value);

                AppConfig.save();
                localStorage.setItem(AppConfig.getStorageKey('paused'), AppState.isPaused);
                localStorage.setItem(AppConfig.getStorageKey('retryCount'), AppState.nextPageRetry);

                this.updateRateDisplay();
                Logger.log("设置已保存");

                if (!AppState.isPaused) {
                    videoController.processVideos();
                    videoController.checkModals(true);
                }
            });
        }

        syncConfigToUI() {
            document.getElementById('autoPlay').checked = AppConfig.autoPlay;
            document.getElementById('autoMute').checked = AppConfig.autoMute;
            document.getElementById('autoAdjustRate').checked = AppConfig.autoAdjustRate;
            document.getElementById('autoFillAnswers').checked = AppConfig.autoFillAnswers;
            document.getElementById('showAnswers').checked = AppConfig.showAnswers;
            document.getElementById('autoAnswerSingle').checked = AppConfig.autoAnswerSingle;
            document.getElementById('autoAnswerJudge').checked = AppConfig.autoAnswerJudge;
            document.getElementById('autoAnswerBlank').checked = AppConfig.autoAnswerBlank;
            document.getElementById('rateInput').value = AppConfig.playbackRate;

            AppState.isPaused = localStorage.getItem(AppConfig.getStorageKey('paused')) === 'true';
            AppState.nextPageRetry = parseInt(localStorage.getItem(AppConfig.getStorageKey('retryCount')) || '0');

            const toggleBtn = document.getElementById('toggleScript');
            if (AppState.isPaused) {
                toggleBtn.innerText = '▶️ 继续运行';
                toggleBtn.className = 'control-btn resume-state';
            } else {
                toggleBtn.innerText = '⏸️ 暂停脚本';
                toggleBtn.className = 'control-btn pause-state';
            }
        }

        updateRateDisplay() {
            const display = document.getElementById('rateDisplay');
            const input = document.getElementById('rateInput');
            display.innerText = AppConfig.playbackRate;
            input.value = AppConfig.playbackRate;

            const video = document.querySelector("video, mediaelementwrapper video:first-child");
            if (video) video.playbackRate = AppConfig.playbackRate;

            Logger.log(`播放速率已调整为 ${AppConfig.playbackRate}x`);
        }
    }

    // 防检测处理
    function setupAntiDetection() {
        // 模拟用户代理
        unsafeWindow.navigator.__defineGetter__("userAgent", () =>
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
        );

        // 模拟鼠标活动
        setInterval(() => {
            if (!AppState.isPaused && !AppState.isRestarting) {
                unsafeWindow.document.dispatchEvent(new Event('mousemove'));
            }
        }, 1000);
    }

    // 初始化应用
    const uiController = new UIController();
    const videoController = new VideoController();

    function initApp() {
        uiController.render();
        videoController.init();
        setupAntiDetection();

        if (!AppState.isPaused && !AppState.isRestarting) {
            videoController.processVideos();
            videoController.checkModals();
        }
    }

    setTimeout(initApp, 3000);
})();