// ==UserScript== // @name 嘉职院沃创刷课助手 // @namespace https://github.com/woczx-auto-study // @version 2.2.0 // @description 嘉职院沃创刷课助手 - 支持跨单元自动跳转 // @match *://jxvtcedu.woczx.com/* // @match *://*.woczx.com/* // @include *woczx.com/* // @include *busionline.com/* // @grant unsafeWindow // @grant GM_getValue // @grant GM_setValue // @run-at document-end // @license MIT // ==/UserScript== (function(){ 'use strict'; // ======================== 配置 ======================== const CONFIG={ speed:GM_getValue('woczx_speed',4), smartSkip:GM_getValue('woczx_smartSkip',true), skipSpeed:GM_getValue('woczx_skipSpeed',2), skipRatio:GM_getValue('woczx_skipRatio',0.9), autoMute:GM_getValue('woczx_autoMute',true), autoNext:GM_getValue('woczx_autoNext',true), nextDelay:GM_getValue('woczx_nextDelay',2), showPanel:GM_getValue('woczx_showPanel',true), debug:GM_getValue('woczx_debug',true), endThreshold:GM_getValue('woczx_endThreshold',5), speedEnforceInterval:GM_getValue('woczx_speedEnforce',500), studyId:GM_getValue('woczx_studyId',''), }; const STATE={ running:false,currentCourse:'',currentChapter:'',videoCount:0,popupsClosed:0, startTime:null,playerInstance:null,videoEnded:false,nextAttemptCount:0,maxNextAttempts:15, }; const log=(...a)=>{if(CONFIG.debug)console.log('[嘉职院刷课]',...a);}; const $=(s,r)=>(r||document).querySelector(s); const $$=(s,r)=>[...(r||document).querySelectorAll(s)]; function isVideoPage(){return location.hash.includes('/unit/video');} function isGradePage(){return location.hash.includes('/student/grade/detail');} // ======================== 面板 ======================== function createPanel(){ const p=document.createElement('div'); p.id='woczx-panel'; p.innerHTML='' +'
嘉职院沃创刷课助手 v2.2
' +'
' +'
支持跨单元自动跳转
' +'
防检测
' +'
'+Math.min(CONFIG.speed,8)+'x
' +'
' +'
' +'
' +'
课程: --
章节: --
弹窗: 0
运行: 00:00
倍速: 1x
状态: 待启动
' +'' +'' +'' +'' +'' +'' +'
4x
'; document.body.appendChild(p); bindEvents(p); return p; } function bindEvents(p){ p.querySelector('.wz-toggle').addEventListener('click',function(){ const b=p.querySelector('.wz-body'); b.classList.toggle('collapsed'); this.textContent=b.classList.contains('collapsed')?'+':'−'; }); p.querySelector('#wz-speed').addEventListener('input',function(){ const v=parseFloat(this.value); p.querySelector('#wz-speed-val').textContent=v+'x'; CONFIG.speed=v; GM_setValue('woczx_speed',v); applySpeed(v); }); p.querySelector('#wz-smartskip').addEventListener('change',function(){CONFIG.smartSkip=this.checked;GM_setValue('woczx_smartSkip',this.checked);}); p.querySelector('#wz-skip-speed').addEventListener('change',function(){CONFIG.skipSpeed=parseInt(this.value);GM_setValue('woczx_skipSpeed',parseInt(this.value));}); p.querySelector('#wz-mute').addEventListener('change',function(){CONFIG.autoMute=this.checked;GM_setValue('woczx_autoMute',this.checked);applyMute(this.checked);}); p.querySelector('#wz-autonext').addEventListener('change',function(){CONFIG.autoNext=this.checked;GM_setValue('woczx_autoNext',this.checked);}); p.querySelector('#wz-btn-start').addEventListener('click',startAutoStudy); p.querySelector('#wz-btn-pause').addEventListener('click',pauseAutoStudy); p.querySelector('#wz-btn-stop').addEventListener('click',stopAutoStudy); p.querySelector('#wz-btn-next').addEventListener('click',()=>{if(isVideoPage())goToNextChapter();else if(isGradePage())startNextUnit();}); p.querySelector('#wz-btn-grade').addEventListener('click',goToGradePage); p.querySelector('#wz-btn-debug').addEventListener('click',debugPage); makeDraggable(p); } function makeDraggable(p){ const h=p.querySelector('.wz-header'); let ox,oy,d=false; h.addEventListener('mousedown',e=>{if(e.target.classList.contains('wz-toggle'))return;d=true;ox=e.clientX-p.offsetLeft;oy=e.clientY-p.offsetTop;p.style.transition='none';}); document.addEventListener('mousemove',e=>{if(!d)return;p.style.left=(e.clientX-ox)+'px';p.style.top=(e.clientY-oy)+'px';p.style.right='auto';}); document.addEventListener('mouseup',()=>{d=false;p.style.transition='';}); } function updateStatus(u){for(const[k,v]of Object.entries(u)){const e=document.getElementById(k);if(e)e.textContent=v;}} // ======================== 播放器 ======================== function findVideo(){ const vs=$$('video');for(const v of vs){if(v.readyState>=1||v.src||v.querySelector('source'))return v;} if(vs.length>0)return vs[0]; for(const f of $$('iframe')){try{const d=f.contentDocument||f.contentWindow?.document;if(d){const v=d.querySelector('video');if(v)return v;}}catch(e){}} for(const c of $$('[class*="player"],[id*="player"],[class*="video-wrap"],[class*="videoBox"]')){const v=c.querySelector('video');if(v)return v;} return null; } function getVE(p){if(!p)return null;if(p.tagName==='VIDEO')return p;if(p.querySelector){const v=p.querySelector('video');if(v)return v;}return p._video||p.video||p;} function applySpeed(s){ const ve=getVE(STATE.playerInstance)||findVideo();if(!ve)return; const sp=s||(CONFIG.smartSkip&&STATE._skipDone?CONFIG.skipSpeed:CONFIG.speed); try{if(ve.playbackRate!==undefined)ve.playbackRate=sp;if(ve.setRate)ve.setRate(sp);}catch(e){} const fl=document.getElementById('wz-float-speed'); if(fl){fl.textContent=sp+'x';fl.style.display='block';clearTimeout(fl._t);fl._t=setTimeout(()=>{fl.style.display='none';},1500);} updateStatus({'wz-current-speed':sp+'x'}); } function applyMute(m){const ve=getVE(STATE.playerInstance)||findVideo();if(ve){try{ve.muted=m;}catch(e){}}} function hookVideo(player){ const ve=getVE(player);if(!ve||ve._wzHooked)return;ve._wzHooked=true;STATE.playerInstance=ve;STATE._skipDone=false;log('挂钩视频'); ve.addEventListener('play',()=>{ STATE.videoEnded=false;updateStatus({'wz-run-status':'播放中'});applySpeed();applyMute(CONFIG.autoMute); if(CONFIG.smartSkip&&!STATE._skipDone&&ve.duration&&isFinite(ve.duration)&&ve.duration>10){ const to=ve.duration*CONFIG.skipRatio;log('跳尾: '+to.toFixed(1)+'s/'+ve.duration.toFixed(1)+'s'); ve.currentTime=to;STATE._skipDone=true;setTimeout(()=>applySpeed(CONFIG.skipSpeed),200); } }); ve.addEventListener('pause',()=>{if(!STATE.videoEnded&&STATE.running&&ve.paused)setTimeout(()=>{if(STATE.running&&ve.paused&&!STATE.videoEnded){ve.muted=true;ve.play().catch(()=>{});}},300);}); ve.addEventListener('ended',()=>{ if(STATE._navigating)return;log('ended');STATE.videoEnded=true;STATE.videoCount++;STATE._skipDone=false; updateStatus({'wz-run-status':'已完成'});if(CONFIG.autoNext)setTimeout(()=>{if(STATE.running)goToNextChapter();},CONFIG.nextDelay*1000); }); ve.addEventListener('loadeddata',()=>{if(STATE.running){STATE.videoEnded=false;STATE.nextAttemptCount=0;STATE._skipDone=false;applyMute(CONFIG.autoMute);}}); ve.addEventListener('ratechange',()=>{if(STATE.running&&!STATE.videoEnded){const t=(CONFIG.smartSkip&&STATE._skipDone)?CONFIG.skipSpeed:CONFIG.speed;if(ve.playbackRate!==t)requestAnimationFrame(()=>applySpeed(t));}}); } // ======================== 弹窗 ======================== function closePopups(){ if(STATE._navigating)return 0;let c=0; for(const m of $$('.ant-modal-wrap,.ant-modal-root')){ if(!m.offsetParent&&m.style.display==='none')continue;const t=m.textContent||''; if(t.includes('继续')||t.includes('确定')||t.includes('确认')||t.includes('认真度')||t.includes('还在吗')){ const b=m.querySelector('.ant-btn-primary')||m.querySelector('.ant-modal-confirm-btns .ant-btn:last-child')||m.querySelector('button:last-of-type'); if(b){b.click();c++;continue;} } const x=m.querySelector('.ant-modal-close');if(x){x.click();c++;} } for(const s of ['.ant-modal','.ant-popover','.ant-popconfirm','.ant-drawer','[class*=dialog]','[class*=modal]','[class*=popup]','[class*=toast]','[role=dialog]','[class*=alert]','[class*=mask]']){ for(const d of $$(s)){ if(!d.offsetParent&&d.style.display==='none')continue;const t=(d.textContent||'').trim();if(!t||t.length<2)continue; if(['继续学习','确定','知道了','认真度检测','学习确认','确认继续','还在吗','是否继续'].some(p=>t.includes(p))){ const btns=[...d.querySelectorAll('button,.ant-btn')].filter(b=>b.offsetParent);if(btns.length>=1){btns[btns.length-1].click();c++;} } } } if(c>0){STATE.popupsClosed+=c;updateStatus({'wz-popup-count':String(STATE.popupsClosed)});}return c; } // ======================== 下一节 ======================== function goToNextChapter(){ if(STATE._navigating){log('跳转中');return false;} STATE.nextAttemptCount++;log('===== 跳转('+STATE.nextAttemptCount+') ====='); // 超过最大尝试次数 → DOM找不到下一节,走兜底:回学习详情页重新定位 if(STATE.nextAttemptCount>STATE.maxNextAttempts){ log('已达最大尝试次数 → 回学习详情页重新定位'); STATE._navigating=false;STATE.nextAttemptCount=0; updateStatus({'wz-run-status':'回详情页定位'}); return goToGradePage(); } STATE._navigating=true; // 先尝试所有策略找下一节 if(tryFindNextChapter()){ STATE.nextAttemptCount=0; return true; } // 没找到 → 解锁并等待重试,不立刻放弃 log('暂未找到下一节入口,'+CONFIG.nextDelay+'秒后重试('+STATE.nextAttemptCount+'/'+STATE.maxNextAttempts+')'); STATE._navigating=false; setTimeout(()=>{if(STATE.running&&!STATE._navigating)goToNextChapter();},CONFIG.nextDelay*1000); return false; } function tryFindNextChapter(){ // 策略0: 章节列表 for(let i=0;i<$$('[class*=list] [class*=item]').length;i++){ const t=($$('[class*=list] [class*=item]')[i].textContent||'').trim(); if(t.includes('已学')&&!t.includes('已完成')&&i+1<$$('[class*=list] [class*=item]').length){ log('策略0: 章节'+i+'→'+(i+1));clickEl($$('[class*=list] [class*=item]')[i+1]);resetChapter();return true; } } // 策略0b: Endmodal const emb=$('[class*=Endmodal] [class*=next_button],[class*=endmodal] [class*=next_button],[class*=Endmodal] .ant-btn-primary'); if(emb&&emb.offsetParent){log('Endmodal');clickEl(emb);resetChapter();return true;} // 策略1: 文字 for(const t of['下一单元','下一节','下一章','下一课','下一个','下节']){ for(const e of $$('button,a,span,div,li,.ant-btn,[class*=btn]')){ if((e.textContent||'').trim()===t&&e.offsetParent&&!e.closest('#woczx-panel')){log('文字:',t);clickEl(e);resetChapter();return true;} } } // 侧边栏 for(const s of['.ant-menu-item','[class*=chapter-item]','[class*=lesson-item]','[class*=list-item]','[class*=catalog] li']){ let found=false; for(let i=0;i<$$(s).length;i++){ const cl=$$(s)[i].className||'';const act=cl.includes('active')||cl.includes('selected')||cl.includes('current')||cl.includes('playing'); if(found&&$$(s)[i].offsetParent){log('侧边栏下一个');clickEl($$(s)[i].querySelector('a')||$$(s)[i]);resetChapter();return true;} if(act)found=true; } } return false; } function resetChapter(){ STATE.videoEnded=false;STATE.playerInstance=null;STATE.nextAttemptCount=0;STATE._skipDone=false;log('跳转成功'); // 保持 _navigating 锁定 5 秒,防止 ended/checkNearEnd 的二次触发 setTimeout(()=>{STATE._navigating=false;},5000); } function clickEl(el){ el.click();el.dispatchEvent(new MouseEvent('click',{bubbles:true,cancelable:true})); el.dispatchEvent(new MouseEvent('mousedown',{bubbles:true}));el.dispatchEvent(new MouseEvent('mouseup',{bubbles:true})); } // ======================== ★ 跨单元跳转 ★ ======================== function goToGradePage(){ STATE._navigating=true;log('===== 跳转学习详情页 ====='); const m=location.hash.match(/study_id=(\d+)/); const sid=m?m[1]:CONFIG.studyId; if(sid){CONFIG.studyId=sid;GM_setValue('woczx_studyId',sid);} if(sid){ updateStatus({'wz-run-status':'跳转学习详情...'}); location.hash='#/app/student/grade/detail/'+sid; // 由 watchRoute() 统一触发 startNextUnit(),避免与 goToGradePage 的定时器冲突造成循环跳转 setTimeout(()=>{STATE._navigating=false;},5000); }else{log('无study_id');STATE._navigating=false;} return true; } function startNextUnit(){ if(!isGradePage())return goToGradePage(); log('===== 查找未学习单元 ====='); const units=$$('[class*="detail_list_video--"]'); // 策略1(优先): 找"继续学习"——先做完未完成的单元 for(const u of units){ const t=(u.textContent||'').trim(); if(t.includes('继续学习')&&u.offsetParent){ log('进行中单元:',t.substring(0,60)); const btn=[...u.querySelectorAll('*')].find(el=>(el.textContent||'').trim()==='继续学习'&&el.children.length===0); if(btn)clickEl(btn);else clickEl(u); updateStatus({'wz-run-status':'继续学习'}); startVideoModeForce(); return true; } } // 策略2: 找"未开始" + "开始学习" for(const u of units){ const t=(u.textContent||'').trim(); if(t.includes('未开始')&&t.includes('开始学习')&&u.offsetParent){ log('未学习单元:',t.substring(0,60)); const btn=[...u.querySelectorAll('*')].find(el=>(el.textContent||'').trim()==='开始学习'&&el.children.length===0); if(btn){clickEl(btn);}else{clickEl(u);} updateStatus({'wz-run-status':'进入新单元'}); startVideoModeForce(); return true; } } // 策略3: 当前页无可用单元 → 尝试翻下一页 if(tryNextPage()){ updateStatus({'wz-run-status':'翻页中...'}); return false; } log('全部完成');updateStatus({'wz-run-status':'✅ 全部完成!'});stopAutoStudy();return false; } // ======================== ★ 翻页 ★ ======================== function tryNextPage(){ // 查找 Ant Design 分页器 const pagers=['.ant-pagination','[class*=pagination]','.ant-list-pagination','[class*=Pagination]']; for(const s of pagers){ const pager=$(s); if(!pager||!pager.offsetParent)continue; // 找"下一页"按钮(未禁用) const nextBtn=pager.querySelector('.ant-pagination-next:not(.ant-pagination-disabled)') ||pager.querySelector('[class*=next]:not([class*=disabled]):not([disabled])') ||pager.querySelector('[title="下一页"]:not([disabled])') ||[...pager.querySelectorAll('li')].find(li=>(li.textContent||'').includes('下一页')&&!li.className.includes('disabled')) ||[...pager.querySelectorAll('button,a,li')].find(el=>{ const t=(el.textContent||'').trim(); return (t==='下一页'||t==='>'||t==='›'||t==='»')&&el.offsetParent&&!el.disabled&&!el.className.includes('disabled'); }); if(nextBtn){ log('翻页 → 下一页'); clickEl(nextBtn); setTimeout(()=>{if(STATE.running)startNextUnit();},2000); return true; } // 检查是否还有未激活的页码按钮 const pageItems=[...pager.querySelectorAll('.ant-pagination-item:not(.ant-pagination-item-active):not(.ant-pagination-disabled)')]; if(pageItems.length>0){ log('翻页 → 第'+pageItems[0].textContent+'页'); clickEl(pageItems[0]); setTimeout(()=>{if(STATE.running)startNextUnit();},2000); return true; } // 通用:找任何未禁用、非当前页的页码元素 for(const el of [...pager.querySelectorAll('li,a,button')]){ const cls=el.className||''; const txt=(el.textContent||'').trim(); if(/^\d+$/.test(txt)&&!cls.includes('active')&&!cls.includes('current')&&!cls.includes('disabled')&&el.offsetParent&&!el.disabled){ log('翻页 → 页码'+txt); clickEl(el); setTimeout(()=>{if(STATE.running)startNextUnit();},2000); return true; } } } // 策略B: 页面底部的分页区域(非 Ant Design 组件) for(const el of $$('[class*=page] a,[class*=page] button,[class*=page] li,.ant-list-pagination a')) { const t=(el.textContent||'').trim(); if((t==='下一页'||t==='>'||t==='›'||t==='»'||/^\d+$/.test(t))&&el.offsetParent&&!el.disabled&&!el.className.includes('disabled')&&!el.className.includes('active')){ log('翻页(通用) → ',t); clickEl(el); setTimeout(()=>{if(STATE.running)startNextUnit();},2000); return true; } } return false; } // ★ 强制启动视频模式(不管当前状态) function startVideoModeForce(){ // 清理旧状态,但不碰 _navigating(防抖锁由 goToNextChapter/resetChapter 管理) STATE.running = true; STATE.videoEnded = false; STATE.playerInstance = null; STATE.nextAttemptCount = 0; STATE._skipDone = false; // 不重置 _navigating!否则会破坏跳转防抖 // 重启所有定时器 clearInterval(mainLoop); clearInterval(popupLoop); clearInterval(speedLoop); log('强制启动视频模式'); updateStatus({'wz-run-status':'运行中'}); updateInfo(); // 主循环 mainLoop=setInterval(()=>{ if(!STATE.running)return; const p=findVideo(); if(p&&(!STATE.playerInstance||STATE.playerInstance!==getVE(p))){ const v=getVE(p);if(v&&!v._wzHooked)hookVideo(p); } if(STATE.playerInstance){ const v=getVE(STATE.playerInstance); if(v&&v.paused&&!STATE.videoEnded){ // 必须先静音再播放,否则浏览器拦截自动播放 v.muted=true; v.play().then(()=>{applySpeed();applyMute(CONFIG.autoMute);}).catch(()=>{ // 浏览器拦截,尝试模拟点击视频 v.muted=true; v.play().catch(()=>{}); }); } if(v&&!STATE.videoEnded)checkNearEnd(); } updateInfo(); if(STATE.startTime){ const e=Math.floor((Date.now()-STATE.startTime)/1000); updateStatus({'wz-runtime':String(Math.floor(e/60)).padStart(2,'0')+':'+String(e%60).padStart(2,'0')}); } },2000); // 弹窗检测 popupLoop=setInterval(()=>{if(STATE.running)closePopups();},CONFIG.popupCheckInterval); // 速度强制 startSpeedLoop(); // 不断尝试挂钩视频,直到成功 let retries=0; function tryHook(){ if(!STATE.running||!isVideoPage()){if(retries<20&&STATE.running)setTimeout(tryHook,1500);return;} const p=findVideo(); if(p){ const v=getVE(p);if(v)v._wzHooked=false; hookVideo(p);applySpeed();applyMute(CONFIG.autoMute); log('视频挂钩成功 (重试'+retries+'次)'); } else if(retries<20){ retries++; setTimeout(tryHook,1500); } else { log('视频挂钩超时'); } } setTimeout(tryHook,2000); // 重启 DOM 监听 watchNew(); // 更新面板按钮 const startBtn=document.getElementById('wz-btn-start'); const pauseBtn=document.getElementById('wz-btn-pause'); const stopBtn=document.getElementById('wz-btn-stop'); if(startBtn)startBtn.style.display='none'; if(pauseBtn)pauseBtn.style.display='block'; if(stopBtn)stopBtn.style.display='block'; } // ======================== 辅助 ======================== function updateInfo(){ for(const s of['[class*=course-title]','[class*=course-name]','h1','h2','.ant-page-header-heading-title','.ant-breadcrumb li:last-child']){ const e=$(s);if(e&&e.textContent&&e.textContent.trim().length>1&&e.textContent.trim().length<100){STATE.currentCourse=e.textContent.trim();break;} } for(const s of['.ant-menu-item-selected','[class*=active][class*=chapter]','[class*=current]','[class*=playing]','.is-active']){ const e=$(s);if(e&&e.textContent&&e.textContent.trim().length>1){STATE.currentChapter=e.textContent.trim();break;} } updateStatus({'wz-course-name':STATE.currentCourse||'未识别','wz-chapter-name':STATE.currentChapter||'未识别'}); } function checkNearEnd(){ if(!STATE.running||STATE.videoEnded||STATE._navigating)return; const ve=getVE(STATE.playerInstance)||findVideo();if(!ve||!ve.duration||!isFinite(ve.duration))return; const r=ve.duration-ve.currentTime; // 视频剩余5秒内,且已播放超过10秒(排除智能跳尾后对短片的误触发) if(r<=5&&r>0&&ve.currentTime>10){ if(!STATE._nearEndTime)STATE._nearEndTime=Date.now(); // 持续在结尾区域超过12秒仍无ended事件 → 兜底跳转 if(Date.now()-STATE._nearEndTime>12000){ log('ended未触发,兜底跳转(r='+r.toFixed(1)+'s)'); STATE.videoEnded=true;STATE.videoCount++;STATE._nearEndTime=null; updateStatus({'wz-run-status':'兜底跳转'});if(CONFIG.autoNext)goToNextChapter(); } }else if(r>5){ STATE._nearEndTime=null; } } // ======================== 调试 ======================== function debugPage(){ console.group('嘉职院刷课 - 页面结构'); console.log('URL:',location.href,'| 视频页:',isVideoPage(),'| 详情页:',isGradePage()); const vs=$$('video');console.log('Video:',vs.length);vs.forEach((v,i)=>console.log(' ['+i+']',v.src?.substring(0,80),v.duration,v.paused)); const bs=[...$$('button,.ant-btn,a[class*=btn]')].filter(b=>b.offsetParent&&!b.closest('#woczx-panel')); console.log('按钮:',bs.length);bs.slice(0,30).forEach((b,i)=>console.log(' ['+i+']',(b.textContent||'').trim().substring(0,25),b.className?.substring(0,50))); if(isGradePage()){ console.log('--- 学习详情单元 ---'); $$('[class*="detail_list_video--"]').forEach((u,i)=>{ const t=(u.textContent||'').trim(); console.log(' ['+i+']',(t.includes('未开始')?'⬜':t.includes('100%')?'✅':t.includes('%')?'📖':' '),t.substring(0,80)); }); } const si=$$('.ant-menu-item,[class*=chapter],[class*=lesson],[class*=section]'); if(si.length){si.slice(0,20).forEach((it,i)=>{const cl=it.className||'';console.log(' '+(cl.includes('active')||cl.includes('selected')?'✅':' ')+'['+i+']',it.textContent?.trim()?.substring(0,50));});} console.groupEnd();alert('分析完成,按F12查看'); } // ======================== 主循环 ======================== let mainLoop,popupLoop,speedLoop; function startAutoStudy(){ if(isGradePage()){startNextUnit();return;} if(!isVideoPage()){log('非视频页,请进入课程');return;} startVideoModeForce(); } function startSpeedLoop(){if(speedLoop)clearInterval(speedLoop);speedLoop=setInterval(()=>{if(!STATE.running||STATE.videoEnded)return;applySpeed();},CONFIG.speedEnforceInterval);} function pauseAutoStudy(){ STATE.running=false;updateStatus({'wz-run-status':'已暂停'}); document.getElementById('wz-btn-start').style.display='block';document.getElementById('wz-btn-start').textContent='继续刷课';document.getElementById('wz-btn-pause').style.display='none'; clearInterval(mainLoop);clearInterval(popupLoop);clearInterval(speedLoop); } function stopAutoStudy(){ STATE.running=false;updateStatus({'wz-run-status':'已停止'}); document.getElementById('wz-btn-start').style.display='block';document.getElementById('wz-btn-start').textContent='开始刷课';document.getElementById('wz-btn-pause').style.display='none';document.getElementById('wz-btn-stop').style.display='none'; clearInterval(mainLoop);clearInterval(popupLoop);clearInterval(speedLoop); if(STATE.playerInstance)try{getVE(STATE.playerInstance).playbackRate=1;}catch(e){} } function watchNew(){if(STATE._obs)STATE._obs.disconnect();const o=new MutationObserver(()=>{if(!STATE.running)return;const p=findVideo();if(p){const v=getVE(p);if(v&&!v._wzHooked){hookVideo(p);applySpeed();applyMute(CONFIG.autoMute);}}});o.observe(document.body,{childList:true,subtree:true});STATE._obs=o;} // ======================== 登录 ======================== function showLoginTip(){ if(!location.hash.includes('login'))return;const t=document.createElement('div'); t.style.cssText='position:fixed;top:16px;left:50%;transform:translateX(-50%);background:#fff3cd;border:1px solid #ffc107;border-radius:8px;padding:10px 20px;z-index:99999;font-size:13px;text-align:center;max-width:400px;'; t.innerHTML='嘉职院沃创刷课助手 v2.2
请手动登录后进入课程。
Ctrl+Shift+S 开始/停止 | Ctrl+Shift+G 返回详情页'; document.body.appendChild(t);setTimeout(()=>t.remove(),8000); } // ======================== 路由 ======================== function watchRoute(){ let last=location.hash; setInterval(()=>{ if(location.hash!==last){ const prev=last;last=location.hash;log('路由:',prev,'→',location.hash); STATE.playerInstance=null;STATE.videoEnded=false;STATE.nextAttemptCount=0; if(STATE.running){ updateInfo(); if(isVideoPage()){startVideoModeForce();} else if(isGradePage()){setTimeout(()=>startNextUnit(),1500);} } if(location.hash.includes('login'))showLoginTip(); } },500); } // ======================== GM & 快捷键 ======================== if(typeof GM_registerMenuCommand==='function'){GM_registerMenuCommand('重置配置',()=>{GM_setValue('woczx_speed',4);GM_setValue('woczx_smartSkip',true);GM_setValue('woczx_skipSpeed',2);GM_setValue('woczx_autoMute',true);GM_setValue('woczx_popupInterval',1000);GM_setValue('woczx_autoNext',true);GM_setValue('woczx_nextDelay',2);GM_setValue('woczx_showPanel',true);GM_setValue('woczx_debug',true);alert('已重置,请刷新');});} document.addEventListener('keydown',e=>{ if(e.ctrlKey&&e.shiftKey&&e.key==='S'){e.preventDefault();STATE.running?stopAutoStudy():startAutoStudy();} if(e.ctrlKey&&e.shiftKey&&e.key==='D'){e.preventDefault();debugPage();} if(e.ctrlKey&&e.shiftKey&&e.key==='N'){e.preventDefault();goToNextChapter();} if(e.ctrlKey&&e.shiftKey&&e.key==='G'){e.preventDefault();goToGradePage();} }); // ======================== 初始化 ======================== function init(){ console.log('[嘉职院刷课] v2.2 已加载 -',location.href); if(document.body&&document.body.children.length>0)setTimeout(initCore,1500); else document.addEventListener('DOMContentLoaded',()=>setTimeout(initCore,1500)); } function initCore(){ const toast=document.createElement('div');toast.style.cssText='position:fixed;top:10px;left:50%;transform:translateX(-50%);background:#52c41a;color:#fff;padding:8px 20px;border-radius:6px;z-index:999999;font-size:14px;font-weight:bold;box-shadow:0 2px 8px rgba(0,0,0,.2);';toast.textContent='✅ 嘉职院沃创刷课助手 v2.2 已就绪';document.body.appendChild(toast);setTimeout(()=>toast.remove(),2500); if(CONFIG.showPanel)createPanel();watchRoute();if(location.hash.includes('login'))showLoginTip(); // ★ 自动开始:视频页直接开始,学习详情页自动选单元 if(isVideoPage()){log('自动开始刷课');startVideoModeForce();} else if(isGradePage()){log('自动选择单元');startNextUnit();} log('初始化完成'); } const w=unsafeWindow||window;w.__woczx__={start:startAutoStudy,stop:stopAutoStudy,debug:debugPage,goNext:goToNextChapter,goGrade:goToGradePage,nextUnit:startNextUnit,config:CONFIG}; init(); })();