专技天下自动刷课脚本

自动播放并静音,自动下一节,后台播放不暂停

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.greasyfork.org/scripts/588921/1887495/%E4%B8%93%E6%8A%80%E5%A4%A9%E4%B8%8B%E8%87%AA%E5%8A%A8%E5%88%B7%E8%AF%BE%E8%84%9A%E6%9C%AC.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         zgzjzj-autoplay
// @namespace    https://tampermonkey.net/
// @version      2.0
// @description  auto play zgzjzj courses, works when minimized
// @author       Antigravity
// @match        https://www.zgzjzj.com/learncenter/play*
// @match        https://*.zgzjzj.com/learncenter/play*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    // ============================================================
    // PART 1: Override Page Visibility API
    // This MUST run at document-start, before the site's own scripts
    // ============================================================

    // 1a. Override document.hidden to always return false
    Object.defineProperty(document, 'hidden', {
        get: function () { return false; },
        configurable: true
    });

    // 1b. Override document.visibilityState to always return 'visible'
    Object.defineProperty(document, 'visibilityState', {
        get: function () { return 'visible'; },
        configurable: true
    });

    // 1c. Override document.webkitVisibilityState (for older WebKit browsers)
    Object.defineProperty(document, 'webkitVisibilityState', {
        get: function () { return 'visible'; },
        configurable: true
    });

    // 1d. Block visibilitychange events so the site never knows you left
    document.addEventListener('visibilitychange', function (e) {
        e.stopImmediatePropagation();
    }, true);
    document.addEventListener('webkitvisibilitychange', function (e) {
        e.stopImmediatePropagation();
    }, true);

    // 1e. Block blur/focus events on window (some sites use these too)
    window.addEventListener('blur', function (e) {
        e.stopImmediatePropagation();
    }, true);
    window.addEventListener('focus', function (e) {
        e.stopImmediatePropagation();
    }, true);

    // 1f. Override document.hasFocus to always return true
    document.hasFocus = function () { return true; };

    console.log('[zgzjzj-helper] Visibility API override applied');

    // ============================================================
    // PART 2: Web Worker based timer (immune to browser throttling)
    // ============================================================

    function createWorkerTimer(callback, interval) {
        var blob = new Blob([
            'var iv = setInterval(function(){ postMessage("tick"); }, ' + interval + ');'
        ], { type: 'application/javascript' });
        var url = URL.createObjectURL(blob);
        var worker = new Worker(url);
        worker.onmessage = function () {
            callback();
        };
        return worker;
    }

    // ============================================================
    // PART 3: Main automation logic (runs after page loads)
    // ============================================================

    window.addEventListener('load', function () {
        console.log('[zgzjzj-helper] script loaded, starting automation');

        var CONFIG = {
            playbackRate: 1.0,
            checkInterval: 2000,
            autoMute: true
        };

        // --- UI Panel ---
        function createUI() {
            if (document.getElementById('zgzjzj-helper-panel')) return;
            var panel = document.createElement('div');
            panel.id = 'zgzjzj-helper-panel';
            panel.style.cssText = 'position:fixed;top:80px;right:20px;z-index:999999;background:rgba(15,23,42,0.92);color:#fff;padding:14px 18px;border-radius:10px;font-size:13px;box-shadow:0 8px 24px rgba(0,0,0,0.3);font-family:sans-serif;min-width:210px;border:1px solid rgba(255,255,255,0.1);backdrop-filter:blur(8px);';
            panel.innerHTML = '<div style="font-weight:bold;margin-bottom:8px;color:#38bdf8;">&#9889; \u8BFE\u7A0B\u81EA\u52A8\u64AD\u653E\u4E2D v2.0</div><div id="zgzjzj-status" style="margin-bottom:6px;color:#e2e8f0;">\u72B6\u6001: \u521D\u59CB\u5316...</div><div id="zgzjzj-progress" style="color:#94a3b8;font-size:12px;">\u6B63\u5728\u76D1\u542C\u89C6\u9891\u7EC4\u4EF6</div><div id="zgzjzj-vis" style="color:#4ade80;font-size:11px;margin-top:6px;">&#x2705; \u53EF\u89C1\u6027\u9501\u5B9A\u5DF2\u542F\u7528\uFF08\u6700\u5C0F\u5316\u4E0D\u4F1A\u6682\u505C\uFF09</div>';
            document.body.appendChild(panel);
        }

        function updateStatus(s, d) {
            var a = document.getElementById('zgzjzj-status');
            var b = document.getElementById('zgzjzj-progress');
            if (a) a.innerText = '\u72B6\u6001: ' + s;
            if (b && d) b.innerText = d;
        }

        // --- Popup handler ---
        function handlePopups() {
            var btns = document.querySelectorAll('.el-dialog__wrapper .el-button--primary, .el-message-box__btns .el-button--primary, .el-message-box__btns button');
            for (var i = 0; i < btns.length; i++) {
                if (btns[i] && btns[i].offsetParent !== null) {
                    console.log('[\u4E13\u6280\u5929\u4E0B\u52A9\u624B] \u70B9\u51FB\u5F39\u7A97\u6309\u94AE');
                    btns[i].click();
                }
            }
            var allBtns = document.querySelectorAll('button, .el-button, a, div[role="button"]');
            var keywords = ['\u7EE7\u7EED\u5B66\u4E60', '\u786E\u5B9A', '\u786E\u8BA4', '\u6211\u77E5\u9053\u4E86', '\u7EE7\u7EED\u64AD\u653E', '\u5F00\u59CB\u64AD\u653E'];
            for (var j = 0; j < allBtns.length; j++) {
                var txt = (allBtns[j].innerText || '').trim();
                if (allBtns[j].offsetParent !== null) {
                    for (var k = 0; k < keywords.length; k++) {
                        if (txt === keywords[k]) {
                            console.log('[\u4E13\u6280\u5929\u4E0B\u52A9\u624B] \u70B9\u51FB: ' + txt);
                            allBtns[j].click();
                            break;
                        }
                    }
                }
            }
        }

        // --- Next section ---
        function playNextSection() {
            console.log('[\u4E13\u6280\u5929\u4E0B\u52A9\u624B] \u6B63\u5728\u5BFB\u627E\u4E0B\u4E00\u8282...');
            updateStatus('\u5BFB\u627E\u4E0B\u4E00\u8282...', '\u68C0\u7D22\u8BFE\u7A0B\u76EE\u5F55\u4E2D');

            var nextKeywords = ['\u4E0B\u4E00\u8282', '\u4E0B\u4E00\u8BFE', '\u4E0B\u4E00\u7AE0'];
            var allEl = document.querySelectorAll('button, a, span, div');
            for (var i = 0; i < allEl.length; i++) {
                var t = (allEl[i].innerText || '').trim();
                for (var n = 0; n < nextKeywords.length; n++) {
                    if (t === nextKeywords[n] && allEl[i].offsetParent !== null) {
                        console.log('[\u4E13\u6280\u5929\u4E0B\u52A9\u624B] \u627E\u5230\u4E0B\u4E00\u8282\u6309\u94AE');
                        allEl[i].click();
                        return true;
                    }
                }
            }

            var items = document.querySelectorAll('.el-tree-node__content, li.el-menu-item, .course-item, .video-item, .catalog-item');
            var visible = [];
            for (var m = 0; m < items.length; m++) {
                if (items[m].innerText && items[m].innerText.trim().length > 0 && items[m].offsetParent !== null) {
                    visible.push(items[m]);
                }
            }

            if (visible.length > 0) {
                var currentIndex = -1;
                for (var p = 0; p < visible.length; p++) {
                    if (visible[p].classList.contains('active') || visible[p].classList.contains('is-active') || visible[p].classList.contains('current') || visible[p].querySelector('.active, .is-active')) {
                        currentIndex = p;
                        break;
                    }
                }
                if (currentIndex !== -1 && currentIndex + 1 < visible.length) {
                    console.log('[\u4E13\u6280\u5929\u4E0B\u52A9\u624B] \u70B9\u51FB\u7B2C ' + (currentIndex + 1) + ' \u4E2A\u5C0F\u8282');
                    visible[currentIndex + 1].click();
                    return true;
                }
            }

            updateStatus('\u672A\u68C0\u6D4B\u5230\u4E0B\u4E00\u8282', '\u8BF7\u624B\u52A8\u70B9\u51FB\u4E0B\u4E00\u8BFE');
            return false;
        }

        // --- Prevent video pause from external calls ---
        var videoPatchApplied = false;
        function patchVideoPause() {
            var video = document.querySelector('video');
            if (video && !videoPatchApplied) {
                var originalPause = video.pause.bind(video);
                video.pause = function () {
                    // Check if this pause call comes from visibility change
                    // Only allow pause if user explicitly clicks pause button
                    if (document.hidden === false && !video._userPaused) {
                        console.log('[zgzjzj-helper] blocked programmatic pause');
                        return;
                    }
                    return originalPause();
                };
                videoPatchApplied = true;
                console.log('[zgzjzj-helper] video.pause() patched');
            }
        }

        // --- Main loop ---
        var nextSectionTriggered = false;

        function mainLoop() {
            createUI();
            handlePopups();
            patchVideoPause();

            var video = document.querySelector('video');
            if (!video) {
                updateStatus('\u7B49\u5F85\u89C6\u9891\u52A0\u8F7D...', '\u672A\u627E\u5230\u64AD\u653E\u5668');
                nextSectionTriggered = false;
                return;
            }

            if (CONFIG.autoMute && !video.muted) {
                video.muted = true;
            }

            if (video.playbackRate !== CONFIG.playbackRate) {
                try { video.playbackRate = CONFIG.playbackRate; } catch (e) {}
            }

            if (video.ended) {
                if (!nextSectionTriggered) {
                    nextSectionTriggered = true;
                    updateStatus('\u672C\u8282\u5B8C\u6210', '\u6B63\u5728\u8DF3\u8F6C\u4E0B\u4E00\u8282...');
                    setTimeout(function () {
                        playNextSection();
                        // Reset flag after some time to allow re-trigger on new video
                        setTimeout(function () { nextSectionTriggered = false; }, 5000);
                    }, 1500);
                }
            } else if (video.paused) {
                nextSectionTriggered = false;
                updateStatus('\u5DF2\u6682\u505C', '\u6B63\u5728\u6062\u590D\u64AD\u653E...');
                video._userPaused = false;
                var p = video.play();
                if (p !== undefined) {
                    p.catch(function () {
                        video.muted = true;
                        video.play();
                    });
                }
            } else {
                nextSectionTriggered = false;
                var cur = Math.floor(video.currentTime);
                var tot = Math.floor(video.duration || 0);
                var pct = tot > 0 ? Math.floor((cur / tot) * 100) : 0;
                updateStatus('\u64AD\u653E\u4E2D \u25B6', '\u8FDB\u5EA6: ' + pct + '% (' + cur + '\u79D2 / ' + tot + '\u79D2)');
            }
        }

        // Use Web Worker timer so it keeps running when tab is in background
        try {
            createWorkerTimer(mainLoop, CONFIG.checkInterval);
            console.log('[zgzjzj-helper] Web Worker timer started');
        } catch (e) {
            // Fallback to setInterval if Worker fails
            console.log('[zgzjzj-helper] Worker failed, using setInterval fallback');
            setInterval(mainLoop, CONFIG.checkInterval);
        }

    }); // end window.load

})();