Greasy Fork is available in English.

学起Plus、弘成教育挂课自动连续播放

一个网课挂机自动连续播放工具,仅适用于学起Plus、弘成教育 sccchina.net chinaedu.net,反馈与交流QQ群:715307684,更新日期:2022年11月27日

// ==UserScript==
// @name         学起Plus、弘成教育挂课自动连续播放
// @namespace    http://tampermonkey.net/
// @version      1.0.6
// @description  一个网课挂机自动连续播放工具,仅适用于学起Plus、弘成教育 sccchina.net chinaedu.net,反馈与交流QQ群:715307684,更新日期:2022年11月27日
// @author       哆哆啦啦梦
// @match        *://*.chinaedu.net/*
// @match        *://*.sccchina.net/*
// @match        *://*.edu.cn/*
// @match        *://*.bnude.cn/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chinaedu.net
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @run-at       document-end
// @license      GPLv3
// ==/UserScript==

const lessionRules = {
    "play.html": {
        selector: ['.page-sidebar li>a>span[class^="title"]'],
    },
    "study.do": {
        beforeFun: () => {
            const catalogDiv = document.getElementById("catalogDiv");
            if (catalogDiv.childElementCount === 0) {
                const catalog = document.getElementById("catalogA");
                catalog && catalog.className.indexOf("Cur") === -1 && catalog.click();
            }
        },
        selector: ["#catalogDiv span[onclick]", "#catalogDiv span[class^='tit']", "#catalogDiv li h3[onclick]"],
    },
    "mp4_video_index.html": {
        selector: [".ui-folder .ui-leaf span"],
    },
    "index.html": {
        selector: [".cwcOutline span[id$='_span']"],
    },
};

const currentRules = {
    "videolearning.html": {
        selector: [".page-sidebar li.active>a>span"],
    },
    "play.html": {
        selector: [".page-sidebar li.active>a>span"],
    },
    "mp4_video_index.html": {
        selector: [".ui-folder .ui-leaf.ui-selected span"],
    },
    "study.do": {
        selector: [
            "#catalogDiv .cur span",
            ".study-video-title span[class$='title']",
            "#catalogDiv li.cur h3[onclick]",
        ],
    },
    "index.html": {
        selector: [".cwcOutline .curSelectedNode span[id$='_span']"],
    },
};

const videoRules = {
    "video.html": {
        selector: ["#videoFrame video"],
    },
    "play.html": {
        selector: ["#draggable video"],
    },
    "mp4_video_index.html": {
        selector: [".plyr__video-wrapper video"],
    },
    "study.do": {
        selector: ["videobox video"],
    },
    "index.html": {
        selector: [".video-js video"],
    },
};

const noNeedAutoPlayRules = ["mp4_video_index"];

function isInNoNeedAutoPlay() {
    return noNeedAutoPlayRules.find((e) => document.URL.indexOf(e) > 0);
}

function urlIn(rules) {
    for (let key in rules) {
        if (document.URL.indexOf(key) > 0) {
            return true;
        }
    }

    return false;
}

function getDataForRules(rules) {
    for (let key in rules) {
        if (document.URL.indexOf(key) > 0) {
            for (let i = 0; i < rules[key].selector.length; i++) {
                rules[key].beforeFun && rules[key].beforeFun(rules[key].selector[i]);
                const res = document.querySelectorAll(rules[key].selector[i]);
                rules[key].afterFun && rules[key].afterFun(rules[key].selector[i], res);

                if (res.length > 0) {
                    return res;
                }
            }
        }
    }

    return null;
}

function getCurrentLession() {
    const arr = getDataForRules(currentRules);

    if (arr) {
        GM_setValue("current", arr[arr.length - 1].innerText);
    }
}

function getLessionsInfo() {
    const arr = getDataForRules(lessionRules);

    if (arr) {
        const lessions = [];
        for (let i = 0; i < arr.length; i++) {
            const className = "api20221120-" + i;
            if (arr[i].className.indexOf(className) === -1) {
                arr[i].className += " " + className;
            }
            lessions.push({ title: arr[i].innerText, className });
        }
        GM_setValue("lessions", lessions);
    }
}

