// ==UserScript== // @name 江西省补贴性线上职业技能培训管理平台网课脚本 // @icon https://i.zpimg.cn/public_read/%E6%B1%9F%E8%A5%BFpc_16838203danjzg8w.png // @namespace http://scriptcat.org/ // @version 1.1 // @description 自动连播,强制静音 // @match https://jiangxi.zhipeizaixian.com/study* // @grant none // @run-at document-idle // ==/UserScript== (function() { 'use strict'; let enabled = true; let forceMute = true; let forcePlay = true; let videoTimer = null; let urlMonitor = null; const ui = document.createElement('div'); ui.id = 'as-panel'; ui.innerHTML = `
🎧 自动刷课 v9.3
运行中
强制播放
静音
`; document.body.appendChild(ui); const statusEl = ui.querySelector('#as-status'); const btnStop = ui.querySelector('#as-stop'); const btnStart = ui.querySelector('#as-start'); const btnNext = ui.querySelector('#as-next'); const swForce = ui.querySelector('#as-force'); const swMute = ui.querySelector('#as-mute'); const body = ui.querySelector('.body'); const head = ui.querySelector('.head'); head.addEventListener('click', () => { body.style.display = body.style.display === 'none' ? '' : 'none'; head.querySelector('span:last-child').textContent = body.style.display === 'none' ? '▶' : '▼'; }); function updateStatus(msg) { statusEl.textContent = msg || (enabled ? '运行中' : '已停止'); } // 停止全部 function stopAll() { enabled = false; if (videoTimer) { clearInterval(videoTimer); videoTimer = null; } if (urlMonitor) { clearInterval(urlMonitor); urlMonitor = null; } sessionStorage.removeItem('auto_task'); sessionStorage.removeItem('auto_next'); sessionStorage.removeItem('last_finished_unit'); const v = document.querySelector('video'); if (v) v.pause(); btnStop.style.display = 'none'; btnStart.style.display = 'block'; updateStatus('已停止'); console.log('[自动刷课] 已停止'); } function startAll() { enabled = true; btnStop.style.display = 'block'; btnStart.style.display = 'none'; updateStatus('运行中'); startUrlMonitor(); run(); console.log('[自动刷课] 已启动'); } swForce.addEventListener('click', () => { forcePlay = !forcePlay; swForce.classList.toggle('on', forcePlay); }); swMute.addEventListener('click', () => { forceMute = !forceMute; swMute.classList.toggle('on', forceMute); const video = document.querySelector('video'); if (video) applyMuteState(video); }); btnStop.addEventListener('click', stopAll); btnStart.addEventListener('click', startAll); btnNext.addEventListener('click', () => { const params = new URLSearchParams(location.search); const courseId = params.get('course_id'); const classId = params.get('class_id'); if (!courseId || !classId) return updateStatus('缺少参数'); sessionStorage.removeItem('auto_task'); sessionStorage.setItem('auto_next', '1'); location.href = `/study?course_id=${courseId}&class_id=${classId}`; }); function waitFor(sel, timeout = 15000) { return new Promise(resolve => { const start = Date.now(); function check() { const el = document.querySelector(sel); if (el) return resolve(el); if (Date.now() - start > timeout) return resolve(null); requestAnimationFrame(check); } check(); }); } function isVideoPage() { return location.pathname.includes('/study/video'); } function applyMuteState(video) { if (!video) return; if (forceMute) { video.muted = true; video.volume = 0; try { Object.defineProperty(video, 'muted', { get: () => true, set: () => {}, configurable: true }); Object.defineProperty(video, 'volume', { get: () => 0, set: () => {}, configurable: true }); } catch(e){} const icon = document.querySelector('.prism-volume .volume-icon'); if (icon && !icon.classList.contains('mute')) { icon.dispatchEvent(new MouseEvent('click', { bubbles: true })); } const p = document.getElementById('J_prismPlayer'); const player = p?._player || p?.player || p?.aliplayer; if (player && typeof player.mute === 'function') player.mute(); } else { try { Object.defineProperty(video, 'muted', { writable: true, configurable: true }); Object.defineProperty(video, 'volume', { writable: true, configurable: true }); } catch(e){} video.muted = false; video.volume = 1; } } async function handleVideo() { const params = new URLSearchParams(location.search); const currentUnitId = params.get('unit_id'); const taskRaw = sessionStorage.getItem('auto_task'); if (!taskRaw) { updateStatus('无任务,返回目录'); sessionStorage.setItem('auto_next', '1'); const cid = params.get('course_id'); const clid = params.get('class_id'); location.href = `/study?course_id=${cid}&class_id=${clid}`; return; } let task; try { task = JSON.parse(taskRaw); } catch { task = null; } if (!task || task.unitId !== currentUnitId) { updateStatus('任务不匹配,返回目录'); sessionStorage.removeItem('auto_task'); sessionStorage.setItem('auto_next', '1'); const cid = params.get('course_id') || task?.courseId; const clid = params.get('class_id') || task?.classId; if (cid && clid) location.href = `/study?course_id=${cid}&class_id=${clid}`; return; } updateStatus(`播放 ${currentUnitId}`); console.log(`[自动刷课] 播放 unit_id=${currentUnitId}`); const video = await waitFor('video'); if (!video) return; applyMuteState(video); let lastTime = -1; let stuck = 0; const goBack = (reason) => { console.log(`[自动刷课] 结束:${reason},记录 unit=${currentUnitId}`); sessionStorage.setItem('last_finished_unit', currentUnitId); clearInterval(videoTimer); videoTimer = null; sessionStorage.removeItem('auto_task'); const cid = task.courseId || params.get('course_id'); const clid = task.classId || params.get('class_id'); sessionStorage.setItem('auto_next', '1'); location.href = `/study?course_id=${cid}&class_id=${clid}`; }; videoTimer = setInterval(() => { if (!enabled) { clearInterval(videoTimer); videoTimer = null; return; } if (!video || !document.contains(video)) return; if (forcePlay && video.paused) { video.play().catch(()=>{}); const b = document.querySelector('.prism-big-play-btn'); if (b) b.dispatchEvent(new MouseEvent('click', { bubbles: true })); } applyMuteState(video); if (video.duration && !isNaN(video.duration) && video.duration > 0) { const progress = video.currentTime / video.duration; const remaining = video.duration - video.currentTime; // 1. 正常结束 if (remaining < 1 || video.ended) { goBack('正常结束'); return; } // 2. 进度 ≥95% 且卡住 5 秒 if (Math.abs(video.currentTime - lastTime) < 0.1) { stuck++; } else { stuck = 0; lastTime = video.currentTime; } if (progress >= 0.95 && stuck >= 25) { // 25次 * 200ms = 5秒 goBack('进度95%卡死'); return; } // 3. 额外:超过总时长10秒强制返回 if (video.currentTime > video.duration + 10) { goBack('超时'); } } }, 200); const playBtn = await waitFor('.prism-big-play-btn'); if (playBtn) playBtn.dispatchEvent(new MouseEvent('click', { bubbles: true })); } async function handleCatalog(retryCount = 0) { if (!enabled) return; const forceNext = sessionStorage.getItem('auto_next'); if (forceNext === '1') { sessionStorage.removeItem('auto_next'); updateStatus('自动跳转下一节...'); } else { updateStatus('扫描目录...'); } // 等待目录标签 let tab = null; for (let i = 0; i < 30; i++) { const tabs = document.querySelectorAll('.leftFate___1zL-W'); for (const t of tabs) { const span = t.querySelector('.icon_text___BnIA_'); if (span && span.textContent.trim() === '目录') { tab = t; break; } } if (tab) break; await new Promise(r => setTimeout(r, 500)); } if (!tab) { updateStatus('目录标签未找到'); return; } if (!tab.classList.contains('fate_active___rKVF5')) { tab.dispatchEvent(new MouseEvent('click', { bubbles: true })); await new Promise(r => setTimeout(r, 600)); } const container = await waitFor('.mulu_list___hpQAJ', 5000); if (!container) { updateStatus('目录未加载'); return; } const lastFinished = sessionStorage.getItem('last_finished_unit'); const rows = container.querySelectorAll('.content_units_wrap___29R73'); for (const row of rows) { const right = row.querySelector('.units_right_box___xROhe'); if (!right) continue; // 1. 已完成 → 跳过 if (right.querySelector('.study_success_svg___Dc7SQ')) continue; // 获取当前行的 unit_id const link = row.querySelector('a.units_wrap_box___3DqST'); if (!link) continue; const href = link.href; const u = new URL(href); const rowUnitId = u.searchParams.get('unit_id'); if (!rowUnitId) continue; // 2. **关键**:如果这个课程是刚播放完但还没显示已完成的,强制跳过 if (lastFinished && rowUnitId === lastFinished) { console.log(`[自动刷课] 跳过刚看完的 unit=${rowUnitId}(等待状态刷新)`); continue; } // 3. 未开始或学习中 → 选择它 if (right.querySelector('.not_start___2H5mD') || right.querySelector('.progress_get_on___3F-Lx')) { const taskData = { courseId: new URLSearchParams(location.search).get('course_id'), classId: new URLSearchParams(location.search).get('class_id'), unitId: rowUnitId }; sessionStorage.setItem('auto_task', JSON.stringify(taskData)); updateStatus(`跳转: ${rowUnitId}`); console.log(`[自动刷课] 下一节 unit_id=${rowUnitId}`); location.href = href; return; } } // 没有找到合适的课程,但存在刚看完的课程可能还处于未刷新状态 if (lastFinished) { // 检查该课程是否仍然存在于列表中,且还没有对勾 let stillExists = false; for (const row of rows) { const link = row.querySelector('a.units_wrap_box___3DqST'); if (!link) continue; const u = new URL(link.href); if (u.searchParams.get('unit_id') === lastFinished) { const right = row.querySelector('.units_right_box___xROhe'); if (right && !right.querySelector('.study_success_svg___Dc7SQ')) { stillExists = true; } break; } } if (stillExists && retryCount < 5) { updateStatus(`等待状态更新... (${retryCount+1}/5)`); console.log(`[自动刷课] 等待 last_finished 刷新,重试${retryCount+1}`); await new Promise(r => setTimeout(r, 3000)); return handleCatalog(retryCount + 1); } } // 彻底没有未完成课程了 updateStatus('全部完成!'); console.log('[自动刷课] 所有课程已完成'); alert('🎉 所有课程已完成!微信:windows557'); stopAll(); } function run() { if (!enabled) return; if (videoTimer) { clearInterval(videoTimer); videoTimer = null; } if (isVideoPage()) handleVideo(); else handleCatalog(); } let lastUrl = location.href; function startUrlMonitor() { if (urlMonitor) clearInterval(urlMonitor); urlMonitor = setInterval(() => { if (location.href !== lastUrl) { lastUrl = location.href; if (enabled) run(); } }, 1000); } run(); startUrlMonitor(); updateStatus('运行中'); console.log('[自动刷课] 智能跳过引擎已启动'); })();