党校自动刷课

阻止页面通过visibilitychange等事件检测标签页切换和后台状态,并自动静音视频,同时阻止暂停视频,保持播放。

当前为 2025-11-15 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         党校自动刷课
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  阻止页面通过visibilitychange等事件检测标签页切换和后台状态,并自动静音视频,同时阻止暂停视频,保持播放。
// @author       AMT
// @match        *://你的党校url/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 保存原始属性值
    const originalVisibilityState = Object.getOwnPropertyDescriptor(Document.prototype, 'visibilityState');
    const originalHidden = Object.getOwnPropertyDescriptor(Document.prototype, 'hidden');
    const originalHasFocus = Object.getOwnPropertyDescriptor(Document.prototype, 'hasFocus');

    // 重写 visibilityState 属性,始终返回 'visible'
    Object.defineProperty(document, 'visibilityState', {
        get: function() {
            return 'visible';
        },
        configurable: false
    });

    // 重写 hidden 属性,始终返回 false
    Object.defineProperty(document, 'hidden', {
        get: function() {
            return false;
        },
        configurable: false
    });

    // 重写 hasFocus 方法,始终返回 true
    Object.defineProperty(document, 'hasFocus', {
        get: function() {
            return function() {
                return true;
            };
        },
        configurable: false
    });

    // 阻止所有相关的事件
    const eventsToBlock = [
        'visibilitychange',
        'webkitvisibilitychange',
        'blur',
        'focus',
        'pagehide',
        'pageshow'
    ];

    eventsToBlock.forEach(eventName => {
        // 在捕获阶段阻止事件传播
        document.addEventListener(eventName, function(event) {
            event.stopImmediatePropagation();
            event.preventDefault();
        }, true);

        window.addEventListener(eventName, function(event) {
            event.stopImmediatePropagation();
            event.preventDefault();
        }, true);
    });

    // 额外处理 window 的 blur 和 focus 事件
    const windowEvents = ['blur', 'focus', 'beforeunload', 'unload'];
    windowEvents.forEach(eventName => {
        window.addEventListener(eventName, function(event) {
            event.stopImmediatePropagation();
            event.preventDefault();
            return false;
        }, true);
    });

    // 阻止 requestAnimationFrame 在后台时暂停
    const originalRAF = window.requestAnimationFrame;
    window.requestAnimationFrame = function(callback) {
        return originalRAF.call(this, callback);
    };

    // 阻止 setInterval 在后台时降速
    const originalSetInterval = window.setInterval;
    window.setInterval = function(callback, delay) {
        return originalSetInterval.call(this, callback, delay);
    };

    // 阻止 setTimeout 在后台时降速
    const originalSetTimeout = window.setTimeout;
    window.setTimeout = function(callback, delay) {
        return originalSetTimeout.call(this, callback, delay);
    };

    // 静音所有现有视频,但不阻止播放
    function muteAllVideos() {
        const videos = document.querySelectorAll('video');
        videos.forEach(video => {
            try {
                // 只设置静音,不干扰播放状态
                video.muted = true;
                if (typeof video.volume !== 'undefined') {
                    video.volume = 0;
                }

                // 如果视频被暂停了(可能是因为离开页面),尝试重新播放
                if (video.paused && !video.ended && video.readyState > 2) {
                    const playPromise = video.play();
                    if (playPromise !== undefined) {
                        playPromise.catch(e => {
                            // 忽略播放错误,可能是用户交互限制
                            console.log('视频自动播放被阻止:', e);
                        });
                    }
                }

                console.log('视频已静音:', video.src || '未知来源');
            } catch (e) {
                console.log('处理视频时出错:', e);
            }
        });
    }

    // 监听视频状态变化,确保静音状态下持续播放
    function setupVideoWatchers() {
        const videos = document.querySelectorAll('video');
        videos.forEach(video => {
            // 移除可能已存在的监听器,避免重复
            if (!video._muteWatcher) {
                video._muteWatcher = true;

                // 监听暂停事件,如果是静音状态且不是用户手动暂停,则恢复播放
                video.addEventListener('pause', function(e) {
                    if (this.muted && !this.ended && this.readyState > 2) {
                        // 检查是否是因为页面不可见导致的暂停
                        setTimeout(() => {
                            if (this.paused && this.muted) {
                                const playPromise = this.play();
                                if (playPromise !== undefined) {
                                    playPromise.catch(err => {
                                        console.log('恢复播放失败:', err);
                                    });
                                }
                            }
                        }, 100);
                    }
                }, true);

                // 监听播放事件,确保播放时是静音状态
                video.addEventListener('play', function() {
                    if (!this.muted) {
                        this.muted = true;
                        if (typeof this.volume !== 'undefined') {
                            this.volume = 0;
                        }
                    }
                }, true);
            }
        });
    }

    // 监听新视频元素的添加
    const videoObserver = new MutationObserver(function(mutations) {
        let shouldMute = false;
        mutations.forEach(function(mutation) {
            mutation.addedNodes.forEach(function(node) {
                if (node.nodeName === 'VIDEO') {
                    shouldMute = true;
                } else if (node.querySelectorAll) {
                    const newVideos = node.querySelectorAll('video');
                    if (newVideos.length > 0) {
                        shouldMute = true;
                    }
                }
            });
        });

        if (shouldMute) {
            setTimeout(() => {
                muteAllVideos();
                setupVideoWatchers();
            }, 300);
        }
    });

    // 重写 Video 构造函数,自动设置静音但保持播放
    const OriginalVideo = window.HTMLVideoElement || window.Video;
    if (OriginalVideo) {
        const originalVideoProto = OriginalVideo.prototype;

        // 重写 play 方法,确保播放前静音
        const originalPlay = originalVideoProto.play;
        if (originalPlay) {
            originalVideoProto.play = function() {
                try {
                    this.muted = true;
                    if (typeof this.volume !== 'undefined') {
                        this.volume = 0;
                    }
                } catch (e) {
                    console.log('播放前静音失败:', e);
                }
                return originalPlay.call(this);
            };
        }
    }

    // 初始化视频静音功能
    function initVideoMuting() {
        // 立即静音现有视频
        muteAllVideos();
        setupVideoWatchers();

        // 监听DOM变化,静音新添加的视频
        videoObserver.observe(document, {
            childList: true,
            subtree: true
        });

        // 定期检查并静音视频(防止页面代码取消静音)
        setInterval(() => {
            muteAllVideos();
            setupVideoWatchers();
        }, 3000);

        console.log('视频自动静音功能已启用,同时保持播放状态');
    }

    // 页面加载完成后初始化视频静音
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initVideoMuting);
    } else {
        initVideoMuting();
    }

    // 额外措施:防止浏览器自动暂停
    document.addEventListener('visibilitychange', function(e) {
        // 这个监听器在我们阻止事件传播之前执行
        const videos = document.querySelectorAll('video');
        videos.forEach(video => {
            if (video.paused && video.muted) {
                setTimeout(() => {
                    video.play().catch(e => {});
                }, 50);
            }
        });
    }, false);
    console.log('页面后台状态检测已被阻止');

})();