let findVideoCount = 0;
const findVideoMaxCount = 3;

function getVideo() {
    const status = GM_getValue("play_end");
    if (GM_getValue("video") || status) {
        return;
    }

    if (findVideoCount >= findVideoMaxCount) {
        if (status !== "not found") {
            GM_setValue("play_end", "not found");
            findVideoCount = 0;
        }
        return;
    }

    if (document.querySelector("video")) {
        GM_setValue("video", document.URL);

        setTimeout(() => {
            playCheck();
        }, 5000);
    } else {
        findVideoCount++;
    }
}

function playCheck() {
    if (GM_getValue("play_end")) {
        return;
    }

    const video = document.querySelector("video");

    if (video) {
        video.muted = true;
        video.playbackRate = 2;

        const currentTime = video.currentTime.toFixed(1);
        const totalTime = video.duration.toFixed(1);
        const nowTime = new Date();

        console.log(`${nowTime.getHours()}:${nowTime.getMinutes()}:${nowTime.getSeconds()},当前进度:${currentTime}/${totalTime} ${(currentTime / totalTime).toFixed(1)},${document.URL}`);

        if (video.ended || totalTime - currentTime < 35 * video.playbackRate) {
            video.onpause = null;

            setTimeout(() => {
                GM_setValue("play_end", "over");
            }, 5000);
        } else {
            if (video.paused) {
                console.log("视频被暂停,继续播放!");
                video.play();
                video.onpause = function() {
                    document.querySelector("video").play();
                }
            }

            setTimeout(() => {
                playCheck();
            }, 5000);
        }
    } else {
        console.log("异常:找不到视频元素了");
    }
}

function nextCheck() {
    const status = GM_getValue("play_end");
    const lessions = GM_getValue("lessions");
    if (status && lessions && lessions.length) {
        let currentText = GM_getValue("current");
        const lastCurrent = GM_getValue("last_current");

        if (!lastCurrent || (currentText && lastCurrent !== currentText)) {
            GM_setValue("last_current", currentText);
        } else {
            currentText = lastCurrent;
        }

        let index = GM_getValue("last_pos") ?? 0;

        const newIndex = lessions.findIndex((e) => e.title === currentText);

        if (newIndex !== -1 && newIndex > index) {
            index = newIndex;
        }

        if (status === "not found" && !currentText) {
            index = 0;
        } else if (isInNoNeedAutoPlay()) {
            return;
        } else {
            index += 1;
        }

        GM_setValue("last_pos", index);

        if (index < lessions.length) {
            console.log(lessions[index]);
            GM_deleteValue("play_end");
            GM_deleteValue("video");
            document.querySelector("." + lessions[index].className).click();
        } else {
            alert("课程播放结束");
            return;
        }
    }

    setTimeout(() => {
        nextCheck();
    }, 5000);
}

function getResource() {
    getCurrentLession();
    getLessionsInfo();
}

function init() {
    GM_deleteValue("play_end");
    GM_deleteValue("video");
    GM_deleteValue("current");
    GM_deleteValue("last_current");
    GM_deleteValue("lessions");
}


function initLessions() {
    GM_setValue("step", 1);
    GM_setValue("last_pos", 0);
    return true;
}

function popupClose() {
    const tips = document.querySelector(".win-content");

    if (tips && tips.innerText.indexOf("继续学习") > 0) {
        const btn = document.querySelector(".win-content .close-win-bt");
        btn && btn.click();
    }

    const pop = document.querySelector("#pop");

    pop && pop.querySelector(".pop_close").click();
}

function work() {
    init();

    setTimeout(() => {
        urlIn(lessionRules) && initLessions() && nextCheck();
    }, 5000);

    setInterval(() => {
        urlIn(videoRules) && getVideo();
    }, 10000);

    setInterval(() => {
        getResource();
        popupClose();
    }, 3000);

    setInterval(() => {
        if (document.URL.indexOf("sccchina.net/student/") >= 0) {
            // 定时刷新
            setTimeout(() => {
                location.reload();
            }, (new Date().getSeconds() + 100) * 1234);
        }
    }, 60 * 15 * 1000);
}

(function () {
    "use strict";

    work();
})();