Greasy Fork is available in English.

在浙学自动刷课

可以快速地(默认16倍速)自动看完在浙学上一门科目的所有视频和文档。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==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" 
*/