Greasy Fork

Greasy Fork is available in English.

浙江省全民终身学习公共服务平台自动播放

浙江省全民终身学习公共服务平台自动播放脚本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         浙江省全民终身学习公共服务平台自动播放
// @namespace    https://pages.zaizhexue.top
// @version      1.0
// @description  浙江省全民终身学习公共服务平台自动播放脚本
// @author       m0zey
// @match        https://*.zjlll.cn/*
// @grant        none
// @license       MIT
// ==/UserScript==

(function() {
    'use strict';

    // 等待页面加载完成
    function waitForElement(selector, callback) {
        const element = document.querySelector(selector);
        if (element) {
            callback(element);
        } else {
            setTimeout(() => waitForElement(selector, callback), 100);
        }
    }
    console.log('脚本已加载');


    // 创建播放按钮
    function createPlayButton() {
        const playButton = document.createElement('button');
        playButton.innerHTML = '<i class="el-icon-video-play"></i> 自动播放未完成章节';
        playButton.style.cssText = `
            background: #409EFF;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            margin-left: 10px;
            font-size: 14px;
            display: inline-flex;
            align-items: center;
            gap: 5px;
        `;
        
        // 添加悬停效果
        playButton.addEventListener('mouseenter', function() {
            this.style.background = '#66b1ff';
        });
        
        playButton.addEventListener('mouseleave', function() {
            this.style.background = '#409EFF';
        });
        
        // 添加点击事件
        playButton.addEventListener('click', function() {
            startPlaying();
        });
        
        return playButton;
    }

    // 创建停止按钮
    function createStopButton() {
        const stopButton = document.createElement('button');
        stopButton.innerHTML = '<i class="el-icon-video-pause"></i> 停止自动播放';
        stopButton.style.cssText = `
            background: #F56C6C;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            margin-left: 10px;
            font-size: 14px;
            display: none;
            align-items: center;
            gap: 5px;
        `;
        
        // 添加悬停效果
        stopButton.addEventListener('mouseenter', function() {
            this.style.background = '#f78989';
        });
        
        stopButton.addEventListener('mouseleave', function() {
            this.style.background = '#F56C6C';
        });
        
        // 添加点击事件
        stopButton.addEventListener('click', function() {
            stopAutoPlaying();
        });
        
        return stopButton;
    }

    // 创建状态显示元素
    function createStatusDisplay() {
        const display = document.createElement('div');
        display.style.cssText = `
            background: rgba(74, 144, 226, 0.1);
            color: #4a90e2;
            border: 1px solid rgba(74, 144, 226, 0.3);
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 14px;
            font-weight: 500;
            margin-right: 10px;
            display: none;
            align-items: center;
            gap: 6px;
            max-width: 300px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        `;
        display.textContent = '准备播放...';
        return display;
    }

    // 创建遮罩层
    function createOverlayMask() {
        const mask = document.createElement('div');
        mask.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.3);
            z-index: 9998;
            display: none;
            pointer-events: auto;
        `;
        
        // 阻止所有点击事件
        mask.addEventListener('click', function(e) {
            e.preventDefault();
            e.stopPropagation();
            return false;
        }, true);
        
        return mask;
    }

    // 显示遮罩层
    function showOverlayMask() {
        if (overlayMask) {
            overlayMask.style.display = 'block';
            // 确保停止按钮在遮罩层之上
            if (stopButton) {
                stopButton.style.position = 'relative';
                stopButton.style.zIndex = '9999';
            }
        }
    }

    // 隐藏遮罩层
    function hideOverlayMask() {
        if (overlayMask) {
            overlayMask.style.display = 'none';
            // 恢复停止按钮的样式
            if (stopButton) {
                stopButton.style.position = '';
                stopButton.style.zIndex = '';
            }
        }
    }

    // 自动播放状态
    let isAutoPlaying = false;
    let currentChapterIndex = 0;
    let unfinishedChapters = [];
    let playButton, stopButton, statusDisplay, overlayMask;

    // 停止自动播放
    function stopAutoPlaying() {
        console.log('停止自动播放');
        isAutoPlaying = false;
        currentChapterIndex = 0;
        unfinishedChapters = [];
        
        // 停止当前播放的视频
        const video = document.querySelector('video');
        if (video) {
            video.pause();
        }
        
        // 切换按钮显示状态
        if (playButton && stopButton) {
            playButton.style.display = 'inline-flex';
            stopButton.style.display = 'none';
        }
        
        // 隐藏状态显示
        if (statusDisplay) {
            statusDisplay.style.display = 'none';
        }
        
        // 隐藏遮罩层
        hideOverlayMask();
        
        alert('已停止自动播放');
    }

    // 获取未完成的章节列表
    function getUnfinishedChapters() {
        const chapterList = document.querySelectorAll('.sub-chapter-list .chapter');
        const unfinished = [];
        
        chapterList.forEach((chapter, index) => {
            const rateElement = chapter.querySelector('.rate');
            if (rateElement) {
                const rate = rateElement.textContent.trim();
                // 如果进度不是100%,则添加到未完成列表
                if (rate !== '100%') {
                    unfinished.push({
                        element: chapter,
                        index: index,
                        rate: rate,
                        name: chapter.querySelector('.chapter-name')?.textContent?.trim() || '未知章节'
                    });
                }
            }
        });
        
        return unfinished;
    }

    // 点击章节
    function clickChapter(chapter) {
        console.log(`点击章节: ${chapter.name} (进度: ${chapter.rate})`);
        chapter.element.click();
    }

    // 等待视频加载并播放
    function waitForVideoAndPlay() {
        return new Promise((resolve) => {
            const checkVideo = () => {
                const video = document.querySelector('video');
                if (video && video.readyState >= 2) { // 视频已加载足够数据
                    console.log('视频已加载,开始播放');
                    video.play().then(() => {
                        console.log('视频开始播放');
                        // 监听视频结束事件
                        video.addEventListener('ended', () => {
                            console.log('视频播放完成');
                            resolve();
                        }, { once: true });
                        

                    }).catch(err => {
                        console.error('视频播放失败:', err);
                        resolve();
                    });
                } else {
                    setTimeout(checkVideo, 500);
                }
            };
            checkVideo();
        });
    }

    // 自动播放下一个章节
    async function playNextChapter() {
        // 检查是否被停止
        if (!isAutoPlaying) {
            console.log('自动播放已被停止');
            return;
        }
        
        if (currentChapterIndex >= unfinishedChapters.length) {
            console.log('所有未完成章节已播放完毕');
            isAutoPlaying = false;
            
            // 恢复按钮状态
            if (playButton && stopButton) {
                playButton.style.display = 'inline-flex';
                stopButton.style.display = 'none';
            }
            
            // 隐藏状态显示
            if (statusDisplay) {
                statusDisplay.style.display = 'none';
            }
            
            // 隐藏遮罩层
            hideOverlayMask();
            
            alert('🎉 所有未完成章节已播放完毕!');
            return;
        }

        const chapter = unfinishedChapters[currentChapterIndex];
        console.log(`开始播放第 ${currentChapterIndex + 1}/${unfinishedChapters.length} 个章节: ${chapter.name}`);
        
        // 更新状态显示
        if (statusDisplay) {
            statusDisplay.textContent = `正在播放: ${chapter.name} (${currentChapterIndex + 1}/${unfinishedChapters.length})`;
        }
        
        // 点击章节
        clickChapter(chapter);
        
        // 等待页面加载和视频播放完成
        await new Promise(resolve => setTimeout(resolve, 2000)); // 等待页面加载
        
        // 再次检查是否被停止
        if (!isAutoPlaying) {
            console.log('自动播放已被停止');
            return;
        }
        
        await waitForVideoAndPlay();
        
        // 再次检查是否被停止
        if (!isAutoPlaying) {
            console.log('自动播放已被停止');
            return;
        }
        
        // 播放下一个章节
        currentChapterIndex++;
        setTimeout(() => playNextChapter(), 1000); // 1秒后播放下一个
    }

    // 播放功能
    function startPlaying() {
        console.log('开始自动播放课程...');
        
        if (isAutoPlaying) {
            console.log('已在自动播放中,请勿重复点击');
            return;
        }
        
        // 获取未完成的章节
        unfinishedChapters = getUnfinishedChapters();
        
        if (unfinishedChapters.length === 0) {
            alert('🎉 所有章节都已完成!');
            return;
        }
        
        console.log(`找到 ${unfinishedChapters.length} 个未完成章节:`);
        unfinishedChapters.forEach((chapter, index) => {
            console.log(`${index + 1}. ${chapter.name} (${chapter.rate})`);
        });
        
        // 确认开始自动播放
        const confirmed = confirm(`找到 ${unfinishedChapters.length} 个未完成章节,是否开始自动播放?\n\n注意:\n- 请保持页面在前台\n- 播放过程中请勿操作页面\n- 可点击"停止自动播放"按钮随时停止`);
        
        if (confirmed) {
            isAutoPlaying = true;
            currentChapterIndex = 0;
            
            // 切换按钮显示状态
            if (playButton && stopButton) {
                playButton.style.display = 'none';
                stopButton.style.display = 'inline-flex';
            }
            
            // 显示状态显示
            if (statusDisplay) {
                statusDisplay.style.display = 'inline-flex';
                statusDisplay.textContent = '准备播放...';
            }
            
            // 显示遮罩层,禁用其他元素点击
            showOverlayMask();
            
            playNextChapter();
        }
    }

    // 插入按钮的函数
    function insertButtons() {
        // 查找study-t元素
        const studyElement = document.querySelector('.study-t');
        if (!studyElement) {
            console.log('未找到.study-t元素,可能不在课程页面');
            return false;
        }
        
        // 检查是否已经插入过按钮
        if (studyElement.querySelector('.auto-play-button')) {
            console.log('按钮已存在,跳过插入');
            return true;
        }
        
        console.log('找到study-t元素,准备插入按钮');
        
        // 创建遮罩层(如果还没有创建)
        if (!overlayMask) {
            overlayMask = createOverlayMask();
            document.body.appendChild(overlayMask);
        }
        
        // 创建状态显示
        statusDisplay = createStatusDisplay();
        
        // 创建播放按钮
        playButton = createPlayButton();
        playButton.classList.add('auto-play-button'); // 添加标识类
        
        // 创建停止按钮
        stopButton = createStopButton();
        stopButton.classList.add('auto-play-button'); // 添加标识类
        
        // 初始状态:显示播放按钮,隐藏停止按钮和状态显示
        stopButton.style.display = 'none';
        statusDisplay.style.display = 'none';
        
        // 找到返回按钮的父容器
        const backButton = studyElement.querySelector('a.back');
        if (backButton) {
            // 在返回按钮前插入状态显示、播放按钮和停止按钮
            backButton.parentNode.insertBefore(statusDisplay, backButton);
            backButton.parentNode.insertBefore(playButton, backButton);
            backButton.parentNode.insertBefore(stopButton, backButton);
            console.log('状态显示、播放按钮和停止按钮已成功插入');
        } else {
            // 如果没有找到返回按钮,直接添加到study-t容器末尾
            studyElement.appendChild(statusDisplay);
            studyElement.appendChild(playButton);
            studyElement.appendChild(stopButton);
            console.log('状态显示、播放按钮和停止按钮已添加到容器末尾');
        }
        
        return true;
    }

    // 监听路由变化
    function observeRouteChanges() {
        let currentUrl = location.href;
        
        // 监听 popstate 事件(浏览器前进后退)
        window.addEventListener('popstate', function() {
            if (location.href !== currentUrl) {
                currentUrl = location.href;
                console.log('检测到路由变化(popstate):', currentUrl);
                setTimeout(() => {
                    insertButtons();
                }, 1000);
            }
        });
        
        // 监听 pushstate 和 replacestate(程序化路由变化)
        const originalPushState = history.pushState;
        const originalReplaceState = history.replaceState;
        
        history.pushState = function() {
            originalPushState.apply(history, arguments);
            if (location.href !== currentUrl) {
                currentUrl = location.href;
                console.log('检测到路由变化(pushState):', currentUrl);
                setTimeout(() => {
                    insertButtons();
                }, 1000);
            }
        };
        
        history.replaceState = function() {
            originalReplaceState.apply(history, arguments);
            if (location.href !== currentUrl) {
                currentUrl = location.href;
                console.log('检测到路由变化(replaceState):', currentUrl);
                setTimeout(() => {
                    insertButtons();
                }, 1000);
            }
        };
        
        // 使用 MutationObserver 监听 DOM 变化
        const observer = new MutationObserver(function(mutations) {
            let shouldCheck = false;
            mutations.forEach(function(mutation) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    // 检查是否有新的 study-t 元素被添加
                    for (let node of mutation.addedNodes) {
                        if (node.nodeType === 1) { // 元素节点
                            if (node.classList && node.classList.contains('study-t') || 
                                node.querySelector && node.querySelector('.study-t')) {
                                shouldCheck = true;
                                break;
                            }
                        }
                    }
                }
            });
            
            if (shouldCheck) {
                console.log('检测到DOM变化,可能有新的课程页面');
                setTimeout(() => {
                    insertButtons();
                }, 500);
            }
        });
        
        // 开始观察
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // 页面加载完成后执行
    window.addEventListener('load', function() {
        // 等待一段时间确保页面完全渲染
        setTimeout(() => {
            insertButtons();
        }, 1000);
        
        // 开始监听路由变化
        observeRouteChanges();
    });
    
    // 如果页面已经加载完成,立即执行
    if (document.readyState === 'complete') {
        setTimeout(() => {
            insertButtons();
            observeRouteChanges();
        }, 1000);
    }

})();