Plyr Video Controller Pro (可安装版)

带浮动控制面板的Plyr视频控制器(播放时触发跳转+自动点击确认)

// ==UserScript==
// @name         Plyr Video Controller Pro (可安装版)
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  带浮动控制面板的Plyr视频控制器(播放时触发跳转+自动点击确认)
// @author       glenn
// @match        http://dxzx.ouc.edu.cn/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @license      MIT
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 配置存储
    const config = {
        autoSkip: GM_getValue('autoSkip', true),
        enableSeek: GM_getValue('enableSeek', true),
        playbackSpeed: GM_getValue('speed', 2.0),
        skipDelay: GM_getValue('delay', 100),
        autoClick: GM_getValue('autoClick', true),
        debugMode: GM_getValue('debug', true)
    };

    // 精准样式(匹配目标网站UI)
    GM_addStyle(`
        #plyr-control-panel {
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 9999;
            background: rgba(255,255,255,0.95);
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 12px 15px;
            width: 260px;
            box-shadow: 0 3px 12px rgba(0,0,0,0.15);
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        }
        #plyr-control-panel h3 {
            margin: 0 0 10px 0;
            color: #333;
            font-size: 16px;
            font-weight: 600;
            border-bottom: 1px solid #eee;
            padding-bottom: 8px;
        }
        .control-row {
            margin: 12px 0;
            display: flex;
            align-items: center;
        }
        .control-label {
            flex: 1;
            font-size: 14px;
            color: #555;
        }
        .control-btn {
            background: #f8f9fa;
            border: 1px solid #dadce0;
            color: #3c4043;
            padding: 6px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 13px;
            transition: all 0.2s;
        }
        .control-btn:hover {
            background: #f1f3f4;
            border-color: #d2e3fc;
        }
        .control-checkbox {
            margin-right: 8px;
            accent-color: #1a73e8;
        }
        .speed-input {
            width: 50px;
            padding: 4px;
            border: 1px solid #dadce0;
            border-radius: 4px;
            text-align: center;
        }
    `);

    // 自动点击"我知道了"(精准匹配图片结构)
    function autoClickConfirm() {
        if (!config.autoClick) return false;
        
        // 精确匹配路径:.public_cont1 > .public_table > .public_text > a.public_result
        const confirmButton = document.querySelector('.public_cont1 .public_table .public_text a.public_result');
        
        if (confirmButton && confirmButton.textContent.trim() === "我知道了") {
            confirmButton.click();
            debugLog('自动点击成功');
            return true;
        }
        return false;
    }

    // 创建控制面板(中文化界面)
    function createControlPanel() {
        const panel = document.createElement('div');
        panel.id = 'plyr-control-panel';
        panel.innerHTML = `
            <h3>Plyr视频控制器</h3>
            <div class="control-row">
                <label class="control-label">
                    <input type="checkbox" class="control-checkbox" id="auto-skip" ${config.autoSkip ? 'checked' : ''}>
                    自动跳转结尾
                </label>
            </div>
            <div class="control-row">
                <label class="control-label">
                    <input type="checkbox" class="control-checkbox" id="enable-seek" ${config.enableSeek ? 'checked' : ''}>
                    启用进度条
                </label>
            </div>
            <div class="control-row">
                <label class="control-label">
                    <input type="checkbox" class="control-checkbox" id="auto-click" ${config.autoClick ? 'checked' : ''}>
                    自动点击确认
                </label>
            </div>
            <div class="control-row">
                <span class="control-label">播放速度:</span>
                <input type="number" class="speed-input" id="speed-control" min="0.5" max="4" step="0.1" value="${config.playbackSpeed}">
            </div>
            <div class="control-row" style="justify-content: flex-end;">
                <button class="control-btn" id="apply-btn">应用设置</button>
                <button class="control-btn" id="reset-btn">重置视频</button>
            </div>
        `;
        document.body.appendChild(panel);

        // 事件监听
        document.getElementById('apply-btn').addEventListener('click', applySettings);
        document.getElementById('reset-btn').addEventListener('click', resetVideo);
    }

    // 应用设置
    function applySettings() {
        config.autoSkip = document.getElementById('auto-skip').checked;
        config.enableSeek = document.getElementById('enable-seek').checked;
        config.autoClick = document.getElementById('auto-click').checked;
        config.playbackSpeed = parseFloat(document.getElementById('speed-control').value);

        GM_setValue('autoSkip', config.autoSkip);
        GM_setValue('enableSeek', config.enableSeek);
        GM_setValue('autoClick', config.autoClick);
        GM_setValue('speed', config.playbackSpeed);

        if (window.currentPlayer) {
            setupPlayerHooks(window.currentPlayer);
        }
        alert('设置已保存!');
    }

    // 重置视频
    function resetVideo() {
        if (window.currentPlayer) {
            window.currentPlayer.currentTime = 0;
            window.currentPlayer.play();
        }
    }

    // 核心控制逻辑
    function setupPlayerHooks(player) {
        // 移除原有限制
        player.off('ended');
        player.off('timeupdate');

        // 设置播放速度
        player.speed = config.playbackSpeed;

        // 启用进度条
        if (config.enableSeek) {
            player.media.controls = true;
            player.media.removeAttribute('controlslist');
            document.querySelector('.plyr__progress').style.display = 'block';
        }

        // 播放时跳转逻辑
        player.on('play', () => {
            if (config.autoSkip) {
                setTimeout(() => {
                    if (player.duration > 0) {
                        player.currentTime = player.duration - 1;
                        debugLog('已触发结尾跳转');
                    }
                }, config.skipDelay);
            }
            autoClickConfirm();
        });

        // 添加自动点击触发点
        player.on('pause', autoClickConfirm);
        player.on('ended', autoClickConfirm);
    }

    // 播放器检测
    function detectPlayer() {
        const video = document.querySelector('video');
        if (video) {
            const checkInterval = setInterval(() => {
                if (video.plyr) {
                    clearInterval(checkInterval);
                    window.currentPlayer = video.plyr;
                    setupPlayerHooks(video.plyr);
                }
            }, 500);
        }
    }

    // 初始化
    function init() {
        createControlPanel();
        detectPlayer();
        
        // 监听动态内容加载
        new MutationObserver((mutations) => {
            detectPlayer();
            autoClickConfirm();
        }).observe(document.body, {
            childList: true,
            subtree: true
        });

        // 定时检查确认按钮(每2秒)
        setInterval(autoClickConfirm, 2000);
    }

    // 调试日志
    function debugLog(message) {
        if (config.debugMode) console.log(`[Plyr控制] ${new Date().toLocaleTimeString()}: ${message}`);
    }

    // 启动
    if (document.readyState === 'complete') {
        init();
    } else {
        window.addEventListener('load', init);
    }
})();