学习助手 - 自动确认与视频播放

自动点击学习提示的确定按钮,并在视频暂停时自动按空格键播放

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         学习助手 - 自动确认与视频播放
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  自动点击学习提示的确定按钮,并在视频暂停时自动按空格键播放
// @author       Assistant
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_notification
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 配置项
    const config = {
        // 提示文本检测
        targetText: /你已学习了\d+分钟,继续学习请按确定,从头学习请按取消/,
        buttonText: ['确定', '确认', '继续学习', '是'],
        checkInterval: 1000,
        
        // 视频检测
        videoCheckInterval: 2000,
        autoPlayVideo: true,
        videoSelectors: [
            'video',
            '.video',
            '[class*="video"]',
            'iframe',
            'object',
            'embed'
        ],
        
        // 界面显示
        enableUI: true,
        enableLog: true,
        showNotifications: true
    };

    // 状态变量
    let isMonitoring = true;
    let lastVideoCheck = 0;
    let foundVideos = new Set();

    // 日志函数
    function log(...args) {
        if (config.enableLog) {
            console.log('[学习助手]', ...args);
        }
    }

    // 创建状态指示器
    function createStatusIndicator() {
        if (!config.enableUI) return;
        
        const indicator = document.createElement('div');
        indicator.id = 'study-assistant-indicator';
        indicator.innerHTML = `
            <div style="
                position: fixed;
                top: 10px;
                right: 10px;
                background: #4CAF50;
                color: white;
                padding: 8px 12px;
                border-radius: 4px;
                font-size: 12px;
                z-index: 10000;
                box-shadow: 0 2px 5px rgba(0,0,0,0.2);
                cursor: move;
                opacity: 0.9;
            ">
                <strong>学习助手</strong>
                <div>状态: <span id="study-status">运行中</span></div>
                <div>视频: <span id="video-status">检测中</span></div>
            </div>
        `;
        
        document.body.appendChild(indicator);
        
        // 简单的拖拽功能
        let isDragging = false;
        let dragOffset = { x: 0, y: 0 };
        
        indicator.addEventListener('mousedown', startDrag);
        document.addEventListener('mousemove', doDrag);
        document.addEventListener('mouseup', stopDrag);
        
        function startDrag(e) {
            isDragging = true;
            const rect = indicator.getBoundingClientRect();
            dragOffset.x = e.clientX - rect.left;
            dragOffset.y = e.clientY - rect.top;
            indicator.style.opacity = '0.7';
        }
        
        function doDrag(e) {
            if (!isDragging) return;
            indicator.style.left = (e.clientX - dragOffset.x) + 'px';
            indicator.style.top = (e.clientY - dragOffset.y) + 'px';
            indicator.style.right = 'auto';
        }
        
        function stopDrag() {
            isDragging = false;
            indicator.style.opacity = '0.9';
        }
        
        return indicator;
    }

    // 更新状态显示
    function updateStatus(message, type = 'info') {
        if (!config.enableUI) return;
        
        const statusElement = document.getElementById('study-status');
        const videoElement = document.getElementById('video-status');
        
        if (statusElement && type === 'info') {
            statusElement.textContent = message;
        }
        if (videoElement && type === 'video') {
            videoElement.textContent = message;
        }
    }

    // 显示通知
    function showNotification(title, message) {
        if (config.showNotifications) {
            // 使用浏览器通知(如果可用)
            if (typeof GM_notification !== 'undefined') {
                GM_notification({
                    title: title,
                    text: message,
                    timeout: 3000
                });
            } else if ('Notification' in window && Notification.permission === 'granted') {
                new Notification(title, { body: message });
            }
        }
        
        log(title + ': ' + message);
    }

    // 查找按钮元素
    function findConfirmButton() {
        const buttonSelectors = [
            'button',
            'input[type="button"]',
            'input[type="submit"]',
            '.btn',
            '.button',
            '[class*="btn"]',
            '[class*="button"]',
            '[onclick]'
        ];

        const buttonTexts = Array.isArray(config.buttonText) ? config.buttonText : [config.buttonText];

        for (const selector of buttonSelectors) {
            const buttons = document.querySelectorAll(selector);
            for (const button of buttons) {
                const buttonText = button.textContent?.trim() || button.value || button.getAttribute('title') || '';
                if (!buttonText) continue;
                
                for (const targetText of buttonTexts) {
                    let isMatch = false;
                    if (targetText instanceof RegExp) {
                        isMatch = targetText.test(buttonText);
                    } else {
                        isMatch = buttonText.includes(targetText);
                    }
                    
                    if (isMatch) {
                        const style = window.getComputedStyle(button);
                        if (style.display !== 'none' && style.visibility !== 'hidden' && 
                            !button.disabled && style.opacity !== '0') {
                            return button;
                        }
                    }
                }
            }
        }
        return null;
    }

    // 检查页面是否包含目标文本
    function containsTargetText() {
        const walker = document.createTreeWalker(
            document.body,
            NodeFilter.SHOW_TEXT,
            null,
            false
        );

        let node;
        while (node = walker.nextNode()) {
            const text = node.textContent.trim();
            if (config.targetText instanceof RegExp) {
                if (config.targetText.test(text)) {
                    return true;
                }
            } else if (text.includes(config.targetText)) {
                return true;
            }
        }
        return false;
    }

    // 检查并点击确定按钮
    function checkAndClickConfirm() {
        if (!isMonitoring) return false;
        
        if (containsTargetText()) {
            log('检测到学习提示框');
            const button = findConfirmButton();
            
            if (button) {
                log('找到确定按钮,准备点击');
                updateStatus('点击确定按钮');
                
                try {
                    // 模拟真实点击事件序列
                    ['mousedown', 'mouseup', 'click'].forEach(eventType => {
                        button.dispatchEvent(new MouseEvent(eventType, {
                            bubbles: true,
                            cancelable: true,
                            view: window
                        }));
                    });
                    
                    button.click();
                    showNotification('学习助手', '已自动点击确定按钮');
                    log('成功点击确定按钮');
                    return true;
                } catch (error) {
                    log('点击过程中发生错误:', error);
                }
            } else {
                log('未找到匹配的确定按钮');
            }
        }
        return false;
    }

    // 查找视频元素
    function findVideoElements() {
        const videos = [];
        
        // 查找原生video元素
        const nativeVideos = document.querySelectorAll('video');
        nativeVideos.forEach(video => {
            if (!foundVideos.has(video)) {
                videos.push(video);
                foundVideos.add(video);
            }
        });
        
        // 查找其他可能的视频容器
        config.videoSelectors.forEach(selector => {
            if (selector === 'video') return; // 已经处理过
            
            const elements = document.querySelectorAll(selector);
            elements.forEach(element => {
                // 检查元素是否包含视频特征
                if (element.src && element.src.match(/\.(mp4|webm|ogg)/i) ||
                    element.innerHTML.includes('video') ||
                    element.className.match(/video/)) {
                    if (!foundVideos.has(element)) {
                        videos.push(element);
                        foundVideos.add(element);
                    }
                }
            });
        });
        
        return videos;
    }

    // 模拟空格键按下
    function simulateSpaceKey() {
        log('模拟空格键按下');
        updateStatus('恢复视频播放');
        
        // 创建并发送键盘事件
        const spaceKeyEvent = new KeyboardEvent('keydown', {
            key: ' ',
            code: 'Space',
            keyCode: 32,
            which: 32,
            bubbles: true,
            cancelable: true
        });
        
        document.activeElement.dispatchEvent(spaceKeyEvent);
        
        // 也发送keyup事件
        const keyUpEvent = new KeyboardEvent('keyup', {
            key: ' ',
            code: 'Space',
            keyCode: 32,
            which: 32,
            bubbles: true,
            cancelable: true
        });
        
        document.activeElement.dispatchEvent(keyUpEvent);
    }

    // 检查视频状态并处理暂停
    function checkVideoStatus() {
        if (!isMonitoring || !config.autoPlayVideo) return;
        
        const now = Date.now();
        if (now - lastVideoCheck < config.videoCheckInterval) return;
        lastVideoCheck = now;
        
        const videos = findVideoElements();
        if (videos.length === 0) {
            updateStatus('未检测到视频', 'video');
            return;
        }
        
        updateStatus(`检测到 ${videos.length} 个视频`, 'video');
        
        let foundPaused = false;
        videos.forEach((video, index) => {
            // 对于原生video元素
            if (video.tagName === 'VIDEO') {
                if (video.paused && !video.ended) {
                    log(`检测到视频 ${index + 1} 已暂停,尝试播放`);
                    foundPaused = true;
                    
                    // 先尝试直接播放
                    video.play().catch(error => {
                        log(`直接播放失败: ${error},尝试模拟空格键`);
                        simulateSpaceKey();
                    });
                }
            } else {
                // 对于其他视频元素,模拟空格键
                log(`检测到视频容器 ${index + 1},模拟空格键`);
                simulateSpaceKey();
                foundPaused = true;
            }
        });
        
        if (foundPaused) {
            showNotification('学习助手', '检测到视频暂停,已自动恢复播放');
        }
    }

    // 初始化视频监控
    function initVideoMonitoring() {
        // 定期检查视频状态
        setInterval(checkVideoStatus, config.videoCheckInterval);
        
        // 监听页面焦点变化,恢复时检查视频
        document.addEventListener('visibilitychange', function() {
            if (!document.hidden) {
                setTimeout(checkVideoStatus, 1000);
            }
        });
        
        log('视频监控已启动');
    }

    // 主初始化函数
    function init() {
        log('学习助手脚本已加载,正在初始化...');
        
        // 创建状态指示器
        if (config.enableUI) {
            createStatusIndicator();
        }
        
        // 启动提示框监控
        const confirmObserver = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.addedNodes.length > 0) {
                    setTimeout(checkAndClickConfirm, 300);
                }
            });
        });
        
        confirmObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
        
        // 定期检查提示框(备用)
        setInterval(checkAndClickConfirm, config.checkInterval);
        
        // 初始化视频监控
        if (config.autoPlayVideo) {
            initVideoMonitoring();
        }
        
        // 初始检查
        setTimeout(() => {
            checkAndClickConfirm();
            checkVideoStatus();
        }, 2000);
        
        // 提供控制接口
        window.StudyAssistant = {
            enable: function() {
                isMonitoring = true;
                updateStatus('运行中');
                showNotification('学习助手', '已启用');
            },
            disable: function() {
                isMonitoring = false;
                updateStatus('已暂停');
                showNotification('学习助手', '已暂停');
            },
            checkNow: function() {
                checkAndClickConfirm();
                checkVideoStatus();
            },
            config: config
        };
        
        log('学习助手初始化完成');
        showNotification('学习助手', '脚本已启动,开始监控学习提示和视频状态');
    }

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