在浙学自动刷课
// ==UserScript==
// @name 在浙学自动刷课
// @namespace Vikrant
// @version 1.2.1
// @description 可以快速地(默认16倍速)自动看完在浙学上一门科目的所有视频和文档。
// @author Vikrant
// @match https://www.zjooc.cn/ucenter/student/course/study/*/plan/detail/*
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @icon https://www.zjooc.cn/favicon.ico
// @license GNU GPLv3
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
let videoRate = 16 //倍速播放视频的倍数,最大为16倍,默认为16倍,网速慢的话可以调小一些,防止卡顿
let delay = 2000 //某些环节等待加载的延迟,如果网络卡顿可以调大一些(单位ms)
let win = unsafeWindow
let winDoc = unsafeWindow.document
let labelList = []
//let buttonList = []
let dirList = []
let labelNow = null
//let buttonNow = null
let dirNow = null
let dirIndex = 0
//let buttonIndex = 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
},
_docLabel: "div>span.label>i:not(.icon-shipin):not(.complete)+span",
docLabel: function () { //获取每一个小章节里上方内容是文档(未看)的标签存入数组
let list = winDoc.querySelectorAll(
"div>span.label>i:not(.icon-shipin):not(.complete)+span"
)
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) { //函数内定义的stop(),方便在setInterval内部停止自身
clearInterval(obj)
}
}
function nextDir() { //跳转至下一个小章节
if (++dirIndex > dirList.length - 1) {
end()
} else {
dirList[dirIndex].click()
dirNow = dirList[dirIndex]
setTimeout(() => {
labelList = find.label() //初始化labelList相关的值
labelIndex = 0
labelNow = labelList[0]
//buttonList = find.button() //初始化buttonList相关的值
//buttonIndex = 0
//buttonNow = buttonList[0]
if (labelList.length == 0) {
nextDir()
} else {
labelNow.click()
setTimeout(() => {
if (winDoc.querySelector("video")) {
videoPlay(nextLabel)
} else {
//buttonNow.click()
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 {
//buttonList = find.button() //不知道为什么要重新初始化一遍buttonList才能正确点击
//buttonNow = buttonList[++buttonIndex]
//buttonNow.click()
find.button().click()
setTimeout(() => {
nextLabel()
}, delay);
}
}, 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以及相关的值
dirIndex = 0
dirNow = dirList[0]
dirList[0].click()
setTimeout(() => {
labelList = find.label()
labelIndex = 0
labelNow = labelList[0]
//buttonList = find.button()
//buttonIndex = 0
//buttonNow = buttonList[0]
if (labelList.length == 0) {
nextDir()
} else {
labelNow.click()
setTimeout(() => {
if (winDoc.querySelector("video")) {
videoPlay(nextLabel)
} else {
//buttonNow.click()
find.button().click()
setTimeout(() => {
nextLabel()
}, delay);
}
}, delay);
}
}, 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;
}
`)
})();
/*
.o8
"888
888oooo. oooo ooo
d88' `88b `88. .8'
888 888 `88..8'
888 888 `888'
`Y8bod8P' .8'
.o..P'
`Y8P'
oooooo oooo o8o oooo .
`888. .8' `"' `888 .o8
`888. .8' oooo 888 oooo oooo d8b .oooo. ooo. .oo. .o888oo
`888. .8' `888 888 .8P' `888""8P `P )88b `888P"Y88b 888
`888.8' 888 888888. 888 .oP"888 888 888 888
`888' 888 888 `88b. 888 d8( 888 888 888 888 .
`8' o888o o888o o888o d888b `Y888""8o o888o o888o "888"
*/