2025年暑期教师研修|国家智慧教育平台|自动刷课(保活+章节跳转+防挂起增强版)

支持刷课跳转、防黑屏、防后台暂停、课程数量可配置,适用于 https://teacher.vocational.smartedu.cn/h/subject/summer2025/*

// ==UserScript==
// @name         2025年暑期教师研修|国家智慧教育平台|自动刷课(保活+章节跳转+防挂起增强版)
// @namespace    http://tampermonkey.net/
// @version      2025.07.25.Enhanced
// @description  支持刷课跳转、防黑屏、防后台暂停、课程数量可配置,适用于  https://teacher.vocational.smartedu.cn/h/subject/summer2025/*
// @author       snake
// @match        https://teacher.vocational.smartedu.cn/h/subject/summer2025/*
// @match        https://core.teacher.vocational.smartedu.cn/p/course/vocational/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=smartedu.cn
// @require      https://fastly.jsdelivr.net/npm/[email protected]/dist/sweetalert2.all.min.js
// @resource     css https://fastly.jsdelivr.net/npm/[email protected]/dist/sweetalert2.min.css
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @run-at       window-load
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

     const CONFIG = {
        homePage: "https://teacher.vocational.smartedu.cn/h/subject/summer2025/",
        courseUrls: [

            // 大力弘扬教育家精神
        "https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006810077161279488?itemId=1003783402737631232&type=1&segId=1003783189381775360&projectId=1003782210250866688&orgId=571896669159477248&originP=2&service=https%3A%2F%2Fteacher.vocational.smartedu.cn%2Fh%2Fsubject%2Fsummer2025%2F",
        // 数字素养提升
        "https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006810077178056704?itemId=1003783541964902400&type=1&segId=1003783492176969728&projectId=1003782210250866688&orgId=571896669159477248&originP=2&service=https%3A%2F%2Fteacher.vocational.smartedu.cn%2Fh%2Fsubject%2Fsummer2025%2F",

        "https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006810077190639616?itemId=1003783661561286656&type=1&segId=1003783607545495552&projectId=1003782210250866688&orgId=571896669159477248&originP=2&service=https%3A%2F%2Fteacher.vocational.smartedu.cn%2Fh%2Fsubject%2Fsummer2025%2F",
        // 心理健康教育能力提升
        "https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006810077203222528?itemId=1003783791077199872&type=1&segId=1003783745559068672&projectId=1003782210250866688&orgId=571896669159477248&originP=2&service=https%3A%2F%2Fteacher.vocational.smartedu.cn%2Fh%2Fsubject%2Fsummer2025%2F",
        // 实验室安全管理
        "https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006810077215805440?itemId=1003783955703631872&type=1&segId=1003783868238266368&projectId=1003782210250866688&orgId=571896669159477248&originP=2&service=https%3A%2F%2Fteacher.vocational.smartedu.cn%2Fh%2Fsubject%2Fsummer2025%2F",
        // 科创劳动教育的实践路径
        ],
       // 👇 每个课程要刷的视频数量(手动配置)
        courseVideoLimits: [9, 12, 3,5, 9], // 顺序对应上面的课程列表
        playbackRate: 1.0,
        mute: true,
        checkInterval: 3000
    };
    const SELECTORS = {
        videoListItem: ".video-title.clearfix",
        playButtonContainer: ".one",
        videoElement: "video",
        videoContainer: "#video",
        xgplayerStartButton: ".xgplayer-start",
        anyPopupConfirmButton: ".el-button.el-button--primary, .layui-layer-btn0",
    };

    function getCurrentCourseIndex() {
        const currentUrl = window.location.href;
        const currentCourseId = (currentUrl.match(/\/v_(\d+)/) || [])[1];
        for (let i = 0; i < CONFIG.courseUrls.length; i++) {
            const courseId = (CONFIG.courseUrls[i].match(/\/v_(\d+)/) || [])[1];
            if (courseId === currentCourseId) return i;
        }
        return -1;
    }

    function isCourseCompleted() {
        const index = getCurrentCourseIndex();
        const required = CONFIG.courseVideoLimits[index] || 10;
        const watched = document.querySelectorAll(`${SELECTORS.videoListItem}[data-progress="1"]`).length;
        return watched >= required;
    }

    function navigateToNextCourse() {
        const currentIndex = getCurrentCourseIndex();
        const nextIndex = currentIndex + 1;
        if (nextIndex < CONFIG.courseUrls.length) {
            window.location.href = CONFIG.courseUrls[nextIndex];
        } else {
            Swal.fire("🎉 所有课程完成", "挂机结束!", "success");
        }
    }

    function findAndPlayNextVideo() {
        if (isCourseCompleted()) {
            navigateToNextCourse();
            return;
        }
        const nextItem = document.querySelector(`${SELECTORS.videoListItem}:not([data-progress="1"])`);
        if (nextItem) {
            const playBtn = nextItem.querySelector(SELECTORS.playButtonContainer);
            if (playBtn) playBtn.click();
        } else {
            navigateToNextCourse();
        }
    }

    function handleVideoPlayback() {
        const video = document.querySelector(`${SELECTORS.videoContainer} ${SELECTORS.videoElement}`);
        if (!video) return;
        video.muted = CONFIG.mute;
        video.playbackRate = CONFIG.playbackRate;
        video.play().catch(() => {});
        video.addEventListener("ended", () => setTimeout(findAndPlayNextVideo, 1000));
    }

    function observeVideoChanges() {
        const targetNode = document.querySelector(SELECTORS.videoContainer);
        if (!targetNode) return setTimeout(observeVideoChanges, 2000);
        let lastSrc = "";
        const observer = new MutationObserver(() => {
            const video = targetNode.querySelector(SELECTORS.videoElement);
            if (video && video.src && video.src !== lastSrc) {
                lastSrc = video.src;
                handleVideoPlayback();
            }
        });
        observer.observe(targetNode, { childList: true, subtree: true });
    }

    function setupGlobalPopupObserver() {
        const observer = new MutationObserver(() => {
            const btn = document.querySelector(SELECTORS.anyPopupConfirmButton);
            if (btn && btn.offsetParent !== null) {
                observer.disconnect();
                btn.click();
                setTimeout(() => {
                    findAndPlayNextVideo();
                    setupGlobalPopupObserver();
                }, 2000);
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    function simulateUserActivity() {
        let lastTime = 0;
        function step(time) {
            if (time - lastTime > 2000) {
                document.dispatchEvent(new MouseEvent('mousemove'));
                document.dispatchEvent(new Event('scroll'));
                const video = document.querySelector('video');
                if (video && video.paused) {
                    video.play().catch(() => {});
                }
                lastTime = time;
            }
            requestAnimationFrame(step);
        }
        requestAnimationFrame(step);
    }

    function keepAwakeWhenHidden() {
        document.addEventListener("visibilitychange", () => {
            const video = document.querySelector("video");
            if (video && document.visibilityState === "hidden" && video.paused) {
                video.play().catch(() => {});
            }
        });

        setInterval(() => {
            const video = document.querySelector("video");
            if (document.visibilityState === "hidden" && video && video.paused) {
                video.play().catch(() => {});
            }
        }, 5000);
    }

    function main() {
        GM_addStyle(GM_getResourceText("css"));
        const href = window.location.href;

        if (CONFIG.courseUrls.some(u => href.includes("/" + u.match(/v_\d+/)[0]))) {
            setupGlobalPopupObserver();
            observeVideoChanges();
            setTimeout(findAndPlayNextVideo, 3000);
            keepAwakeWhenHidden();
            simulateUserActivity();
        } else if (href.startsWith(CONFIG.homePage)) {
            Swal.fire({
                title: '是否开启自动刷课?',
                text: '系统将自动播放视频并跳转章节',
                icon: 'question',
                showCancelButton: true,
                confirmButtonText: '是,开始',
                cancelButtonText: '否',
                allowOutsideClick: false
            }).then((result) => {
                if (result.isConfirmed) {
                    Swal.fire("准备跳转...", "", "info");
                    setTimeout(() => {
                        window.location.href = CONFIG.courseUrls[0];
                    }, 1500);
                }
            });
        }
    }

    window.addEventListener('load', main);
})();