// ==UserScript== // @name 安徽继续教育全自动刷课-UI优化终极版新 // @namespace http://tampermonkey.net/ // @version 1.2 // @description 修复视频页被误判为文本页(懒加载/iframe导致) | 作业页不提交、仅目录跳转 | 独立作业跳转开关 | 自动识别未浏览课程 | 多层目录顺序跳转 | 新增联系作者QQ按钮 // @author 1678955137 // @match *://main.ahjxjy.cn/study/html/content/studying/* // @grant GM_addStyle // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @run-at document-end // @license GPL 3 // ==/UserScript== (function() { 'use strict'; const unsafe = unsafeWindow || window; const state = { panel: null, isCollapsed: GM_getValue('panelCollapsed', false), currentSpeed: GM_getValue('playbackSpeed', 1.5), volumeLevel: GM_getValue('volumeLevel', 0), isAutoJumpEnabled: GM_getValue('isAutoJumpEnabled', true), isHomeworkAutoJumpEnabled: GM_getValue('isHomeworkAutoJumpEnabled', false), // 作业页自动跳转开关 logMessages: [], pos: GM_getValue('panelPos', {left: '20px', top: '20%'}), hasProcessedTextPage: false, homeworkProcessed: false, homeworkLogged: false, endJumpScheduled: false, pageType: 'unknown', lastUrl: location.href, textConfirmTimer: null, textJumpTimer: null }; // --- 样式定义 --- GM_addStyle(` :root { --primary: #4CAF50; --bg: rgba(255, 255, 255, 0.95); } #gemini-panel { position: fixed; z-index: 2147483647; width: 300px; background: var(--bg); backdrop-filter: blur(12px); border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); border: 1px solid rgba(0,0,0,0.05); transition: width 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28), height 0.3s, opacity 0.3s; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } #gemini-panel.collapsed { width: 50px; height: 50px; border-radius: 25px; cursor: pointer; overflow: hidden; } .panel-header { padding: 12px; background: var(--primary); color: white; border-top-left-radius: 12px; border-top-right-radius: 12px; cursor: move; display: flex; justify-content: space-between; align-items: center; font-weight: bold; } .panel-body { padding: 15px; display: flex; flex-direction: column; gap: 15px; } .collapsed .panel-body, .collapsed .panel-header span { display: none; } .collapsed .panel-header { height: 100%; padding: 0; justify-content: center; background: var(--primary); border-radius: 25px; } .control-row { display: flex; align-items: center; justify-content: space-between; font-size: 14px; color: #333; } .slider-ui { flex: 1; margin: 0 10px; height: 4px; accent-color: var(--primary); cursor: pointer; } .btn-action { width: 100%; padding: 10px; border: none; border-radius: 8px; background: var(--primary); color: white; font-weight: bold; cursor: pointer; transition: transform 0.1s; } .btn-action:active { transform: scale(0.96); } .btn-next { background: #2196F3 !important; } .btn-find-unstudied { background: #FF9800 !important; } .btn-qq { background: #9b59b6 !important; } .log-area { height: 120px; background: #1e1e1e; color: #adff2f; padding: 8px; font-size: 12px; border-radius: 6px; overflow-y: auto; white-space: pre-wrap; box-shadow: inset 0 2px 5px rgba(0,0,0,0.2); line-height: 1.4; } .switch { position: relative; display: inline-block; width: 40px; height: 20px; } .switch input { opacity: 0; width: 0; height: 0; } .slider-round { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 20px; } .slider-round:before { position: absolute; content: ""; height: 16px; width: 16px; left: 2px; bottom: 2px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .slider-round { background-color: var(--primary); } input:checked + .slider-round:before { transform: translateX(20px); } `); // --- 日志输出 --- function log(msg) { const time = new Date().toLocaleTimeString([], { hour12: false }); state.logMessages.push(`[${time}] ${msg}`); if (state.logMessages.length > 30) state.logMessages.shift(); const el = document.getElementById('logArea'); if (el) { el.textContent = state.logMessages.join('\n'); el.scrollTop = el.scrollHeight; } } // --- 自动展开所有折叠目录 --- function expandAllFolder() { const toggleBtns = document.querySelectorAll('.toggle_lesson'); toggleBtns.forEach(btn => { const childUl = btn.nextElementSibling?.nextElementSibling; if (childUl) { const isHide = childUl.classList.contains('hidden') || getComputedStyle(childUl).display === 'none'; if (isHide) { btn.click(); log("自动展开折叠任务菜单"); } } }); } // --- 查找视频元素(主文档 + 同源 iframe)--- function getVideoEl() { try { const v = document.querySelector('video, .jw-video'); if (v) return v; } catch (e) {} try { const iframes = document.querySelectorAll('iframe'); for (const f of iframes) { try { const doc = f.contentDocument || (f.contentWindow && f.contentWindow.document); if (!doc) continue; const v2 = doc.querySelector('video, .jw-video'); if (v2) return v2; } catch (e2) {} } } catch (e) {} return null; } // --- 判断页面是否存在播放器(video元素 / 播放器容器 / 跨域播放器iframe)--- function hasVideoPlayer() { if (getVideoEl()) return true; try { if (document.querySelector('.jwplayer, .jw-media, .video-js, .ckplayer, .prism-player, #player, .video-player')) return true; } catch (e) {} try { const iframes = document.querySelectorAll('iframe'); for (const f of iframes) { const src = (f.src || '').toLowerCase(); if (/player|video|\.m3u8|media/.test(src)) return true; } } catch (e) {} return false; } // --- 页面类型识别(仅在类型变化时输出日志,避免刷屏)--- function detectPageType() { const hasVideo = hasVideoPlayer(); const isHomeworkPage = document.title.includes('作业') || document.querySelector('a[title*="作业"]') || document.querySelector('i.icon-yuan'); const hasNextBtn = !!document.querySelector('#focus > div.preNext.next') || Array.from(document.querySelectorAll('a, button')).some(el => el.textContent.includes('下一单元') || el.textContent.includes('下一节')); let newType; if (hasVideo) { newType = 'video'; } else if (isHomeworkPage) { newType = 'homework'; } else if (hasNextBtn) { newType = 'text'; } else { newType = 'unknown'; } if (newType !== state.pageType) { const prev = state.pageType; state.pageType = newType; const typeText = { video: '视频课程页', text: '文本/教案页', homework: '作业页面(仅目录跳转)', unknown: '未知页面' }; log(`页面类型识别:${typeText[newType]}`); if (newType === 'video') { // 之前误判为文本/未知,现在发现视频 → 取消待执行的文本跳转 if (prev === 'text' || prev === 'unknown') { cancelPendingJump(); log("✅ 已取消文本页跳转,按视频页处理"); } updateVideo(); } } return state.pageType; } // --- 判断章节是否已浏览/已学习 --- function isSectionStudied(link) { const parentLi = link.closest('li'); if (!parentLi) return false; if (parentLi.classList.contains('current')) return true; const hasCompleteIcon = parentLi.querySelector('i.iconfont.icon-iconfontyuan[title*="已完成"]') !== null; const hasCompleteText = (link.title || '').includes('已完成') || link.textContent.includes('已完成'); return hasCompleteIcon || hasCompleteText; } // --- 查找并点击第一个未浏览的章节(含作业链接)--- function findAndClickUnstudiedSection() { expandAllFolder(); const linkNodes = document.querySelectorAll( 'ul.list-order li a[href*="/study/html/content/studying/"]:not([href*="javascript:void(0)"])' ); const allLinks = Array.from(linkNodes); if (allLinks.length === 0) { log("❌ 未扫描到任何课程链接"); return false; } const unstudiedLink = allLinks.find(link => !isSectionStudied(link)); if (unstudiedLink) { log(`🔍 找到未浏览内容:${unstudiedLink.title || unstudiedLink.textContent.trim()}`); unstudiedLink.click(); return true; } else { log("✅ 目录内所有内容均已浏览完成"); return false; } } // --- 核心:目录顺序跳转(纯链接跳转,无任何提交操作)--- function jumpFromDirectory() { expandAllFolder(); const linkNodes = document.querySelectorAll( 'ul.list-order li a[href*="/study/html/content/studying/"]:not([href*="javascript:void(0)"])' ); const allLinks = Array.from(linkNodes); if (allLinks.length === 0) { log("❌ 未扫描到任何课程链接"); return false; } // 双重定位当前页面 let currIndex = -1; const currentLi = document.querySelector('li.current'); if (currentLi) { const currA = currentLi.querySelector('a[href*="/study/html/content/studying/"]'); if (currA) { currIndex = allLinks.findIndex(link => link.href === currA.href); } } if (currIndex === -1) { const nowUrl = location.href; currIndex = allLinks.findIndex(link => link.href === nowUrl); } // 定位失败 / 当前已是最后一节 → 自动找未浏览内容 if (currIndex === -1 || currIndex >= allLinks.length - 1) { log("当前位置异常/已到末尾,自动查找未浏览内容"); return findAndClickUnstudiedSection(); } const nextLink = allLinks[currIndex + 1]; // 下一节已浏览,继续查找 if (isSectionStudied(nextLink)) { log(`⚠️ 下一项 ${nextLink.title} 已浏览,继续查找未浏览内容`); return findAndClickUnstudiedSection(); } log(`➡ 顺序跳转至下一项:${nextLink.title || nextLink.textContent.trim()}`); nextLink.click(); return true; } // --- 统一跳转入口 --- function jumpNextManually() { expandAllFolder(); const nextBtn = document.querySelector('#focus > div.preNext.next') || Array.from(document.querySelectorAll('a, button')).find(el => el.textContent.includes('下一单元') || el.textContent.includes('下一节') ); if (nextBtn) { log("点击页面内置【下一节】按钮"); nextBtn.click(); return; } log("页面无内置跳转按钮,使用目录顺序跳转"); jumpFromDirectory(); } // --- 取消待执行的文本页跳转(复查发现视频时调用)--- function cancelPendingJump() { if (state.textConfirmTimer) { clearTimeout(state.textConfirmTimer); state.textConfirmTimer = null; } if (state.textJumpTimer) { clearTimeout(state.textJumpTimer); state.textJumpTimer = null; } state.hasProcessedTextPage = false; } // --- 构建控制面板 --- function createPanel() { const panel = document.createElement('div'); panel.id = 'gemini-panel'; if (state.isCollapsed) panel.classList.add('collapsed'); panel.style.left = state.pos.left; panel.style.top = state.pos.top; panel.innerHTML = `
刷课助手 V8.9
${state.isCollapsed ? '🚀' : '➖'}
倍速: ${state.currentSpeed}x
音量
全局自动跳转
作业页自动跳转
正在启动...
作业页仅目录跳转 | 不提交作业
`; document.body.appendChild(panel); state.panel = panel; bindEvents(); } // --- 绑定面板交互事件 --- function bindEvents() { document.getElementById('closeBtn').onclick = (e) => { e.stopPropagation(); toggleCollapse(); }; state.panel.onclick = () => { if(state.isCollapsed) toggleCollapse(); }; document.getElementById('speedRange').oninput = (e) => { const val = parseFloat(e.target.value); state.currentSpeed = val; document.getElementById('speedVal').innerText = val; updateVideo(); GM_setValue('playbackSpeed', val); }; document.getElementById('volumeRange').oninput = (e) => { state.volumeLevel = parseFloat(e.target.value); updateVideo(); GM_setValue('volumeLevel', state.volumeLevel); }; document.getElementById('jumpToggle').onchange = (e) => { state.isAutoJumpEnabled = e.target.checked; GM_setValue('isAutoJumpEnabled', state.isAutoJumpEnabled); log(state.isAutoJumpEnabled ? "✅ 开启全局自动跳转" : "❌ 关闭全局自动跳转"); }; document.getElementById('homeworkJumpToggle').onchange = (e) => { state.isHomeworkAutoJumpEnabled = e.target.checked; GM_setValue('isHomeworkAutoJumpEnabled', state.isHomeworkAutoJumpEnabled); log(state.isHomeworkAutoJumpEnabled ? "✅ 开启作业页自动跳转" : "❌ 关闭作业页自动跳转"); }; document.getElementById('playBtn').onclick = () => { const v = getVideoEl(); if(v) { v.play().catch(()=>{}); log("尝试强制播放视频"); } else { log("当前页面无视频播放器"); } }; document.getElementById('manualNextBtn').onclick = jumpNextManually; document.getElementById('findUnstudiedBtn').onclick = () => { log("手动查找目录中未浏览内容..."); findAndClickUnstudiedSection(); }; // 新增:联系作者QQ按钮事件 document.getElementById('contactQQBtn').onclick = () => { const qqUrl = "https://qm.qq.com/q/fpVawk4WgS"; window.open(qqUrl, "_blank"); log("💬 打开QQ加好友"); }; makeDraggable(state.panel); } // --- 面板折叠/展开 --- function toggleCollapse() { state.isCollapsed = !state.isCollapsed; GM_setValue('panelCollapsed', state.isCollapsed); state.panel.classList.toggle('collapsed'); document.getElementById('closeBtn').innerText = state.isCollapsed ? '🚀' : '➖'; if (!state.isCollapsed && parseInt(state.panel.style.left) < 0) { state.panel.style.left = '10px'; } } // --- 面板拖动 + 边缘吸附 --- function makeDraggable(el) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; const header = document.getElementById('panelHeader'); header.onmousedown = (e) => { e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = () => { document.onmouseup = null; document.onmousemove = null; const distLeft = el.offsetLeft; const distRight = window.innerWidth - (el.offsetLeft + el.offsetWidth); if (distLeft < 50) el.style.left = '0px'; if (distRight < 50) el.style.left = (window.innerWidth - el.offsetWidth) + 'px'; GM_setValue('panelPos', {left: el.style.left, top: el.style.top}); }; document.onmousemove = (e) => { e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; el.style.top = (el.offsetTop - pos2) + "px"; el.style.left = (el.offsetLeft - pos1) + "px"; }; }; } // --- 视频倍速、音量、播放维护(主文档 + iframe)--- function updateVideo() { const video = getVideoEl(); if (video) { try { video.playbackRate = state.currentSpeed; video.volume = state.volumeLevel; if (video.paused && !video.ended) { video.play().catch(()=>{}); } } catch (e) {} } } // --- 视频播放完毕自动跳转(防重复触发)--- function checkEnd() { if (state.pageType !== 'video' || !state.isAutoJumpEnabled) return; if (state.endJumpScheduled) return; const video = getVideoEl(); if (video && video.ended) { state.endJumpScheduled = true; log("📹 视频播放完成,3秒后跳转"); setTimeout(() => { state.endJumpScheduled = false; jumpNextManually(); }, 3000); } } // --- 文本页延时跳转(先复查再安排,防止视频未加载完被误判)--- function scheduleTextJump() { if (state.hasProcessedTextPage) return; state.hasProcessedTextPage = true; const delay = Math.floor(Math.random() * 10000) + 10000; const delaySec = (delay / 1000).toFixed(1); log(`📄 文本页面,${delaySec}秒后跳转目录下一项`); state.textJumpTimer = setTimeout(() => jumpNextManually(), delay); } function checkTextPageAutoJump() { if (state.pageType !== 'text' || !state.isAutoJumpEnabled) return; if (state.hasProcessedTextPage) return; // 已安排跳转 if (state.textConfirmTimer) return; // 正在复查 // 首次识别为文本页:延迟 3.5 秒复查一次,确认没有视频再安排跳转 state.textConfirmTimer = setTimeout(() => { state.textConfirmTimer = null; if (hasVideoPlayer()) { log("📹 复查发现视频播放器,本页按视频页处理"); state.pageType = 'video'; cancelPendingJump(); updateVideo(); return; } scheduleTextJump(); }, 3500); } // --- 作业页面跳转逻辑(核心修改:不提交作业,仅目录跳转)--- function checkHomeworkAutoJump() { // 全局自动跳转关闭 / 作业自动跳转关闭 → 不执行 if (state.pageType !== 'homework' || !state.isAutoJumpEnabled || !state.isHomeworkAutoJumpEnabled) return; if (state.homeworkProcessed) return; state.homeworkProcessed = true; // 作业页随机延时 10~20秒,模拟浏览,**无任何提交操作** const delay = Math.floor(Math.random() * 10000) + 10000; const delaySec = (delay / 1000).toFixed(1); log(`📝 作业页面,${delaySec}秒后跳转目录下一项(不提交作业)`); setTimeout(() => jumpNextManually(), delay); } // --- 作业页统一处理(只提示一次,避免刷屏)--- function handleHomeworkPage() { if (state.pageType !== 'homework') return; if (state.isHomeworkAutoJumpEnabled) { checkHomeworkAutoJump(); } else if (!state.homeworkLogged) { state.homeworkLogged = true; log("📝 作业页面:已关闭作业自动跳转,停留在当前页面"); } } // --- 监听视频元素注入(懒加载/异步播放器场景)--- function watchForVideoInjection() { if (!window.MutationObserver) return; try { const mo = new MutationObserver(() => { if (hasVideoPlayer() && (state.pageType === 'text' || state.pageType === 'unknown')) { log("📹 检测到播放器注入,立即切换为视频页并取消文本跳转"); cancelPendingJump(); state.pageType = 'video'; updateVideo(); } }); mo.observe(document.body, { childList: true, subtree: true }); } catch (e) {} } // --- 全局定时轮询主逻辑 --- function mainLoop() { // 页面切换重置所有标记 if (location.href !== state.lastUrl) { state.lastUrl = location.href; state.hasProcessedTextPage = false; state.homeworkProcessed = false; state.homeworkLogged = false; state.endJumpScheduled = false; cancelPendingJump(); expandAllFolder(); detectPageType(); } else { // 周期性复查页面类型:修复视频懒加载/iframe 导致的误判 detectPageType(); } updateVideo(); checkEnd(); checkTextPageAutoJump(); handleHomeworkPage(); } // --- 初始化 --- function init() { createPanel(); expandAllFolder(); detectPageType(); log("✅ 脚本 V8.9 初始化完成 | 作业页仅目录跳转,不提交作业"); setInterval(mainLoop, 3000); // 自动关闭弹窗/挂机验证 setInterval(() => { const dialogBtn = document.querySelector('.layui-layer-btn0, .confirm'); if (dialogBtn) { log("🔔 自动关闭验证弹窗"); dialogBtn.click(); } }, 5000); watchForVideoInjection(); } window.addEventListener('load', init); })();