// ==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('[自动刷课] 智能跳过引擎已启动');
})();