// ==UserScript== // @name 人工智能2026观看脚本V1.5 -仅供学习 // @namespace http://tampermonkey.net/ // @version 1.5 // @description 优化Bug // @author 诺亚 // @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 { const blockEvents = ['visibilitychange', 'webkitvisibilitychange', 'blur', 'pagehide', 'focusout']; blockEvents.forEach(eventName => { window.addEventListener(eventName, e => e.stopImmediatePropagation(), true); document.addEventListener(eventName, e => e.stopImmediatePropagation(), true); }); Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'hidden', { get: () => false, configurable: true }); Object.defineProperty(window, 'hasFocus', { value: () => true, configurable: true }); console.log("🚀 终极挂课脚本 V1.5 已启动!切屏/最小化检测已完全拦截"); } 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; flex-wrap: wrap; } .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.feedback-btn { background: linear-gradient(135deg, #ec4899 0%, #db2777 100%); color: #ffffff; flex: 1 1 100%; } .action-btn.feedback-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(236, 72, 153, 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" } ]; 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 ? '诺亚NOYA' : ''}
运行状态 已停止
播放进度 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.getElementById('feedback-btn').addEventListener('click', () => { GM_openInTab('tencent://message/?uin=723167066&Site=&Menu=yes', { active: true }); log('info', '🔗 正在打开QQ反馈窗口'); }); 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 { 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'); if (leftItem && icon && !icon.classList.contains('is-expanded')) { log('info', `📖 展开大章节 ${i + 1}/${mainChapters.length}`); safeClick(leftItem); await randomSleep(400, 800); } } 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); 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) { await startSmartEduExclusiveAutoStudy(); } else { 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("🚀 启动智慧教育平台专属自动学习"); 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]; if (isPaused) { log('info', '⏸️ 已暂停,等待继续...'); await new Promise(resolve => { pauseResolve = resolve; }); if (stopRequested) break; log('info', '▶️ 继续学习'); } log('info', `▶️ 正在打开第 ${currentIndex} 个视频...`); console.log(`▶️ 正在打开第 ${i + 1} 个视频...`); currentItem.scrollIntoView({ behavior: 'smooth', block: 'center' }); await sleep(1500); safeClick(currentItem); log('info', "⏳ 等待弹窗和视频加载..."); console.log("⏳ 等待 4 秒钟让弹窗和播放器完全渲染..."); await sleep(4000); log('info', '🎬 正在激活视频播放...'); await forcePlayVideo(); log('info', "⏳ 正在等待视频播放完毕,请挂机..."); console.log("⏳ 正在等待视频播放完毕,请挂机..."); await watchSmartEduVideoUntilEnd(i + 1); log('info', `✅ 第 ${currentIndex} 个视频结束,尝试关闭弹窗...`); console.log(`✅ 第 ${i + 1} 个视频结束,尝试关闭弹窗...`); 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 { log('info', "🎯 未找到任何关闭按钮,尝试按 ESC 关闭..."); console.log("未找到任何关闭按钮,尝试按 ESC 关闭..."); document.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape', 'bubbles': true})); document.dispatchEvent(new KeyboardEvent('keyup', {'key': 'Escape', 'bubbles': true})); } await sleep(3000); } if (stopRequested) { log('info', '⏹️ 用户手动停止了学习'); } else { log('success', '🎉 所有界面的视频已全部轮询播放完毕!'); console.log("🎉 所有界面的视频已全部轮询播放完毕!"); } } 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', '🎉 太棒了,所有任务脚本运行结束!'); } } 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); await sleep(800); if (video && !video.paused) { log('success', '✅ 播放成功激活!'); return; } } } 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); 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; } 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++; const processNode = itemNode.querySelector('.process'); if (processNode && (processNode.innerText.includes('100%') || processNode.innerText.includes('已完成'))) { log('success', `✅ 第 ${index} 个视频系统进度已更新为完成!`); cleanupAndResolve(); return; } 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; } } 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; if (videoEl.paused && videoEl.currentTime < videoEl.duration) { log('warning', '⚡ 检测到视频暂停,强制恢复播放...'); videoEl.play().catch(() => console.log("尝试自动播放被拦截")); } if (videoEl.ended || (videoEl.duration > 0 && videoEl.currentTime >= videoEl.duration - 1)) { clearInterval(checkInterval); resolve(); } } else { notFoundCount++; if (notFoundCount >= 5) { log('warning', `⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`); console.warn(`⚠️ 第 ${index} 个视频似乎无法加载视频流,准备跳过...`); clearInterval(checkInterval); resolve(); } } }, 3000); }); } 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 = ''; } 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); } } 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); } } if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(init, 1000); } else { window.addEventListener('DOMContentLoaded', () => { setTimeout(init, 1000); }); } })();