// ==UserScript== // @name 在浙学自动刷课 // @namespace Vikrant // @version 1.0.0 // @description 可以快速地自动看完在浙学上一门科目的所有视频和文档。 // @author Vikrant // @match https://www.zjooc.cn/ucenter/student/course/study/*/plan/detail/* // @grant unsafeWindow // @grant GM_addStyle // @license GNU GPLv3 // @run-at document-idle // ==/UserScript== (function () { 'use strict'; let videoRate = 16 //倍速播放视频的倍数,最大为16倍,默认为16倍,网速慢的话可以调小一些 let delay = 2000 //某些环节等待加载的延迟,如果网络卡顿可以调大一些 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 console.log('视频倍速不得大于16倍或小于0!') } if (delay < 0) { delay = 2000 console.log('delay不得小于0!') } let nullFunction = function () { } //空函数 let find = { //查找类函数的集合 _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" ) return list }, _videoLabel: "div>span.label>i.icon-shipin:not(.complete)+span", videoLabel: function () { //获取每一个小章节里上方内容是视频(未看)的标签存入数组 let list = winDoc.querySelectorAll( "div>span.label>i.icon-shipin:not(.complete)+span" ) return list }, _button: "div>div>div.contain-bottom>button", button: function () { //获取小章节里每一个文档的“完成学习”按钮存入数组 let list = winDoc.querySelectorAll( "div>div>div.contain-bottom>button" ) return list } } function doAfterLoad(selector, event, interval = 1000) { //当元素加载后执行特定函数 let scan = setInterval(() => { let load = winDoc.querySelector(selector) if (load) { stop(scan) event() } }, interval) function stop(obj) { //函数内定义的stop(),方便在setInterval内部停止自身 clearInterval(obj) } } function nextDir() { //跳转至下一个小章节 if (++dirIndex > dirList.length - 1) { end() } else { dirList[dirIndex].click() dirNow = dirList[dirIndex] setTimeout(() => { labelList = find.videoLabel() //初始化labelList相关的值 labelIndex = 0 labelNow = labelList[0] if (labelList.length == 0) { nextDir() } else { labelNow.click() setTimeout(() => { videoPlay(nextLabel) docPass() }, delay) } }, delay) } } function nextLabel() { //点击下一个未观看的视频标签 if (++labelIndex > labelList.length - 1) { nextDir() } else { labelList[labelIndex].click() labelNow = labelList[labelIndex] setTimeout(() => { videoPlay(nextLabel) }, delay) } } function videoPlay(afterEvent = nullFunction) { //播放当前页面的视频并指定播放完之后执行的函数 doAfterLoad("video", () => { let video = winDoc.querySelector("video") video.muted = true //静音 video.playbackRate = videoRate //调倍速 video.play() //开始播放视频 video.addEventListener('ended', () => { //监听视频是否播放完毕 afterEvent() }) }) } function docPass() { //自动点击所有“完成学习”按钮 doAfterLoad(find._button, () => { let button = find.button() for (let i = 0; i < button.length; i++) { button[i].click() } }) } function end() { //结束函数 winDoc.querySelector("#passButton").innerHTML = "完成!" GM_addStyle(` #passButton{ background-color:#e67e22 } `) } function main() { //主函数 setTimeout(() => { dirList = find.dir() //初始化dirList和labelList以及相关的值 labelList = find.videoLabel() dirIndex = 0 labelIndex = 0 dirNow = dirList[0] labelNow = labelList[0] dirList[0].click() setTimeout(() => { if (labelList.length == 0) { nextDir() } else { labelList[0].click() videoPlay(nextLabel) } }, delay); }, delay); } 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 = () => { //指定按钮点击事件 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; } `) })();