雨课堂防暂停

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

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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();

})();