// ==UserScript== // @name UOOC 学习助手_szu_v2 // @namespace https://github.com/xiaochai-123 // @version 1.4.1 // @description 自动静音、二倍速、失焦不断播、自动连播、自动答题(视频中非测验)。递归可开关,修复直接跳过首测验视频小节的bug。 // @license GPL // @match *://www.uooc.net.cn/* // @match *://uooc.net.cn/* // @match *://*.uooc.net.cn/* // @match *://uooc.online/* // @match *://*.uooc.online/* // @grant none // @downloadURL 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) { document.querySelectorAll('.layui-layer-shade').forEach(e => e.remove()); 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; // 展开章节(移除uncomplete和测试过滤) if (elem) { 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") { const resourceListDiv = children[i]; const resources = Array.from(resourceListDiv.querySelectorAll(':scope > .basic')); for (let r of resources) { if (r.classList.contains('complete')) continue; let nameSpan = r.querySelector('.tag-source-name'); let nameText = nameSpan ? (nameSpan.innerText || '') : ''; if (nameSpan && nameSpan.classList.contains('taskpoint') && nameText.includes('测验')) { continue; } if (r.querySelector('.icon-video') || /视频/.test(nameText)) { r.click(); clearShader(); await waitForVideoCompletion(); while (playNextVideoInChapter()) { await waitForVideoCompletion(); } return; } } } 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); } } }; } } })();