您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
[Zjooc|在浙学] 自动刷章节视频|文档,支持0-16倍速,支持后台播放,视频静音。
// ==UserScript== // @name AutoZjooc | 在浙学自动刷课 // @namespace Albresky // @version 0.0.4 // @description [Zjooc|在浙学] 自动刷章节视频|文档,支持0-16倍速,支持后台播放,视频静音。 // @author Albresky // @include /^https:\/\/www\.zjooc\.cn\/ucenter\/student\/course\/study\/.*/plan\/detail\/.+$ // @include /^https:\/\/www\.zjooc\.cn\/ucenter\/student\/course\/study\/.*/test\/do\/.+$ // @grant unsafeWindow // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @icon https://www.zjooc.cn/favicon.ico // @license GPLv3 // @run-at document-start // ==/UserScript== (function () { "use strict"; function mLog(text) { console.log(new Date().toLocaleTimeString() + " | [AutoZjooc] " + text); } mLog("Script starts."); let videoRate = 1; // 倍速播放视频的倍数,最大为16倍,默认为16倍,网速慢的话可以调小一些,防止卡顿 let startFromSelected = true; // 是否从当前选中的章节开始刷课 let v_mute = true; // 是否静音 let delay = 1000; // 某些环节等待加载的延迟,如果网络卡顿可以调大一些(单位ms) let autoRestartDelay = 5000; // 重新开始任务的延迟,如果网络卡顿可以调大一些(单位ms) let recent_loaded_delay = 8000; // 视频加载失败时的页面刷新 timestamp delay (单位ms) let videoRightLoadDelay = 1000; // 检测视频是否加载成功的延迟(单位ms) let videoValidCheckDelay = 60000; // 视频有效性检查的周期(单位ms) let enableFloatTime = true; const regExamPath = new RegExp( "^/ucenter/student/course/study/.*/test/do/.+$", "g" ); const floatStyle = "background: rgb(0, 49, 168);color: rgb(255, 255, 255);right: 50%;height: auto;top: 0%;margin-right: -70px;width: 140px;overflow: hidden;z-index: 9999;margin-top: -6px;padding: 8px;position: fixed;text-align: center;border-bottom-left-radius: 10px;border-bottom-right-radius: 10px;"; let win = unsafeWindow; let winDoc = unsafeWindow.document; let labelList = []; let dirList = []; let labelNow = null; let dirNow = null; let dirIndex = 0; let labelIndex = 0; if (videoRate < 0 || videoRate > 16) { videoRate = 16; mLog("视频倍速不得大于16倍或小于0!"); } if (delay < 0) { delay = 2000; mLog("delay不得小于0!"); } if (autoRestartDelay < 1000) { autoRestartDelay = 5000; mLog("autoRestartDelay不得小于1000!"); } let nullFunction = function () {}; let find = { // 获取当前所在章节标题 _curChapterTitle: ".el-header>ul>li", curChapterTitle: function () { let title = winDoc.querySelectorAll(this._curChapterTitle)[0].innerHTML; return title; }, // 获取当前小节标题 _curTitle: ".el-header>ul>li", curTitle: function () { let title = winDoc.querySelectorAll(this._curTitle)[1].innerHTML; return title; }, // 查找当前所在类函数的集合 _curDir: "#pane-Chapter>div>ul>li.el-submenu>ul>li.is-active>span", curDir: function () { // 获取当前所在小章节 let dir = winDoc.querySelector(this._curDir); return dir; }, // 查找类函数的集合 _dir: "#pane-Chapter>div>ul>li.el-submenu>ul>li>span", dir: function () { // 获取每一个小章节存入数组 let list = winDoc.querySelectorAll( "#pane-Chapter>div>ul>li.el-submenu>ul>li>span" ); // 从当前选中的章节开始刷课 if (startFromSelected) { let __curTitle = find.curTitle(); for (let i = 0; i < list.length; i++) { if (list[i].innerHTML == __curTitle) { dirIndex = i; break; } } } return list; }, _videoLabel: "div>span.label>i.icon-shipin:not(.complete)+span", videoLabel: function () { // 获取每个小节未看的视频标签存入数组 let list = winDoc.querySelectorAll(this._videoLabel); return list; }, _docLabel: "div>span.label>i:not(.icon-shipin):not(.complete)+span", docLabel: function () { // 获取每个小节未看的非视频标签存入数组 let list = winDoc.querySelectorAll(this._docLabel); return list; }, label: function () { // 获取小节内所有标签(先所有视频,再所有文档) let videoLabel = find.videoLabel(); let docLabel = find.docLabel(); let list = new Array(videoLabel.length + docLabel.length); for (let i = 0; i < videoLabel.length; ++i) { list[i] = videoLabel[i]; } for (let i = 0; i < docLabel.length; ++i) { list[videoLabel.length + i] = docLabel[i]; } return list; }, _button: "div>div>div.contain-bottom>button", button: function () { // 获取当前文档的“完成学习”按钮并返回 let btn = winDoc.querySelector("div>div>div.contain-bottom>button"); return btn; }, }; function doAfterLoad(selector, event, interval = 1000) { // 当元素加载后执行指定函数 let scan = setInterval(() => { let load = winDoc.querySelector(selector); if (load) { stop(scan); event(); } }, interval); function stop(obj) { clearInterval(obj); } } function onVideoLoadFail() { mLog("Video loading failed, reload page."); GM_setValue("zjooc_last_loaded", Date.now()); location.reload(); } function videoIntervalCheck() { // 每个一段时间检查视频是否在播放,如果不在播放则刷新网页 let scan = setInterval(() => { let _video = winDoc.querySelector("video"); if (_video) { let btnPause = $("[class^='pausech']"); if (btnPause.length > 0) { // If the video is playing normally, click pause button will pause it, and the loading animation will not show. btnPause[0].click(); if (!checkVideoRightLoaded()) { clearInterval(scan); onVideoLoadFail(); } else { mLog("Video is loaded normally."); let btnPlay = $("[class^='playch']"); if (btnPlay.length > 0) { btnPlay[0].click(); } } } } }, videoValidCheckDelay); } function nextDir() { // 跳转至下一小节 if (++dirIndex > dirList.length - 1) { end(); } else { expandChapterNode(dirList[dirIndex]); dirList[dirIndex].click(); dirNow = dirList[dirIndex]; setTimeout(() => { labelList = find.label(); labelIndex = 0; labelNow = labelList[0]; if (labelList.length == 0) { nextDir(); } else { labelNow.click(); setTimeout(() => { if (winDoc.querySelector("video")) { videoPlay(nextLabel); } else { find.button().click(); setTimeout(() => { nextLabel(); }, delay); } }, delay); } }, delay); } } function nextLabel() { // 点击下一个未观看的视频标签 if (++labelIndex > labelList.length - 1) { nextDir(); } else { labelList[labelIndex].click(); labelNow = labelList[labelIndex]; setTimeout(() => { if (winDoc.querySelector("video")) { videoPlay(nextLabel); } else { find.button().click(); setTimeout(() => { nextLabel(); }, delay); } }, delay); } } function checkVideoRightLoaded() { // 检查视频是否加载成功 let v_progress = $("[class^='timetext']")[0].innerText; if (v_progress == "00:00 / 00:00") { return false; } // setTimeout(() => { let _loading = $("[class^='loadingch']"); if ( _loading && _loading.length > 0 && _loading[0].style.display == "block" ) { return false; } return true; // }, videoRightLoadDelay); } function muteVideo() { // 使静音视频 let muteNode = $("[class^='mutech']"); if (muteNode && muteNode.length == 2) { if (muteNode[0].style.display == "block") muteNode[0].click(); } } function videoPlay(afterEvent = nullFunction) { // 播放当前页面的视频并指定播放完之后执行的函数 doAfterLoad("video", () => { let video = winDoc.querySelector("video"); if (!checkVideoRightLoaded()) { onVideoLoadFail(); return; } if (v_mute) { muteVideo(); } videoIntervalCheck(); video.playbackRate = videoRate; // 调倍速 video.play(); // 开始播放视频 video.addEventListener("ended", () => { // 监听视频是否播放完毕 afterEvent(); }); }); } function end() { //结束函数 winDoc.querySelector("#passButton").innerHTML = "完成!"; GM_addStyle(` #passButton{ background-color:#e67e22 } `); } function expandChapterNode(unitNode) { // 自动展开章节节点 let chapterNode = unitNode.parentNode.parentNode.parentNode; if (!chapterNode) { mLog("chapterNode is null"); return; } let menuNode = chapterNode.querySelector("ul"); if (menuNode && menuNode.getAttribute("style")) { menuNode.removeAttribute("style"); } if (chapterNode.getAttribute("aria-expanded") != "true") { chapterNode.setAttribute("aria-expanded", "true"); } let classList = chapterNode.classList; if (!classList.contains("is-active")) { classList.add("is-active"); } if (!classList.contains("is-opened")) { classList.add("is-opened"); } } function recent_loaded() { let last_loaded_time = GM_getValue("zjooc_last_loaded", 0); if (last_loaded_time > 0) { let last_loaded_time_delta = Date.now() - last_loaded_time; mLog("last_loaded_time_delta:" + last_loaded_time_delta + "ms"); GM_deleteValue("zjooc_last_loaded"); if (last_loaded_time_delta < recent_loaded_delay) { mLog("Recent loaded."); return true; } } else { GM_deleteValue("zjooc_last_loaded"); return false; } } function floatTime() { let _timer = setInterval(() => { let timeDiv = winDoc.querySelectorAll("div>.ta-c>p"); if (timeDiv && timeDiv.length >= 2) { if (timeDiv[1].innerHTML.startsWith("\n 倒计时")) { timeDiv[1].style = floatStyle; mLog("Float time enabled."); } clearInterval(_timer); } }, 200); } function main() { setTimeout(() => { dirList = find.dir(); mLog("dirIndex:" + dirIndex); dirNow = dirList[dirIndex]; expandChapterNode(dirNow); dirNow.click(); setTimeout(() => { labelList = find.label(); labelIndex = 0; labelNow = labelList[0]; if (labelList.length == 0) { nextDir(); } else { labelNow.click(); setTimeout(() => { if (winDoc.querySelector("video")) { labelNow.click(); videoPlay(nextLabel); } else { //buttonNow.click() find.button().click(); setTimeout(() => { nextLabel(); }, delay); } }, delay); } }, delay); }, delay); } if (enableFloatTime && regExamPath.test(location.pathname)) { floatTime(); } else { let passButton = winDoc.createElement("button"); passButton.id = "passButton"; passButton.innerHTML = "开始刷课"; win.onload = () => { // 页面加载时添加按钮 let header = winDoc.querySelector("#app>div>section>section>header"); header.appendChild(passButton); passButton.onclick = () => { mLog("passButton clicked."); // 指定按钮点击事件 main(); passButton.innerHTML = "刷课中…"; GM_addStyle(` #passButton{ background-color:#53555e } `); // 按钮点击后移除点击事件 passButton.onclick = nullFunction; }; }; // 定义按钮样式 GM_addStyle(` #passButton{ background-color: #1192ff; color: white; text-align: center; padding: 0px 32px; text-decoration: none; display: inline-block; font-size: 14px; } `); // 视频加载失败后自动重启任务 if (recent_loaded()) { mLog( "Page was recent loaded, auto start in " + autoRestartDelay + " ms." ); setTimeout(() => { passButton.click(); }, autoRestartDelay); } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址