Greasy Fork

Greasy Fork is available in English.

UOOC 学习助手_szu_v2

自动静音、二倍速、失焦不断播、自动连播、自动答题(视频中非测验)。递归可开关,答题后彻底删除所有弹窗/遮罩,包括所有layui-layer-shade视频区域遮挡,保证学习区域无障碍。

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         UOOC 学习助手_szu_v2
// @namespace    https://github.com/xiaochai-123
// @version      1.3
// @description  自动静音、二倍速、失焦不断播、自动连播、自动答题(视频中非测验)。递归可开关,答题后彻底删除所有弹窗/遮罩,包括所有layui-layer-shade视频区域遮挡,保证学习区域无障碍。
// @license      GPL
// @match        *://www.uooc.net.cn/*
// @match        *://uooc.net.cn/*
// @match        *://*.uooc.net.cn/*
// @match        *://uooc.online/*
// @match        *://*.uooc.online/*
// @grant        none
// ==/UserScript==

(function () {
    "use strict";

    // 自动播放和倍速静音
    function keepPlaying() {
        const video = document.getElementById("player_html5_api");
        if (video) {
            video.muted = true;
            video.playbackRate = 2;
            if (video.paused && !video.ended) video.play();
        }
        const playBtn = document.querySelector(".vjs-big-play-button");
        if (playBtn) playBtn.click();
    }
    setInterval(keepPlaying, 500);

    // 强力弹窗和遮罩清理函数,智能判断保留小测验证弹窗
    setInterval(() => {
        // 检查是否存在小测智能验证弹窗
        let hasSmartVerification = false;

        // 判断小测验证弹窗的特征
        document.querySelectorAll('.layui-layer').forEach(layer => {
            const layerText = layer.innerText || '';
            if (layerText.includes('验证') || layerText.includes('智能') || layerText.includes('提交') ||
                layer.querySelector('.layui-layer-btn')) {
                hasSmartVerification = true;
            }
        });

        // 如果没有小测验证弹窗,则执行清理
        if (!hasSmartVerification) {
            // 清除所有layui弹窗遮罩
            document.querySelectorAll('.layui-layer-shade').forEach(e => e.remove());
            // 清除所有layui弹窗本体(但保留可能的小测验证弹窗)
            document.querySelectorAll('.layui-layer.layui-layer-page').forEach(e => {
                const layerText = e.innerText || '';
                // 只删除不包含验证关键词的弹窗
                if (!layerText.includes('验证') && !layerText.includes('智能') && !layerText.includes('提交')) {
                    e.remove();
                }
            });
            // 清除视频区域遮罩
            document.querySelectorAll('.vjs-mask').forEach(e => e.remove());
            // 清理答题内容被拉到视频下方
            let quizDom = document.querySelector(".smallTest-view");
            if (quizDom && quizDom.offsetTop > 600) {
                quizDom.style.display = "none";
            }
        }
    }, 800);

    // 自动答题
    let lastQuizQuestion = null;
    function autoAnswerQuiz() {
        let quizLayer = document.querySelector("#quizLayer");
        if (quizLayer && quizLayer.style.display !== "none") {
            try {
                let videoDiv = document.querySelector("div[uooc-video]");
                if (!videoDiv) return;
                let source = videoDiv.getAttribute("source");
                if (!source) return;
                let quizList = JSON.parse(source).quiz || [];
                let quizQuestionElem = document.querySelector(".smallTest-view .ti-q-c");
                if (!quizQuestionElem) return;
                let quizQuestion = quizQuestionElem.innerHTML.trim();

                if (lastQuizQuestion === quizQuestion && quizLayer.classList.contains("answered")) {
                    return;
                }

                let quizIndex = quizList.findIndex(q => q.question === quizQuestion);
                if (quizIndex === -1) return;
                let quizAnswer = eval(quizList[quizIndex].answer);
                let quizOptions = quizLayer.querySelector("div.ti-alist");
                if (quizOptions) {
                    for (let ans of quizAnswer) {
                        let labelIndex = ans.charCodeAt() - "A".charCodeAt();
                        let opt = quizOptions.children[labelIndex];
                        if (opt) opt.click();
                    }
                    let btn = quizLayer.querySelector("button");
                    if (btn) btn.click();
                    quizLayer.classList.add("answered");
                    lastQuizQuestion = quizQuestion;
                    console.log("自动答题已完成:", quizQuestion, quizAnswer.toString());
                }
            } catch (error) {
                console.log("自动答题发生错误:", error);
            }
        } else {
            lastQuizQuestion = null;
            let answeredLayer = document.querySelector("#quizLayer.answered");
            if (answeredLayer) answeredLayer.classList.remove("answered");
        }
    }
    setInterval(autoAnswerQuiz, 500);

    // ======= 递归连播开关和核心 =======
    let autoRecursiveFlag = false;
    let controlBtn = document.createElement("button");
    controlBtn.innerText = "递归上课:关闭";
    controlBtn.style.position = "fixed";
    controlBtn.style.top = "140px";
    controlBtn.style.right = "30px";
    controlBtn.style.zIndex = "9999";
    controlBtn.style.background = "#00796b";
    controlBtn.style.color = "#fff";
    controlBtn.style.border = "none";
    controlBtn.style.padding = "11px 22px";
    controlBtn.style.borderRadius = "8px";
    controlBtn.style.cursor = "pointer";
    controlBtn.style.boxShadow = "0 2px 12px rgba(0,0,0,0.18)";
    controlBtn.style.userSelect = "none";
    controlBtn.onclick = function () {
        autoRecursiveFlag = !autoRecursiveFlag;
        controlBtn.innerText = "递归上课:" + (autoRecursiveFlag ? "开启" : "关闭");
        if (autoRecursiveFlag) {
            startUltimateCourseRush();
        }
    };
    document.body.appendChild(controlBtn);

    function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
    function clearShader() {
        // 检查是否有小测验证弹窗
        let hasSmartVerification = false;
        document.querySelectorAll('.layui-layer').forEach(layer => {
            const layerText = layer.innerText || '';
            if (layerText.includes('验证') || layerText.includes('智能') || layerText.includes('提交') ||
                layer.querySelector('.layui-layer-btn')) {
                hasSmartVerification = true;
            }
        });

        // 如果没有小测验证弹窗,则执行清理
        if (!hasSmartVerification) {
            document.querySelectorAll("div.layui-layer-shade").forEach(shader => shader.remove());
            document.querySelectorAll('.vjs-mask').forEach(e => e.remove());
        }
    }
    function findCurrentVideoNode() {
        let current = document.querySelector('.basic.active');
        if (current) return current;
        current = document.querySelector('.video-list li.current, .chapter-list li.current, .menu li.current, li.active, li.selected');
        if (current) return current;
        return null;
    }
    function findNextVideoInSameChapter(currentNode) {
        if (!currentNode) return null;
        let chapterContainer = currentNode.closest('ul, .chapter-list, .video-list');
        if (!chapterContainer) return null;
        let allVideoNodes = Array.from(chapterContainer.querySelectorAll('li, .basic'));
        let currentIndex = allVideoNodes.indexOf(currentNode);
        if (currentIndex === -1 || currentIndex >= allVideoNodes.length - 1) {
            return null;
        }
        for (let i = currentIndex + 1; i < allVideoNodes.length; i++) {
            let nextNode = allVideoNodes[i];
            if (nextNode.querySelector('.icon-video, span.icon-video') || nextNode.classList.contains('taskpoint') && !nextNode.innerText.includes("测验")) {
                return nextNode;
            }
        }
        return null;
    }
    function playNextVideoInChapter() {
        let current = findCurrentVideoNode();
        let next = findNextVideoInSameChapter(current);
        if (next) {
            let clickTarget = next.querySelector('a') || next;
            clickTarget.click();
            setTimeout(() => {
                const video = document.getElementById('player_html5_api');
                if (video) {
                    video.muted = true;
                    video.playbackRate = 2;
                    video.play();
                }
            }, 1000);
            return true;
        }
        return false;
    }
    function waitForVideoCompletion() {
        return new Promise(resolve => {
            const video = document.getElementById('player_html5_api');
            if (!video) {
                resolve();
                return;
            }
            video._recursiveResolve = resolve;
            video.onended = function() {
                setTimeout(() => {
                    if (video._recursiveResolve) {
                        video._recursiveResolve();
                    }
                }, 1500);
            };
            video.muted = true;
            video.playbackRate = 2;
            const playBtn = document.querySelector(".vjs-big-play-button");
            if (playBtn) playBtn.click();
            setTimeout(() => {
                if (video.ended && video._recursiveResolve) {
                    video._recursiveResolve();
                }
            }, 500);
        });
    }
    function findCurrentChapter() {
        const currentVideoNode = findCurrentVideoNode();
        if (!currentVideoNode) return null;
        let node = currentVideoNode;
        while (node && node.parentElement) {
            if (node.parentElement.classList.contains('rank-1') || node.classList.contains('rank-1-item')) {
                return node;
            }
            node = node.parentElement;
        }
        return null;
    }
    async function searchUncompleteFromCurrentChapter() {
        const catalogRoot = document.querySelector("ul.rank-1");
        if (!catalogRoot) return;
        const chapters = Array.from(catalogRoot.children);
        const currentChapter = findCurrentChapter();
        let startIndex = 0;
        if (currentChapter) {
            startIndex = chapters.indexOf(currentChapter);
            if (startIndex === -1) startIndex = 0;
        }
        for (let i = startIndex; i < chapters.length; i++) {
            await checkActive(chapters[i]);
        }
    }
    async function checkActive(catalog) {
        let children = catalog.children;
        let elem = catalog?.firstElementChild;
        if (elem && elem.classList.contains("uncomplete") && !elem.innerText.includes("测试")) {
            let iElement = elem.getElementsByTagName("i")[0];
            if (iElement && iElement.classList.contains("icon-xiangxia")) {
                elem.click();
            }
            await sleep(500);
            for (let i = 1; i < children.length; i++) {
                if (children[i].tagName === "DIV" && children[i]?.firstElementChild && !children[i].firstElementChild.classList.contains("complete")) {
                    let spanElem = children[i].firstElementChild.children[1];
                    if (spanElem && spanElem.classList.contains("taskpoint") && !spanElem.innerText.includes("测验")) {
                        children[i].firstElementChild.click();
                        clearShader();
                        await waitForVideoCompletion();
                        while (playNextVideoInChapter()) {
                            await waitForVideoCompletion();
                        }
                    }
                } else if (children[i].tagName === "UL") {
                    await searchUncomplete(children[i]);
                }
            }
        }
    }
    async function searchUncomplete(query) {
        let catalog = query.children;
        for (let i = 0; i < catalog.length; i++) {
            await checkActive(catalog[i]);
        }
    }
    async function startUltimateCourseRush() {
        if (!autoRecursiveFlag) return;
        await searchUncompleteFromCurrentChapter();
    }

    $(document).ready(function () {
        setTimeout(() => { if (autoRecursiveFlag) startUltimateCourseRush(); }, 1000);
        setInterval(async function () {
            if (autoRecursiveFlag) {
                const video = document.getElementById('player_html5_api');
                if ((!video || video.ended) && !document.querySelector(".basic.active")) {
                    await startUltimateCourseRush();
                }
            }
        }, 800);
        setInterval(bindVideoEnded, 800);
    });

    function bindVideoEnded() {
        const video = document.getElementById('player_html5_api');
        if (video && !video._autoNextBound) {
            video._autoNextBound = true;
            video.onended = function () {
                if (autoRecursiveFlag) {
                    if (!playNextVideoInChapter()) {
                        setTimeout(() => {startUltimateCourseRush();}, 1500);
                    }
                }
            };
        }
    }
})();