雨课堂防暂停

防止雨课堂视频切屏暂停,提升学习体验

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         雨课堂防暂停
// @name:en      YuKeTang Anti-Pause
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  防止雨课堂视频切屏暂停,提升学习体验
// @description:en  Prevent YuKeTang video from pausing when switching tabs
// @author       YourName
// @license      MIT
// @match        https://*.yuketang.cn/*
// @match        https://*.xuetangx.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=yuketang.cn
// @grant        none
// @run-at       document-start
// @homepage     https://github.com/你的用户名/雨课堂防暂停
// @supportURL   https://github.com/你的用户名/雨课堂防暂停/issues
// ==/UserScript==

(function() {
    'use strict';

    console.log('[雨课堂防暂停] 插件已加载');

    // ==================== 全局状态管理 ====================
    const state = {
        userPausedManually: false,  // 用户是否主动暂停
        lastUserActionTime: 0        // 最后一次用户操作时间
    };

    // 配置参数
    const config = {
        userActionTimeout: 300      // 用户操作有效期(毫秒)
    };

    // ==================== 工具函数 ====================

    /**
     * 查找视频元素
     */
    function findVideoElement() {
        return document.querySelector('video');
    }

    /**
     * 等待视频元素加载
     */
    function waitForVideo(callback, maxAttempts = 50) {
        let attempts = 0;
        const checkVideo = setInterval(() => {
            const video = findVideoElement();
            if (video) {
                clearInterval(checkVideo);
                callback(video);
            } else if (++attempts >= maxAttempts) {
                clearInterval(checkVideo);
                console.log('[雨课堂防暂停] 未找到视频元素');
            }
        }, 200);
    }

    /**
     * 记录用户操作时间
     */
    function recordUserAction() {
        state.lastUserActionTime = Date.now();
    }

    /**
     * 判断是否在用户操作有效期内
     */
    function isWithinUserActionTimeout() {
        return (Date.now() - state.lastUserActionTime) < config.userActionTimeout;
    }

    /**
     * 安全地调用函数
     */
    function safeCall(fn, ...args) {
        try {
            return fn(...args);
        } catch (e) {
            console.error('[雨课堂防暂停] 错误:', e);
            return null;
        }
    }

    // ==================== 防止视频切屏暂停 ====================

    /**
     * 欺骗页面可见性API
     */
    function spoofVisibilityAPI() {
        // 拦截 document.hidden
        Object.defineProperty(document, 'hidden', {
            get: function() { return false; },
            configurable: true
        });

        // 拦截 document.visibilityState
        Object.defineProperty(document, 'visibilityState', {
            get: function() { return 'visible'; },
            configurable: true
        });

        // 拦截 document.hasFocus
        document.hasFocus = function() { return true; };

        console.log('[雨课堂防暂停] 页面可见性API已被欺骗');
    }

    /**
     * 拦截visibilitychange事件
     */
    function interceptVisibilityChange() {
        document.addEventListener('visibilitychange', function(e) {
            e.stopImmediatePropagation();
        }, true);

        window.addEventListener('blur', function(e) {
            e.stopImmediatePropagation();
        }, true);

        window.addEventListener('focus', function(e) {
            e.stopImmediatePropagation();
        }, true);

        console.log('[雨课堂防暂停] 可见性事件已拦截');
    }

    /**
     * 监听用户主动暂停操作
     */
    function monitorUserPauseActions(video) {
        // 监听空格键
        document.addEventListener('keydown', function(e) {
            if (e.code === 'Space' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
                recordUserAction();
                state.userPausedManually = !video.paused;
                console.log('[雨课堂防暂停] 用户按空格键,暂停状态:', state.userPausedManually);
            }
        }, true);

        // 监听播放/暂停按钮点击
        document.addEventListener('click', function(e) {
            const target = e.target;
            // 检查是否点击了播放控制相关的元素
            if (target.closest('.xt_video_player_common_btn') ||
                target.closest('.vjs-play-control') ||
                target.closest('[class*="play"]') ||
                target.closest('[class*="pause"]')) {
                recordUserAction();
                state.userPausedManually = !video.paused;
                console.log('[雨课堂防暂停] 用户点击播放按钮,暂停状态:', state.userPausedManually);
            }
        }, true);

        console.log('[雨课堂防暂停] 用户操作监听已启动');
    }

    /**
     * 自动恢复播放
     */
    function autoResumePlayback(video) {
        // 监听pause事件
        video.addEventListener('pause', function() {
            // 如果在用户操作有效期内,认为是用户主动暂停
            if (isWithinUserActionTimeout()) {
                console.log('[雨课堂防暂停] 检测到用户主动暂停');
                return;
            }

            // 如果用户之前标记了主动暂停,不恢复播放
            if (state.userPausedManually) {
                console.log('[雨课堂防暂停] 用户已主动暂停,不自动播放');
                return;
            }

            // 否则认为是系统自动暂停,尝试恢复播放
            console.log('[雨课堂防暂停] 检测到系统自动暂停,尝试恢复播放');
            setTimeout(() => {
                if (video.paused && !state.userPausedManually) {
                    safeCall(() => video.play());
                }
            }, 100);
        }, true);

        // 监听play事件,清除手动暂停标记
        video.addEventListener('play', function() {
            state.userPausedManually = false;
            console.log('[雨课堂防暂停] 视频开始播放,清除暂停标记');
        }, true);

        console.log('[雨课堂防暂停] 自动恢复播放功能已启动');
    }

    /**
     * 初始化防切屏暂停功能
     */
    function initAntiPauseFeature() {
        spoofVisibilityAPI();
        interceptVisibilityChange();

        waitForVideo((video) => {
            monitorUserPauseActions(video);
            autoResumePlayback(video);
            console.log('[雨课堂防暂停] 防切屏暂停功能已启用');
        });
    }

    // ==================== 主初始化 ====================

    function init() {
        console.log('[雨课堂防暂停] 开始初始化...');

        // 等待页面基本加载
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => {
                initAntiPauseFeature();
            });
        } else {
            initAntiPauseFeature();
        }

        console.log('[雨课堂防暂停] 初始化完成!');
    }

    // 启动插件
    init();

})();