// ==UserScript== // @name 人工智能2026观看脚本V1.4 -仅供学习 // @namespace http://tampermonkey.net/ // @version 1.4 // @description 修复点击开始没反应+智慧教育平台最新适配+终极防切屏+防暂停 // @author nuoya // @match *://*.smartedu.cn/* // @match https://higher.smartedu.cn/* // @match https://service.icourses.cn/* // @grant GM_addStyle // @grant GM_openInTab // @run-at document-end // ==/UserScript== (function() { 'use strict'; // ==================== 【终极防切屏】彻底解决最小化/切屏停止问题 ==================== try { // 1. 拦截所有切屏相关事件 const blockEvents = ['visibilitychange', 'webkitvisibilitychange', 'blur', 'pagehide', 'focusout']; blockEvents.forEach(eventName => { window.addEventListener(eventName, e => e.stopImmediatePropagation(), true); document.addEventListener(eventName, e => e.stopImmediatePropagation(), true); }); // 2. 重写页面可见性属性(最关键!99%的网站通过这个检测切屏) Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'hidden', { get: () => false, configurable: true }); // 3. 重写窗口焦点属性 Object.defineProperty(window, 'hasFocus', { value: () => true, configurable: true }); console.log("🚀 终极挂课脚本 V5.4.1 已启动!切屏/最小化检测已完全拦截"); } catch (e) { console.warn("⚠️ 部分防切屏功能受限,但核心播放不受影响"); } // ==================== 样式定义 ==================== GM_addStyle(` #auto-study-panel { position: fixed; top: 20px; right: 20px; width: 340px; background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); z-index: 99999999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #ffffff; overflow: hidden; transition: all 0.3s ease; user-select: none; border: 1px solid rgba(255, 255, 255, 0.1); } #auto-study-panel.minimized { width: 60px; height: 60px; border-radius: 50%; } #auto-study-panel.minimized .panel-content { display: none; } #auto-study-panel.minimized .panel-header { height: 60px; padding: 0; justify-content: center; } #auto-study-panel.minimized .panel-title { display: none; } .panel-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; background: rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.1); cursor: move; } .panel-title { font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; } .panel-title::before { content: ''; width: 8px; height: 8px; background: #10b981; border-radius: 50%; animation: pulse 2s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .panel-controls { display: flex; gap: 8px; } .panel-btn { width: 28px; height: 28px; border: none; border-radius: 8px; background: rgba(255, 255, 255, 0.1); color: #ffffff; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; font-size: 14px; } .panel-btn:hover { background: rgba(255, 255, 255, 0.2); } .panel-btn.minimize-btn { font-size: 18px; } .panel-content { padding: 20px; } .status-section { margin-bottom: 20px; } .status-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; font-size: 14px; } .status-label { color: #94a3b8; } .status-value { font-weight: 500; color: #ffffff; } .status-value.running { color: #10b981; } .status-value.paused { color: #f59e0b; } .status-value.stopped { color: #ef4444; } .progress-bar { width: 100%; height: 8px; background: rgba(255, 255, 255, 0.1); border-radius: 4px; overflow: hidden; margin-top: 8px; } .progress-fill { height: 100%; background: linear-gradient(90deg, #10b981, #06b6d4); border-radius: 4px; transition: width 0.3s ease; } .settings-section { margin-bottom: 20px; padding-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.1); } .settings-title { font-size: 14px; font-weight: 600; margin-bottom: 12px; color: #94a3b8; } .setting-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } .setting-label { font-size: 13px; color: #cbd5e1; } .setting-input { width: 70px; padding: 6px 10px; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 6px; background: rgba(255, 255, 255, 0.05); color: #ffffff; font-size: 13px; text-align: center; } .setting-input:focus { outline: none; border-color: #10b981; } .action-buttons { display: flex; gap: 10px; margin-bottom: 16px; } .action-btn { flex: 1; padding: 12px 0; border: none; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; } .action-btn.expand-btn { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); color: #ffffff; } .action-btn.expand-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4); } .action-btn.start-btn { background: linear-gradient(135deg, #10b981 0%, #06b6d4 100%); color: #0a0a0a; } .action-btn.start-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4); } .action-btn.pause-btn { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: #0a0a0a; } .action-btn.pause-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4); } .action-btn.stop-btn { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: #ffffff; } .action-btn.stop-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4); } .action-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none !important; box-shadow: none !important; } /* 推荐课程按钮样式 */ .recommend-section { margin-bottom: 16px; padding-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.1); } .recommend-title { font-size: 14px; font-weight: 600; margin-bottom: 12px; color: #94a3b8; } .recommend-buttons { display: flex; flex-direction: column; gap: 8px; } .recommend-btn { width: 100%; padding: 10px 0; border: none; border-radius: 8px; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s ease; text-align: center; } .recommend-btn.general { background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); color: #ffffff; } .recommend-btn.ai-x { background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); color: #ffffff; } .recommend-btn.llm { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: #0a0a0a; } .recommend-btn:hover { transform: translateY(-1px); box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3); } .log-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.1); } .log-title { font-size: 14px; font-weight: 600; margin-bottom: 10px; color: #94a3b8; display: flex; justify-content: space-between; align-items: center; } .clear-log-btn { font-size: 12px; color: #94a3b8; background: none; border: none; cursor: pointer; } .clear-log-btn:hover { color: #ffffff; } .log-container { height: 140px; overflow-y: auto; background: rgba(0, 0, 0, 0.2); border-radius: 8px; padding: 10px; font-size: 12px; font-family: 'Consolas', monospace; color: #e2e8f0; } .log-container::-webkit-scrollbar { width: 4px; } .log-container::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 2px; } .log-item { margin-bottom: 4px; line-height: 1.4; } .log-item.info { color: #06b6d4; } .log-item.success { color: #10b981; } .log-item.warning { color: #f59e0b; } .log-item.error { color: #ef4444; } .platform-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 11px; font-weight: 600; margin-left: 6px; background: #10b981; color: #0a0a0a; } `); // ==================== 全局变量 ==================== let isRunning = false; let isPaused = false; let currentIndex = 0; let totalVideos = 0; let videoItems = []; let pauseResolve = null; let stopRequested = false; let isAllExpanded = false; let isSmartEduPlatform = false; // 高等教育智慧教育平台标识 // 默认配置(通用模式使用,智慧教育平台强制使用你指定的参数) const config = { scrollDelay: 1500, loadDelay: 5000, closeDelay: 3000, checkInterval: 2000, maxTimeout: 900 }; // 推荐课程配置 const recommendCourses = [ { name: "人工智能应用导论(通识课)", url: "https://higher.smartedu.cn/course/agc3/69ba53600976b58e126c4d8e?type=training", class: "general" }, { name: "自动驾驶场景设计(AI+X)", url: "https://higher.smartedu.cn/course/agc3/69b56a1ff74ce762ce352749?type=training", class: "ai-x" }, { name: "DeepSeek大模型前沿进展", url: "https://higher.smartedu.cn/course/lmc/67d7cf98625fca5f6cf9076e", class: "llm" } ]; // ==================== UI创建 ==================== function createPanel() { // 防止重复创建面板 if (document.getElementById('auto-study-panel')) { console.log("⚠️ 面板已存在,跳过创建"); return; } const panel = document.createElement('div'); panel.id = 'auto-study-panel'; panel.innerHTML = `
自动挂课助手 V5.4.1 ${isSmartEduPlatform ? '智慧教育专属' : ''}
运行状态 已停止
播放进度 0 / 0
参数设置(仅通用模式生效)
滚动等待(ms)
加载等待(ms)
关闭等待(ms)
🔥 推荐课程快速跳转
${recommendCourses.map(course => `` ).join('')}
运行日志
`; document.body.appendChild(panel); bindEvents(); makeDraggable(panel); log('info', '✅ 终极挂课脚本 V5.4.1 已加载完成'); log('success', '🛡️ 切屏/最小化检测已完全拦截,后台挂机零中断'); // 平台检测提示 if (isSmartEduPlatform) { log('success', '🎯 检测到高等教育智慧教育平台,已强制启用figure标签专属脚本'); log('info', 'ℹ️ 完全按照你提供的脚本逻辑执行,无需展开章节'); } else { log('info', 'ℹ️ 通用模式已启用,请先点击"展开全部"再开始学习'); } console.log("✅ UI面板创建成功,开始按钮已绑定事件"); } // ==================== 事件绑定(修复:确保事件正确绑定) ==================== function bindEvents() { // 先移除所有可能存在的旧事件 const startBtn = document.getElementById('start-btn'); const newStartBtn = startBtn.cloneNode(true); startBtn.parentNode.replaceChild(newStartBtn, startBtn); // 重新绑定事件 newStartBtn.addEventListener('click', () => { console.log("🔘 开始学习按钮被点击!"); log('info', '🔘 开始学习按钮被点击'); startAutoStudy(); }); document.getElementById('expand-btn').addEventListener('click', expandAllChapters); document.getElementById('pause-btn').addEventListener('click', togglePause); document.getElementById('stop-btn').addEventListener('click', stopAutoStudy); document.getElementById('minimize-btn').addEventListener('click', toggleMinimize); document.getElementById('clear-log-btn').addEventListener('click', clearLog); // 绑定推荐课程按钮事件 document.querySelectorAll('.recommend-btn').forEach(btn => { btn.addEventListener('click', () => { const url = btn.getAttribute('data-url'); GM_openInTab(url, { active: true }); log('info', `🔗 正在打开推荐课程: ${btn.textContent}`); }); }); // 配置输入框事件(仅通用模式生效) document.getElementById('scroll-delay').addEventListener('change', (e) => { if (!isSmartEduPlatform) config.scrollDelay = parseInt(e.target.value) || 1500; }); document.getElementById('load-delay').addEventListener('change', (e) => { if (!isSmartEduPlatform) config.loadDelay = parseInt(e.target.value) || 5000; }); document.getElementById('close-delay').addEventListener('change', (e) => { if (!isSmartEduPlatform) config.closeDelay = parseInt(e.target.value) || 3000; }); console.log("✅ 所有事件绑定完成"); } // ==================== 拖拽功能 ==================== function makeDraggable(element) { const header = document.getElementById('panel-header'); let isDragging = false; let offsetX, offsetY; header.addEventListener('mousedown', (e) => { if (e.target.closest('.panel-controls')) return; isDragging = true; offsetX = e.clientX - element.offsetLeft; offsetY = e.clientY - element.offsetTop; element.style.zIndex = '99999999'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const x = e.clientX - offsetX; const y = e.clientY - offsetY; element.style.left = `${x}px`; element.style.top = `${y}px`; element.style.right = 'auto'; }); document.addEventListener('mouseup', () => { isDragging = false; }); } // ==================== 核心功能:自动展开所有章节(通用模式) ==================== async function expandAllChapters() { if (isSmartEduPlatform) { log('warning', '⚠️ 智慧教育平台专属模式无需展开章节'); return; } log('info', '🔍 正在扫描所有章节...'); try { // 第一步:展开所有大章节(li.list-item) const mainChapters = document.querySelectorAll('li.list-item'); log('info', `📚 找到 ${mainChapters.length} 个大章节`); for (let i = 0; i < mainChapters.length; i++) { if (stopRequested) break; const chapter = mainChapters[i]; const leftItem = chapter.querySelector('.left-item'); const icon = leftItem?.querySelector('.icon'); // 通过icon的is-expanded类判断是否已经展开 if (leftItem && icon && !icon.classList.contains('is-expanded')) { log('info', `📖 展开大章节 ${i + 1}/${mainChapters.length}`); safeClick(leftItem); await randomSleep(400, 800); } } // 第二步:展开所有小节(ant-collapse-item) const subChapters = document.querySelectorAll('.ant-collapse-item'); log('info', `📑 找到 ${subChapters.length} 个小节`); for (let i = 0; i < subChapters.length; i++) { if (stopRequested) break; const chapter = subChapters[i]; const header = chapter.querySelector('.ant-collapse-header'); if (header && header.getAttribute('aria-expanded') !== 'true') { log('info', `📖 展开小节 ${i + 1}/${subChapters.length}`); safeClick(header); await randomSleep(400, 800); } } // 等待所有内容渲染完成 await sleep(1500); // 第三步:收集所有可见的视频项(同时支持.name和.child-item) videoItems = Array.from(document.querySelectorAll('.name, .child-item')).filter(item => { return item.offsetParent !== null; }); totalVideos = videoItems.length; if (totalVideos === 0) { log('warning', '⚠️ 未找到任何可见的视频项'); } else { log('success', `🎉 所有章节已成功展开,共找到 ${totalVideos} 个可见视频`); } isAllExpanded = true; updateUI(); } catch (error) { log('error', `❌ 展开章节时出错: ${error.message}`); console.error(error); } } // ==================== 核心功能:自动学习(自动切换模式) ==================== async function startAutoStudy() { console.log("🚀 startAutoStudy函数被调用"); if (isRunning) { log('warning', '⚠️ 脚本已经在运行中'); console.log("⚠️ 脚本已经在运行中,isRunning =", isRunning); return; } try { if (isSmartEduPlatform) { // 高等教育智慧教育平台:强制使用你提供的figure脚本逻辑 await startSmartEduExclusiveAutoStudy(); } else { // 通用模式(保留原V5.3逻辑) await startIntegratedAutoStudy(); } } catch (error) { log('error', `❌ 学习过程中出错: ${error.message}`); console.error("❌ 学习过程中出错:", error); } isRunning = false; isPaused = false; updateUI(); console.log("🏁 学习流程结束,isRunning已重置为false"); } // ==================== 【完全按照你要求+修复】智慧教育平台专属脚本逻辑 ==================== async function startSmartEduExclusiveAutoStudy() { isRunning = true; isPaused = false; stopRequested = false; currentIndex = 0; updateUI(); log('info', '🚀 启动高等教育智慧教育平台专属自动学习(figure标签版)'); console.log("🚀 启动智慧教育平台专属自动学习"); // 【修复1:增加视频查询重试机制】最多重试3次,每次等待2秒 let videoItems = null; for (let retry = 0; retry < 3; retry++) { videoItems = document.querySelectorAll('figure[class*="cursor-pointer"]'); if (videoItems.length > 0) break; log('warning', `⚠️ 第 ${retry + 1} 次未找到视频,等待2秒后重试...`); console.log(`⚠️ 第 ${retry + 1} 次未找到视频,等待2秒后重试...`); await sleep(2000); } if (!videoItems || videoItems.length === 0) { log('error', '❌ 经过3次重试仍未找到视频封面,请确认:1. 页面是否完全加载 2. 是否在课程播放页面'); console.error("❌ 经过3次重试仍未找到视频封面"); isRunning = false; updateUI(); return; } totalVideos = videoItems.length; log('info', `共识别到 ${totalVideos} 个视频,准备开始按顺序播放...`); console.log(`共识别到 ${videoItems.length} 个视频,准备开始无脑按顺序播放...`); updateUI(); for (let i = 0; i < videoItems.length; i++) { if (stopRequested) break; currentIndex = i + 1; updateUI(); const currentItem = videoItems[i]; // 等待暂停(保留原V5控制功能) if (isPaused) { log('info', '⏸️ 已暂停,等待继续...'); await new Promise(resolve => { pauseResolve = resolve; }); if (stopRequested) break; log('info', '▶️ 继续学习'); } // 2. 滚动到当前视频并点击(完全按照你指定的参数) log('info', `▶️ 正在打开第 ${currentIndex} 个视频...`); console.log(`▶️ 正在打开第 ${i + 1} 个视频...`); currentItem.scrollIntoView({ behavior: 'smooth', block: 'center' }); await sleep(1500); // 你指定的1500ms safeClick(currentItem); // 给弹窗和视频加载留出足够的时间(你指定的4000ms) log('info', "⏳ 等待弹窗和视频加载..."); console.log("⏳ 等待 4 秒钟让弹窗和播放器完全渲染..."); await sleep(4000); // 保留原V5带节奏强攻播放(增强稳定性) log('info', '🎬 正在激活视频播放...'); await forcePlayVideo(); // 3. 等待视频播放完毕(完全按照你指定的逻辑) log('info', "⏳ 正在等待视频播放完毕,请挂机..."); console.log("⏳ 正在等待视频播放完毕,请挂机..."); await watchSmartEduVideoUntilEnd(i + 1); // 4. 视频看完了,寻找并点击关闭按钮(完全按照你指定的优先顺序+修复) log('info', `✅ 第 ${currentIndex} 个视频结束,尝试关闭弹窗...`); console.log(`✅ 第 ${i + 1} 个视频结束,尝试关闭弹窗...`); // 【修复2:增加更多关闭按钮备选】 let closeBtn = document.querySelector('button[class*="bg-white"][class*="h-10"]'); if (!closeBtn) closeBtn = document.querySelector('button[class*="close"]'); if (!closeBtn) closeBtn = document.querySelector('.anticon-close'); if (!closeBtn) closeBtn = document.querySelector('.video-modal-close'); if (closeBtn) { log('info', "🎯 找到关闭按钮,点击关闭"); console.log("找到关闭按钮,点击关闭..."); safeClick(closeBtn); } else { // 如果没找到特征按钮,尝试模拟按 ESC 键退出弹窗 log('info', "🎯 未找到任何关闭按钮,尝试按 ESC 关闭..."); console.log("未找到任何关闭按钮,尝试按 ESC 关闭..."); document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true})); document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true})); } // 关掉后休息几秒(你指定的3000ms) await sleep(3000); } if (stopRequested) { log('info', '⏹️ 用户手动停止了学习'); } else { log('success', '🎉 所有界面的视频已全部轮询播放完毕!'); console.log("🎉 所有界面的视频已全部轮询播放完毕!"); } } // ==================== 通用模式自动学习(保留原V5.3逻辑) ==================== async function startIntegratedAutoStudy() { isRunning = true; isPaused = false; stopRequested = false; currentIndex = 0; updateUI(); console.log("🚀 终极挂课脚本 V5.4.1 通用模式已启动!"); // 只有在未展开过的情况下才自动展开 if (!isAllExpanded) { log('info', '🔄 检测到未展开章节,正在自动展开...'); await expandAllChapters(); } if (totalVideos === 0) { log('error', '❌ 没有找到任何视频,请先点击"展开全部"按钮!'); isRunning = false; updateUI(); return; } log('info', `✅ 共扫描到 ${totalVideos} 个视频节,准备开始自动过滤和播放...`); for (let i = 0; i < totalVideos; i++) { if (stopRequested) break; currentIndex = i + 1; updateUI(); const currentItem = videoItems[i]; // 双重进度检查,更准确 const processElement = currentItem.querySelector('.process'); const processText = processElement ? processElement.innerText : ''; const parentText = currentItem.parentElement ? currentItem.parentElement.innerText : currentItem.innerText; if (processText.includes('100%') || processText.includes('已完成') || parentText.includes('100%') || parentText.includes('已完成')) { log('info', `⏭️ 第 ${i + 1} 个视频进度为 [${processText || '100%'}],自动跳过!`); continue; } // 等待暂停 if (isPaused) { log('info', '⏸️ 已暂停,等待继续...'); await new Promise(resolve => { pauseResolve = resolve; }); if (stopRequested) break; log('info', '▶️ 继续学习'); } // 滚动并打开视频 log('info', `▶️ 正在滚动并打开第 ${i + 1} 个未完成的视频...`); currentItem.scrollIntoView({ behavior: 'smooth', block: 'center' }); await sleep(config.scrollDelay); safeClick(currentItem); await sleep(config.loadDelay); // 带节奏强攻播放 log('info', "🎬 正在激活视频播放..."); await forcePlayVideo(); // 双重监听进度 log('info', "⏳ 正在监测视频播放状态..."); await waitForProcessToEnd(currentItem, i + 1); // 关闭弹窗 log('info', `☑️ 第 ${i + 1} 个视频流程结束,尝试强制关闭弹窗...`); forceCloseModal(); await sleep(config.closeDelay); } if (stopRequested) { log('info', '⏹️ 用户手动停止了学习'); } else { log('success', '🎉 太棒了,所有任务脚本运行结束!'); } } // ==================== 带节奏强攻播放函数(保留原V5核心) ==================== async function forcePlayVideo() { const video = document.querySelector('video'); if (video) { video.muted = true; // 强制静音解锁自动播放 await sleep(500); } // 多层级套娃点击,点亮即停 const targets = [ document.querySelector('.video-play-img img'), // 最里面的图片 document.querySelector('.video-play-img'), // 中间层 document.querySelector('.video-play'), // 外层 document.querySelector('.vjs-big-play-button'), // 原生大按钮 document.querySelector('button[class*="play"]'), // 通用播放按钮 video // 直接点击视频本身 ]; for (let target of targets) { if (target && target.offsetParent !== null) { log('info', `👉 尝试物理点击播放按钮...`); safeClick(target); // 给框架800毫秒处理时间 await sleep(800); // 播放成功就不再点击后面的元素,防止误点暂停 if (video && !video.paused) { log('success', '✅ 播放成功激活!'); return; } } } // 终极兜底:直接调用视频API if (video && video.paused) { log('info', '👉 物理点击无效,强制调用底层播放API...'); video.play().catch((err) => { log('warning', `⚠️ 底层API被拦截: ${err.message},将继续轮询恢复`); console.log("底层API被拦截:", err); }); } } // ==================== 控制功能 ==================== function togglePause() { if (!isRunning) return; isPaused = !isPaused; updateUI(); if (!isPaused && pauseResolve) { pauseResolve(); pauseResolve = null; } } function stopAutoStudy() { if (!isRunning) return; stopRequested = true; if (isPaused && pauseResolve) { pauseResolve(); } } function toggleMinimize() { const panel = document.getElementById('auto-study-panel'); panel.classList.toggle('minimized'); } // ==================== 辅助函数 ==================== function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 随机延时(反检测核心) function randomSleep(min, max) { const ms = Math.floor(Math.random() * (max - min + 1)) + min; return sleep(ms); } // 统一安全点击函数(兼容所有浏览器) function safeClick(element) { if (!element) return; try { // 创建真实的人类鼠标点击事件 const realClick = new MouseEvent('click', { bubbles: true, cancelable: true, clientX: element.getBoundingClientRect().left + element.offsetWidth / 2, clientY: element.getBoundingClientRect().top + element.offsetHeight / 2 }); element.dispatchEvent(realClick); console.log("✅ 安全点击成功:", element); } catch (error) { log('warning', `⚠️ 点击时出现小问题,已自动处理`); console.error("点击错误:", error); // 兜底:直接调用click方法 try { element.click(); } catch (e) {} } } // ==================== 通用模式强制关闭弹窗 ==================== function forceCloseModal() { const realClick = new MouseEvent('click', { bubbles: true, cancelable: true }); // 传统关闭图标 const spanIcon = document.querySelector('.anticon-close'); if (spanIcon && spanIcon.offsetParent !== null) { log('info', "🎯 使用真实鼠标事件点击了关闭图标!"); spanIcon.dispatchEvent(realClick); return; } // 视频弹窗关闭按钮 const divContainer = document.querySelector('.video-modal-close'); if (divContainer && divContainer.offsetParent !== null) { log('info', "🎯 使用真实鼠标事件点击了关闭容器!"); divContainer.dispatchEvent(realClick); return; } // 遮罩层 const modalMask = document.querySelector('.video-modal-mask'); if (modalMask) { log('info', "🎯 使用真实鼠标事件点击了遮罩层!"); modalMask.dispatchEvent(realClick); return; } // 终极备选:键盘ESC log('info', "🎯 尝试模拟按 ESC 键!"); document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true})); document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true})); } // ==================== 通用模式视频监测 ==================== function waitForProcessToEnd(itemNode, index) { return new Promise((resolve) => { let maxTimeout = 0; let mainInterval = null; let hasVideoEnded = false; // 监听平台强制暂停事件,立即恢复 const videoEl = document.querySelector('video'); if (videoEl) { videoEl.muted = true; videoEl.addEventListener('pause', async function() { if (videoEl.currentTime < videoEl.duration - 1 && !isPaused && !stopRequested) { log('warning', "⚡ 平台尝试暂停视频!自动重新激活播放..."); await forcePlayVideo(); } }); } // 清理所有定时器的函数 function cleanupAndResolve() { if (mainInterval) clearInterval(mainInterval); resolve(); } // 主检查循环 mainInterval = setInterval(async () => { if (stopRequested) { cleanupAndResolve(); return; } if (isPaused) return; maxTimeout++; // 【条件1】系统进度达到100%(最可靠) const processNode = itemNode.querySelector('.process'); if (processNode && (processNode.innerText.includes('100%') || processNode.innerText.includes('已完成'))) { log('success', `✅ 第 ${index} 个视频系统进度已更新为完成!`); cleanupAndResolve(); return; } // 【条件2】视频底层确实播放完毕了 const currentVideo = document.querySelector('video'); if (currentVideo && !hasVideoEnded) { currentVideo.muted = true; // 【终极防暂停】每次检查都强制播放一次 if (currentVideo.paused && currentVideo.currentTime < currentVideo.duration) { log('warning', '⚡ 检测到视频暂停,强制恢复播放...'); await forcePlayVideo(); } currentVideo.play().catch(() => {}); // 额外强制播放一次 if (currentVideo.ended || (currentVideo.duration > 0 && currentVideo.duration - currentVideo.currentTime <= 1)) { hasVideoEnded = true; log('warning', `⚠️ 第 ${index} 个视频本体已播完,跳过平台服务器响应判定。`); cleanupAndResolve(); return; } } // 【条件3】45分钟超时保护(防504假死) if (maxTimeout > config.maxTimeout) { log('warning', `🚨 第 ${index} 个视频监测超时(可能服务器504),强行跳过。`); cleanupAndResolve(); } }, config.checkInterval); }); } // ==================== 【完全按照你要求+修复】智慧教育平台视频监测逻辑 ==================== function watchSmartEduVideoUntilEnd(index) { return new Promise((resolve) => { let notFoundCount = 0; const checkInterval = setInterval(() => { if (stopRequested) { clearInterval(checkInterval); resolve(); return; } if (isPaused) return; const videoEl = document.querySelector('video'); if (videoEl) { notFoundCount = 0; // 找到了就重置计数 // 强制静音,防止吵到你 videoEl.muted = true; // 【保留原V5防暂停增强】如果视频暂停了且没播完,强行让它继续播 if (videoEl.paused && videoEl.currentTime < videoEl.duration) { log('warning', '⚡ 检测到视频暂停,强制恢复播放...'); videoEl.play().catch(() => console.log("尝试自动播放被拦截")); } // 判断视频是否已经播到了最后(你指定留1秒容错) if (videoEl.ended || (videoEl.duration > 0 && videoEl.currentTime >= videoEl.duration - 1)) { clearInterval(checkInterval); resolve(); } } else { notFoundCount++; // 如果连续 15 秒 (5次 * 3秒) 都找不到 video 标签,可能加载卡死了(你指定) if (notFoundCount >= 5) { log('warning', `⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`); console.warn(`⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`); clearInterval(checkInterval); resolve(); // 放行,去点下一个 } } }, 3000); // 你指定的每隔3秒检查一次 }); } // 日志功能 function log(type, message) { try { const container = document.getElementById('log-container'); const item = document.createElement('div'); item.className = `log-item ${type}`; item.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; container.appendChild(item); container.scrollTop = container.scrollHeight; } catch (error) { console.log(message); } } function clearLog() { document.getElementById('log-container').innerHTML = ''; } // 更新UI状态 function updateUI() { try { const statusText = document.getElementById('status-text'); const progressText = document.getElementById('progress-text'); const progressFill = document.getElementById('progress-fill'); const expandBtn = document.getElementById('expand-btn'); const startBtn = document.getElementById('start-btn'); const pauseBtn = document.getElementById('pause-btn'); const stopBtn = document.getElementById('stop-btn'); // 更新状态 if (isRunning) { if (isPaused) { statusText.textContent = '已暂停'; statusText.className = 'status-value paused'; pauseBtn.textContent = '继续'; } else { statusText.textContent = '学习中'; statusText.className = 'status-value running'; pauseBtn.textContent = '暂停'; } } else { statusText.textContent = '已停止'; statusText.className = 'status-value stopped'; } // 更新进度 progressText.textContent = `${currentIndex} / ${totalVideos}`; const progress = totalVideos > 0 ? (currentIndex / totalVideos) * 100 : 0; progressFill.style.width = `${progress}%`; // 更新按钮状态 expandBtn.disabled = isRunning || isSmartEduPlatform; startBtn.disabled = isRunning; pauseBtn.disabled = !isRunning; stopBtn.disabled = !isRunning; } catch (error) { console.error("更新UI错误:", error); } } // ==================== 平台检测与初始化(修复:确保DOM完全加载) ==================== function init() { console.log("🔧 开始初始化脚本..."); // 检测是否为高等教育智慧教育平台 isSmartEduPlatform = window.location.href.includes('higher.smartedu.cn/course/'); console.log("📱 平台检测结果:", isSmartEduPlatform ? "智慧教育平台" : "通用平台"); // 等待页面完全加载后再创建面板 if (document.readyState === 'complete') { createPanel(); } else { window.addEventListener('load', createPanel); } } // 修复:将run-at改为document-end,并增加额外的加载等待 if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(init, 1000); // 延迟1秒初始化,确保页面完全渲染 } else { window.addEventListener('DOMContentLoaded', () => { setTimeout(init, 1000); }); } })();