特定页面视频暂停检测

检测 hifa.shuoguoyun.com 页面中视频暂停,通过聚合推送通知(Token内置),并在右侧面板显示状态和控制通知。

// ==UserScript==
// @name         特定页面视频暂停检测
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  检测 hifa.shuoguoyun.com 页面中视频暂停,通过聚合推送通知(Token内置),并在右侧面板显示状态和控制通知。
// @author       Your Name
// @match        *://hifa.shuoguoyun.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      tui.juhe.cn
// @license      MPL-2.0 License
// ==/UserScript==

(function() {
    'use strict';

    const VIDEO_ID = 'bjy-player-teacher';
    const PUSH_API_URL = 'https://tui.juhe.cn/api/plus/pushApi';
    const SERVICE_ID = 'HsCOtSZ';
    const USER_TOKEN = ''; // <<< 在这里设置您的固定Token

    let videoElement = null;
    let eventListenerAttached = false;
    let statusPanel = null;
    let statusTextElement = null;
    let lastStatusText = '';

    let notificationToggle = null;

    // --- 节流函数 ---
    function throttle(func, limit) {
        let inThrottle;
        return function() {
            const args = arguments;
            const context = this;
            if (!inThrottle) {
                func.apply(context, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        }
    }

    // --- 注入CSS样式 ---
    GM_addStyle(`
        #videoStatusPanel_userscript {
            position: fixed;
            top: 70%;
            right: 0;
            transform: translateY(-50%);
            background-color: rgba(50, 50, 50, 0.85);
            color: white;
            padding: 10px 15px;
            border-top-left-radius: 8px;
            border-bottom-left-radius: 8px;
            z-index: 999999;
            font-family: Arial, sans-serif;
            font-size: 13px;
            box-shadow: -2px 0px 5px rgba(0,0,0,0.4);
            text-align: left;
            min-width: 150px; /* 可以适当减小宽度 */
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        #videoStatusPanel_userscript div {
            margin-bottom: 3px;
        }
        #videoStatusText_userscript {
            font-weight: bold;
            color: #FFD700;
            text-align: center;
            padding: 8px 0; /* 增加一点上下padding */
            border-top: 1px solid #777;
            margin-top: 8px; /* 增加与上方元素的间距 */
        }
        #videoStatusPanel_userscript label {
            display: inline-block;
            margin-right: 8px;
            color: #ddd;
        }
        #videoStatusPanel_userscript input[type="checkbox"] {
            vertical-align: middle;
        }
        #videoStatusPanel_userscript .panel-title {
            text-align: center;
            font-weight: bold;
            padding-bottom: 5px;
            border-bottom: 1px solid #777;
            color: #eee;
        }
        #videoStatusPanel_userscript .input-group {
            display: flex;
            align-items: center;
        }
    `);

    // --- 创建状态面板 ---
    function createStatusPanel() {
        if (document.getElementById('videoStatusPanel_userscript')) {
            statusPanel = document.getElementById('videoStatusPanel_userscript');
            statusTextElement = document.getElementById('videoStatusText_userscript');
            notificationToggle = document.getElementById('notificationToggle_userscript');
            return;
        }

        statusPanel = document.createElement('div');
        statusPanel.id = 'videoStatusPanel_userscript';

        const panelTitleElement = document.createElement('div');
        panelTitleElement.className = 'panel-title';
        panelTitleElement.textContent = '视频监控';
        statusPanel.appendChild(panelTitleElement);

        // 通知开关
        const notificationGroup = document.createElement('div');
        notificationGroup.className = 'input-group';
        const notificationLabel = document.createElement('label');
        notificationLabel.htmlFor = 'notificationToggle_userscript';
        notificationLabel.textContent = '开启暂停通知:';
        notificationToggle = document.createElement('input');
        notificationToggle.type = 'checkbox';
        notificationToggle.id = 'notificationToggle_userscript';
        notificationToggle.checked = GM_getValue('notificationEnabled_v07', false); // 使用新key避免与旧版本冲突
        notificationToggle.addEventListener('change', (event) => {
            GM_setValue('notificationEnabled_v07', event.target.checked);
            console.log('通知状态已保存:', event.target.checked);
            if (event.target.checked && !USER_TOKEN) { // 检查硬编码的TOKEN
                alert('脚本内未配置有效的USER_TOKEN!');
            }
        });
        notificationGroup.appendChild(notificationLabel);
        notificationGroup.appendChild(notificationToggle);
        statusPanel.appendChild(notificationGroup);

        // 状态显示
        statusTextElement = document.createElement('div');
        statusTextElement.id = 'videoStatusText_userscript';
        statusTextElement.textContent = '检测中...';
        statusPanel.appendChild(statusTextElement);

        document.body.appendChild(statusPanel);
        console.log('状态面板已创建 (Token内置)。');
    }

    function updateStatusPanelText(status) {
        if (statusTextElement && lastStatusText !== status) {
            statusTextElement.textContent = status;
            lastStatusText = status;
        }
    }

    // --- 发送通知 ---
    function sendPushNotification(title, content) {
        if (!USER_TOKEN) {
            console.error('错误:USER_TOKEN 未在脚本中定义!');
            updateStatusPanelText('状态: Token未配置!');
            alert('脚本内未配置有效的USER_TOKEN,无法发送通知。');
            return;
        }

        const params = new URLSearchParams();
        params.append('token', USER_TOKEN);
        params.append('title', title);
        params.append('content', content);
        params.append('service_id', SERVICE_ID);

        console.log('发送推送通知:', { title, content, service_id: SERVICE_ID });

        GM_xmlhttpRequest({
            method: 'POST',
            url: PUSH_API_URL,
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            data: params.toString(),
            onload: function(response) {
                console.log('推送通知响应:', response.responseText);
                try {
                    const jsonResponse = JSON.parse(response.responseText);
                    if (jsonResponse.code === 200 || jsonResponse.status === true || (typeof jsonResponse.message === 'string' && jsonResponse.message.includes("成功"))) {
                        console.log('通知发送成功!');
                        updateStatusPanelText('状态: 已暂停 (通知成功)');
                    } else {
                        console.error('通知发送失败:', jsonResponse.message || '未知错误');
                        updateStatusPanelText(`状态: 暂停 (通知失败: ${jsonResponse.message || ''})`);
                        alert(`通知发送失败: ${jsonResponse.message || '请检查Token和网络'}`);
                    }
                } catch (e) {
                    console.error('解析推送响应错误:', e, response.responseText);
                    updateStatusPanelText('状态: 已暂停 (通知响应异常)');
                }
            },
            onerror: function(response) {
                console.error('推送通知请求错误:', response);
                updateStatusPanelText('状态: 已暂停 (通知请求失败)');
                alert('通知请求失败,请检查网络或油猴脚本的跨域权限设置。');
            }
        });
    }

    function handleVideoPause(event) {
        const currentTime = new Date().toLocaleString('zh-CN', { hour12: false });
        const statusMsg = `状态: 已暂停 (时间: ${currentTime.split(' ')[1]})`;
        console.log(`视频 (ID: ${VIDEO_ID}) 已暂停于 ${currentTime}`);
        updateStatusPanelText(statusMsg);

        if (GM_getValue('notificationEnabled_v07', false)) {
            const title = "视频播放通知";
            const content = `视频 ${document.title || '未知页面'} 已停止播放,停止时间为 ${currentTime}`; // 增加了页面标题
            sendPushNotification(title, content);
        }
    }

    function handleVideoPlay(event) {
        console.log(`视频 (ID: ${VIDEO_ID}) 已播放.`);
        updateStatusPanelText('状态: 播放中');
    }

    function attachListenersToVideo() {
        if (videoElement && !eventListenerAttached) {
            videoElement.addEventListener('pause', handleVideoPause);
            videoElement.addEventListener('play', handleVideoPlay);
            eventListenerAttached = true;
            console.log(`已为视频 (ID: ${VIDEO_ID}) 添加暂停/播放监听器。`);

            if (videoElement.paused) {
                updateStatusPanelText('状态: 已暂停');
            } else {
                updateStatusPanelText('状态: 播放中');
            }
        } else if (videoElement && eventListenerAttached) {
             if (videoElement.paused) {
                updateStatusPanelText('状态: 已暂停');
            } else {
                updateStatusPanelText('状态: 播放中');
            }
        }
    }

    const checkVideoPresenceAndState = throttle(() => {
        let videoJustFoundOrReconfirmed = false;
        if (!videoElement || !document.body.contains(videoElement)) {
            videoElement = document.getElementById(VIDEO_ID);
            if (videoElement) {
                console.log(`通过 Observer 找到/确认视频元素 (ID: ${VIDEO_ID})。`);
                videoJustFoundOrReconfirmed = true;
                eventListenerAttached = false;
            } else {
                updateStatusPanelText('状态: 未找到视频');
                return;
            }
        }

        if (videoElement && (!eventListenerAttached || videoJustFoundOrReconfirmed)) {
            attachListenersToVideo();
        } else if (videoElement && eventListenerAttached) {
            if (videoElement.paused) {
                if(!statusTextElement.textContent.includes('已暂停')) {
                     updateStatusPanelText('状态: 已暂停');
                }
            } else {
                if(!statusTextElement.textContent.includes('播放中')) {
                    updateStatusPanelText('状态: 播放中');
                }
            }
        }

        if (!videoElement || !eventListenerAttached) {
            const dialogWrapper = document.querySelector('div.el-dialog__wrapper[style*="z-index: 2003"]');
            if (dialogWrapper && dialogWrapper.style.display !== 'none') {
                videoElement = document.getElementById(VIDEO_ID);
                 if (videoElement && !eventListenerAttached) {
                    console.log(`播放器容器可见,为视频 (ID: ${VIDEO_ID}) 附加监听器。`);
                    attachListenersToVideo();
                }
            }
        }
    }, 250);


    function init() {
        createStatusPanel();
        videoElement = document.getElementById(VIDEO_ID);

        if (videoElement) {
            console.log(`初始找到视频元素 (ID: ${VIDEO_ID})。`);
            attachListenersToVideo();
        } else {
            console.log(`页面加载时未找到视频元素 (ID: ${VIDEO_ID}),将通过 MutationObserver 监测。`);
            updateStatusPanelText('状态: 搜索视频中...');
        }

        const observer = new MutationObserver((mutationsList, observerInstance) => {
            checkVideoPresenceAndState();
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['style', 'id', 'class']
        });
        console.log('MutationObserver 已启动。');
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();