// ==UserScript== // @name 浙江省全民终身学习公共服务平台自动播放 // @namespace https://pages.zaizhexue.top // @version 1.0 // @description 浙江省全民终身学习公共服务平台自动播放脚本 // @author m0zey // @match https://*.zjlll.cn/* // @grant none // ==/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 = ' 自动播放未完成章节'; 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 = ' 停止自动播放'; 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); } })();