党校自动刷课

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

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

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

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

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

您需要先安装一个扩展,例如 篡改猴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('页面后台状态检测已被阻止');

})();