Greasy Fork

Greasy Fork is available in English.

zhyDaDa_超星网课助手

[个人向] 刷超星尔雅的网课, 现在支持pdf/ppt/视频/音频的处理, 提供了设置面板可以调节, 解决了鼠标移开视频暂停的问题

当前为 2023-04-17 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         zhyDaDa_超星网课助手
// @version      1.15
// @description  [个人向] 刷超星尔雅的网课, 现在支持pdf/ppt/视频/音频的处理, 提供了设置面板可以调节, 解决了鼠标移开视频暂停的问题
// @author       zhyDaDa
// @namespace    http://zhydada.github.io/
// @updateurl    http://zhydada.github.io/scripts/4.user.js
// @homepage    http://zhydada.github.io/scripts/4.user.js
// @license      personal - use only

// @match        *://mooc.s.ecust.edu.cn/*
// @match        *://mooc1.chaoxing.com/*

// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @icon         data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAkACQAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAAgACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9yL68aMTFZP3i7toZ9q55xk9hWLovi+6vtRjhurdrdZQQrbzywGcc+wP5Vf1U/Jc/Vv5msK/t5JfIaFo98Mqy/McA4zx+tfP0uG8HTcqNZt8+0m2nHfVWaT6bpnPnGbPBYulS05Grt/Mv634vubDUHhtbdrlYwN7bjwxGcfkQfxrb0+4kkWBnb5m27grZXPfB71ytlbyRtcNKybppmk+Vs4zjj8MVv6VJu+yjPRl/mK58Zw3RqSp0cJpyfFO7bloul7Xv2svuDJ82ljcZVpK3IldP5ianGzpdemW/ma53Wb99PtlePaxZ9vzDpwT/AErsZ4CzyK0bFSTxtrI8ReFTrFnHHCogZJA5JjPIwRj9R+VZ4HiinVxyWNhy0722bSXn1fyX+YZ1k8sbiIV7rlirNdWYujag+oWztJtVlfaNo68A+/rXRaT9+25/jX+Yqv4d8KnR7OSOdVuGeQuCIzwMAY/T9a1rW18uWMLCyhWGPl6V0YjijCU8ZNYenJwvZNKy6d9fvDJcnlgq9Su2uWSskt1sf//Z
// @grant        unsafeWindow
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    /**
+ 1.0 视频基本操作
+ 1.1 寻找猎物多次更新
+ 1.2 确保在看到绿色勾勾出现再跳
+ 1.3 自动判断视频有没有放过, 只挑没放过的视频看
+ 1.4 克服浏览器禁止自动播放的限制, 尽力剔除bug
+ 1.5 可以完成ppt和pdf的任务了
+ 1.6 提供设置面板(保存设置完善成功)
+ 1.7 第二种pdf完善, 全局变量问题解决
+ 1.8 解决了多开和节流问题
+ 1.9 可以处理audio音频任务了
+ 1.10 面板最小化
+ 1.11 读书任务
+ 1.12 记忆面板位置
+ 1.13 保持窗口聚焦, 击败onblur的监视
+ 1.14 采用creatWorker维持后台运行
+ 1.15 和视频一样, 到阅读任务, 自动打开阅读窗口
     */

    //#region 防止多次启动
    if (!(window === window.top) || typeof zhy_settings !== 'undefined') return;
    //#endregion
    //#region /*======================div:函数定义==========================*/
    unsafeWindow.zhy_settings = GM_getValue("zhy_settings", {
        skipGreen: true,
        playbackRate: '1',
        loc_x: '50px',
        loc_y: '50px',
        scrollInterval: 500,
        setMin: 0,
    });
    zhy_settings.done = false;
    unsafeWindow.zhyDaDa = {};
    unsafeWindow.lastCallTime = 0;
    /**
     * 醒目的控制台输出
     * @param {"string"} log 要打印到控制台的话
     * @param {"报错"|"警告"|"启动"|"提示"|"幽灵白"} color 字体颜色 可选项为["报错"|"警告"|"启动"|"提示"|"幽灵白"] 默认绿色
     * @param {"int"} fontSize 字体大小, 默认24
     */
    zhyDaDa.sendLog = (log, color, fontSize) => {
        switch (color) {
            case "报错":
                color = "red";
                break;
            case "警告":
                color = "#F2AB26";
                break;
            case "启动":
                color = "#A162F7";
                break;
            case "提示":
                color = "#35D4C7";
                break;
            case "幽灵白":
                color = "ghostwhite";
                break;
            default:
                color = color || "#43bb88";
                break;
        }

        fontSize = fontSize || 24;
        console.log('%c' + log, 'color: ' + color + ';font-size: ' + fontSize + 'px;font-weight: bold;'); //text-decoration: underline;
    }

    zhyDaDa.getBaseDocument = () => {
        var currentWindow = window;

        while (currentWindow !== currentWindow.parent) {
            currentWindow = currentWindow.parent;
        }

        return currentWindow.document;
    }

    /**
     * 模拟cmd中的sleep函数
     * @param {"number"} d deltaTime 即要等待的时间差 照旧以毫秒为单位
     */
    function sleep(d) {
        return new Promise((success, fail) => {
            setTimeout(success, d);
        });
    }
    //#endregion

    function create(f) {
        var blob = new Blob(['(' + f + ')()']);
        var url = window.URL.createObjectURL(blob);
        var worker = new Worker(url);
        return worker;
    }

    const createWorker = (callback, time) => {
        var pollingWorker = create(`function (e) {
    setInterval(function () {
      this.postMessage(null)
    }, ${time})
  }`);
        pollingWorker.onmessage = callback
        return pollingWorker;
    }

    const stopWorker = (vm) => {
        try {
            vm && vm.terminate()
        } catch (err) {
            console.log(err)
        }
    }

    /*======================div:全局效果==========================*/



    zhyDaDa.findPray = () => {
        // 节流
        let currentTime = Date.now();
        if (currentTime - unsafeWindow.lastCallTime < 3600) return false;
        unsafeWindow.lastCallTime = currentTime;
        try {
            let points = $("#coursetree .roundpoint,.roundpointStudent", zhyDaDa.getBaseDocument()).get();
            // 0有任务 1任务完成 2无任务
            let currentPointIndex = 0;
            let pointsCataList = points.map((e, i) => {
                if (e.parentNode.className == "currents") {
                    currentPointIndex = i;
                }
                if (e.className.indexOf("jobCount") >= 0) {
                    return 0;
                } else if (e.className.indexOf("blue") >= 0) {
                    return 1;
                } else if (e.className.indexOf("noJob") >= 0) {
                    return 2;
                }
            });


            if (!unsafeWindow.zhy_settings.skipGreen) {
                let next = points[currentPointIndex + 1];
                next.parentNode.querySelector('a').click();
                unsafeWindow.setTimeout(() => { zhyDaDa.main(); }, 4800);
            } else if (points.length > 0) {
                let i = currentPointIndex;
                while (pointsCataList[++i] != 0 && i < points.length);
                if (i == points.length && pointsCataList[i] != 0) {
                    i = 0;
                    while (pointsCataList[++i] != 0 && i < currentPointIndex);
                    if (i == currentPointIndex) zhy_settings.done = true;
                }
                let next = points[i];
                next.parentNode.querySelector('a').click();
                unsafeWindow.setTimeout(() => { zhyDaDa.main(); }, 4800);
            }
        } catch { console.log("找不到猎物了"); }
    }

    zhyDaDa.turnToNextPage = () => {
        let focus = $(".currents", zhyDaDa.getBaseDocument()).get()[0];
        let neighbour = focus.parentNode.nextElementSibling;
        neighbour.firstElementChild.querySelector('a').click();
    }

    zhyDaDa.getIframes = () => {
        let aaa = $("iframe", zhyDaDa.getBaseDocument()).get();
        if (aaa.length < 1) {
            return false;
        } else if (aaa[0].id == "iframe") {
            aaa = $("iframe", aaa[0].contentWindow.document).get();
        }
        return aaa;
    }

    zhyDaDa.classifyTasks = (taskIframes) => {
        let tasks = []; //[$iframe,"catagory"]
        tasks = taskIframes.map((iframe) => {
            let className = iframe.className;
            let catagory;
            if (JSON.parse(iframe.getAttribute('data')).jobid.indexOf('read') == 0) catagory = "read";
            if (className.indexOf("video") > 0) catagory = "video";
            if (className.indexOf("pdf") > 0) catagory = "pdf";
            if (className.indexOf("ppt") > 0) catagory = "ppt";
            if (className.indexOf("audio") > 0) catagory = "audio";

            return [iframe, catagory];
        });
        return tasks;
    }

    zhyDaDa.dealTasks = (tasksList) => {
        return new Promise((resolve, reject) => {
            if (tasksList.length < 1) {
                resolve();
            } else {
                let iframeNode = tasksList[0][0];
                let iframeCata = tasksList[0][1];
                switch (iframeCata) {
                    case "video":
                        zhyDaDa.dealVideo(iframeNode).then(() => {
                            tasksList.shift();
                            zhyDaDa.dealTasks(tasksList).then(resolve);
                        });
                        break;

                    case "pdf":
                        zhyDaDa.dealPdf(iframeNode).then(() => {
                            tasksList.shift();
                            zhyDaDa.dealTasks(tasksList).then(resolve);
                        });
                        break;

                    case "ppt":
                        zhyDaDa.dealPpt(iframeNode).then(() => {
                            tasksList.shift();
                            zhyDaDa.dealTasks(tasksList).then(resolve);
                        });
                        break;
                    case "audio":
                        zhyDaDa.dealAudio(iframeNode).then(() => {
                            tasksList.shift();
                            zhyDaDa.dealTasks(tasksList).then(resolve);
                        });
                        break;

                    case "read":
                        zhyDaDa.dealRead(iframeNode).then(() => {
                            resolve();
                        });
                        break;

                    default:
                        reject("No such catagory");
                        break;
                }
            }

        })
    }

    //#region div: Video
    zhyDaDa.dealVideo = (iframeNode) => {
        return new Promise((resolve, reject) => {
            let iconNode = iframeNode.parentNode;
            if (iconNode.classList.length > 1 && zhy_settings.skipGreen) resolve();
            else {
                let videoNode = iframeNode.contentWindow.document.querySelector("video");
                zhyDaDa.watchVideo(videoNode).then(resolve);
            }

        })

    }

    zhyDaDa.watchVideo = (video) => {
            return new Promise((resolve, reject) => {
                video.addEventListener('ended', onEnded); // 添加 ended 事件处理程序

                video.volume = 0;
                video.playbackRate = Number(zhy_settings.playbackRate);
                video.pause = () => { return true }
                video.play(); // 开始播放视频

                function onEnded() {
                    video.removeEventListener('ended', onEnded); // 删除 ended 事件处理程序
                    resolve(); // 视频播放完成,设置 Promise 状态为 fulfilled
                }
            });
        }
        //#endregion 

    //#region div: Pdf
    zhyDaDa.dealPdf = (iframeNode) => {
            return new Promise((resolve, reject) => {
                let iconNode = iframeNode.parentNode;
                let img = iframeNode.contentWindow.document.querySelector("#img");
                if (iconNode.classList.length > 1 && zhy_settings.skipGreen) resolve();
                else {
                    img.scrollTop = img.scrollHeight;
                    let btn = iframeNode.contentWindow.document.querySelector(".mkeRbtn");
                    let num = iframeNode.contentWindow.document.querySelector(".all").innerText;
                    num = Number(num);
                    for (let i = 0; i < num + 2; i++) {
                        btn.click();
                    }
                }
            });
        }
        //#endregion

    //#region div: Ppt
    zhyDaDa.dealPpt = (iframeNode) => {
            return new Promise((resolve, reject) => {
                let iconNode = iframeNode.parentNode;
                if (iconNode.classList.length > 1 && zhy_settings.skipGreen) resolve();
                else {
                    let btn = iframeNode.contentWindow.document.querySelector(".nextBtn");
                    let num = iframeNode.contentWindow.document.querySelector(".all").innerText;
                    num = Number(num);
                    for (let i = 0; i < num + 2; i++) {
                        btn.click();
                    }
                }

            })

        }
        //#endregion

    //#region div: Audio
    zhyDaDa.dealAudio = (iframeNode) => {
            return new Promise((resolve, reject) => {
                let iconNode = iframeNode.parentNode;
                if (iconNode.classList.length > 1 && zhy_settings.skipGreen) resolve();
                else {
                    let btn = iframeNode.contentWindow.document.querySelector(".vjs-play-control");
                    let audioElement = iframeNode.contentWindow.document.querySelector("audio");

                    audioElement.addEventListener('ended', onEnded); // 添加 ended 事件处理程序

                    audioElement.volume = 0;
                    audioElement.playbackRate = Number(zhy_settings.playbackRate);
                    audioElement.pause = () => { return true }
                    audioElement.play(); // 开始播放音频

                    function onEnded() {
                        audioElement.removeEventListener('ended', onEnded); // 删除 ended 事件处理程序
                        resolve(); // 音频播放完成,设置 Promise 状态为 fulfilled
                    }
                }

            })

        }
        //#endregion

    //#region div: Read
    zhyDaDa.dealRead = (iframeNode) => {
            return new Promise((resolve, reject) => {
                // 搜索所有的span, 找出其中innerText为"去阅读"的span, 点击它
                let toReadSpan;
                let frame_content = iframeNode.contentWindow.document.querySelector("#frame_content");
                let spans = frame_content.contentWindow.document.getElementsByTagName("span");
                for (let i = 0; i < spans.length; i++) {
                    if (spans[i].innerText == "去阅读") {
                        toReadSpan = spans[i];
                        break;
                    }
                }
                toReadSpan.click();
                resolve();
            });
        }
        //#endregion

    zhyDaDa.main = () => {

            // 节流
            let currentTime = Date.now();
            if (currentTime - unsafeWindow.lastCallTime < 3600) return false;
            unsafeWindow.lastCallTime = currentTime;
            if (zhy_settings.done) return true;
            let taskIframes = zhyDaDa.getIframes();
            if (!taskIframes) return false;
            let classifiedTasksList = zhyDaDa.classifyTasks(taskIframes);
            zhyDaDa.dealTasks(classifiedTasksList).then(() => { unsafeWindow.setTimeout(zhyDaDa.findPray, 6400); });
        }
        // 插入控制面板浮窗
    zhyDaDa.insertPanel = () => {
        let _style = `
        <style>
            #zhy_settings_panel_4 {
            position: fixed;
            top: ${zhy_settings.loc_y || "50px"};
            left: ${zhy_settings.loc_x || "50px"};
            background-color: #fff;
            border: 1px solid #ccc;
            border-radius: 5px;
            padding: 10px;
            font-size: 12px;
            z-index: 99999;
            width: 180px;
            }

            #zhy_settings_panel_4 h3 {
                margin-top: 0;
                margin-bottom: 10px;
            }
            #drag-handle {
                width: 100%;
                height: 18px;
                cursor: move;
                background: grey;
                border-radius: 10px;
                margin-bottom: 10px;    
                text-align: center;
                color: #bbb;
                user-select: none;
                min-width: 80px;
            }

            #zhy_settings_panel_4 .zhy_settings_item {
            margin-bottom: 10px;
            }

            #zhy_settings_panel_4 label {
            display: inline-block;
            width: 120px;
            }

            #playbackRateContainer {
            display: grid;
            }

            #playbackRateContainer input[type="radio"] {
            display: none;
            }

            #playbackRateContainer label {
            flex: 1;
            text-align: center;
            padding: 5px 10px;
            border: 1px solid #ccc;
            border-radius: 5px;
            cursor: pointer;
            }

            #playbackRateContainer input[type="radio"]:checked + label {
            background-color: #ccc;
            }

            #scrollInterval{
            width: 120px;
            }

        </style>
        `;
        let _html = `
        <div id="zhy_settings_panel_4">
        <div id="drag-handle" flag="0">双击最小化</div>
        <h3>设置面板</h3>
        <div class="zhy_settings_item" id="config">
            <label for="setMin">默认最小化面板</label>
            <input type="checkbox" id="setMin" checked>
        </div>
        <div class="zhy_settings_item">
            <label for="skipGreenToggle">跳过已经看完的任务点:</label>
            <input type="checkbox" id="skipGreenToggle" checked>
        </div>
        <div class="zhy_settings_item">
            <label for="startWork">启动刷课:</label>
            <button id="startWork">启动</button>
        </div>
        <div class="zhy_settings_item">
            <label>视频播放速率:</label>
            <div id="playbackRateContainer">
            <input type="radio" id="rate1" name="playbackRate" value="1" checked>
            <label for="rate1">1</label>

            <input type="radio" id="rate1.25" name="playbackRate" value="1.25">
            <label for="rate1.25">1.25</label>

            <input type="radio" id="rate1.5" name="playbackRate" value="1.5">
            <label for="rate1.5">1.5</label>

            <input type="radio" id="rate2" name="playbackRate" value="2">
            <label for="rate2">2</label>
            </div>
        </div>
        <hr>
        <div class="zhy_settings_item">
            <label for="scrollInterval">滚动间隔(毫秒):</label>
            <input type="number" id="scrollInterval" value="500">
        </div>

        </div>
        `;

        document.documentElement.insertAdjacentHTML('beforeend', _style + _html);


        // 获取设置面板元素
        var panel = document.getElementById("zhy_settings_panel_4");

        // 获取浮窗标题栏元素
        let panelHeader = document.getElementById("drag-handle");

        // 定义变量记录鼠标按下时的坐标和面板的初始位置
        let startX, startY, panelX, panelY;

        // 给标题栏添加鼠标按下事件监听器
        panelHeader.addEventListener("mousedown", function(e) {
            // 记录鼠标按下时的坐标和面板的初始位置
            startX = e.clientX;
            startY = e.clientY;
            panelX = panel.offsetLeft;
            panelY = panel.offsetTop;

            // 给 document 添加鼠标移动和松开事件监听器
            document.addEventListener("mousemove", movePanel);
            document.addEventListener("mouseup", stopPanel);
        });

        // 双击标题栏最小化面板
        panelHeader.addEventListener("dblclick", function() {
            // 判断面板是否已经最小化
            // 如果panel的flag属性值不为"1", 说明面板没有最小化
            if (panel.getAttribute("flag") != "1") {
                // 将panel中除了标题栏之外的元素隐藏
                for (let i = 1; i < panel.children.length; i++) {
                    panel.children[i].style.display = "none";
                }
                panelHeader.innerHTML = "双击恢复";
                // 修改panel的flag属性为"1"
                panel.setAttribute("flag", "1");
            } else {
                for (let i = 1; i < panel.children.length; i++) {
                    panel.children[i].style.display = "block";
                }
                panelHeader.innerHTML = "双击最小化";
                panelHeader.style.width = "100%";
                // 修改panel的flag属性为"0"
                panel.setAttribute("flag", "0");
            }
        });

        // 移动浮窗的函数
        function movePanel(e) {
            // 计算鼠标移动的距离
            var deltaX = e.clientX - startX;
            var deltaY = e.clientY - startY;

            // 更新面板的位置
            panel.style.left = panelX + deltaX + "px";
            panel.style.top = panelY + deltaY + "px";
        }

        // 停止移动浮窗的函数
        function stopPanel() {
            // 记录位置
            zhy_settings.loc_x = panel.style.left;
            zhy_settings.loc_y = panel.style.top;
            GM_setValue("zhy_settings", zhy_settings);
            // 移除鼠标移动和松开事件监听器
            document.removeEventListener("mousemove", movePanel);
            document.removeEventListener("mouseup", stopPanel);
        }


        // 获取设置面板中的元素
        let skipGreenToggle = document.getElementById("skipGreenToggle");
        let startWork = document.getElementById("startWork");
        let playbackRateRadios = document.getElementsByName("playbackRate");
        let setMin = document.getElementById("setMin");

        skipGreenToggle.checked = zhy_settings.skipGreen;
        let inputElement = $(`input[name='playbackRate'][value='${zhy_settings.playbackRate}']`).get();
        (inputElement.length > 0) && (inputElement[0].checked = true);
        setMin.checked = zhy_settings.setMin === 1 ? true : false;
        // 如果设置了默认最小化面板, 则最小化面板(即模拟双击标题栏)
        if (zhy_settings.setMin === 1) {
            panelHeader.dispatchEvent(new MouseEvent("dblclick"));
        }

        // 定义回调函数
        function settingsChanged(ratio) {
            zhy_settings.setMin = zhyDaDa.getBaseDocument().getElementById("setMin").checked ? 1 : 0;
            zhy_settings.skipGreen = zhyDaDa.getBaseDocument().getElementById("skipGreenToggle").checked ? 1 : 0;
            zhy_settings.playbackRate = ratio || zhy_settings.playbackRate;
            zhy_settings.scrollInterval = zhyDaDa.getBaseDocument().getElementById("scrollInterval").value;
            GM_setValue("zhy_settings", zhy_settings);
        }

        // 给setMin添加change事件监听器,当状态发生改变时调用回调函数
        setMin.addEventListener("change", () => { settingsChanged(); });

        // 给skipGreenToggle添加change事件监听器,当状态发生改变时调用回调函数
        skipGreenToggle.addEventListener("change", () => { settingsChanged(); });

        // 给startWork添加click事件监听器,当点击时调用回调函数
        startWork.addEventListener("click", () => { zhyDaDa.main(); });

        // 给playbackRateRadios中的每个单选按钮添加click事件监听器,当点击时调用回调函数
        playbackRateRadios.forEach(function(radio) {
            radio.addEventListener("click", (event) => { settingsChanged(event.target.value); });
        });

        // 给scrollInterval添加change事件监听器,当状态发生改变时调用回调函数
        document.getElementById("scrollInterval").addEventListener("change", (event) => {
            zhy_settings.scrollInterval = event.target.value;
            GM_setValue("zhy_settings", zhy_settings);
        });
    }

    /**
     * 读书功能实现
     */
    zhyDaDa.readBook = () => {
        //缓慢滚动页面到底部, 每2秒滚动一次, 滚动距离为屏幕高度的1/40
        let scrollInterval = createWorker(() => {
            // 滚动body
            document.body.scrollBy(0, window.innerHeight / 40);
            if (window.innerHeight + document.body.scrollTop >= document.body.children[0].offsetHeight) {
                // 找到id为loadbutton的a标签, 点击它, 找不到就clearInterval
                let loadButton = document.getElementById("loadbutton");
                if (loadButton) {
                    loadButton.click();
                } else {
                    clearInterval(scrollInterval);
                }
            }
        }, zhy_settings.scrollInterval || 500);

    }

    /**
     * 保持屏幕焦点
     */
    zhyDaDa.keepFocus = () => {
        const videoDom = document.createElement('video');
        const hiddenCanvas = document.createElement('canvas');
        videoDom.setAttribute('style', 'display:none');
        videoDom.setAttribute('muted', '');
        videoDom.muted = true;
        videoDom.setAttribute('autoplay', '');
        videoDom.autoplay = true;
        videoDom.setAttribute('playsinline', '');
        hiddenCanvas.setAttribute('style', 'display:none');
        hiddenCanvas.setAttribute('width', '1');
        hiddenCanvas.setAttribute('height', '1');
        hiddenCanvas.getContext('2d').fillRect(0, 0, 1, 1);
        videoDom.srcObject = hiddenCanvas.captureStream();
        try {
            unsafeWindow.onblur = () => {
                return true;
            }
            window.onblur = () => {
                return true;
            }
            unsafeWindow.focus();
            window.focus();
        } catch (e) {
            return true;
        }
    }

    zhyDaDa.sendLog("\n###################\n##zhyDaDa 网课助手##\n###################", "启动", 32);
    unsafeWindow.setTimeout(() => { zhyDaDa.insertPanel() }, 1000);
    if (document.title.indexOf("全屏显示") > -1) {
        unsafeWindow.setTimeout(() => { zhyDaDa.readBook() }, 2400);
    } else {
        unsafeWindow.setTimeout(() => { zhyDaDa.main() }, 4800);
        createWorker(() => { zhyDaDa.main() }, 15000);
    }
    createWorker(() => { zhyDaDa.keepFocus() }, 5000);
})();