// ==UserScript==
// @name 江西水利职业学院-成教-柠檬文才-全自动刷课助手(site.wencaischool.net)
// @version 1.0.0
// @description wencaischool.net 全自动刷视频+自动切换章节,支持倍速/静音
// @author 晓晓
// @match *://learning.wencaischool.net/*
// @match *://site.wencaischool.net/*
// @match *://*.wencaischool.net/*
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function () {
'use strict';
if (window.__jxslHelper) {
console.log('已有脚本在运行,跳过');
return;
}
var STATUS_KEY = 'jxsl_running';
function getRunning() { return GM_getValue(STATUS_KEY, false); }
function setRunning(v) { GM_setValue(STATUS_KEY, v); }
var config = {
speed: parseFloat(localStorage.getItem('jxsl_speed')) || 1.0,
autoPlay: true,
autoNext: true,
debug: true
};
function log(msg, type) {
type = type || 'info';
if (!config.debug && type === 'info') return;
var prefix = { error: '❌', success: '✅', warning: '⚠️' }[type] || '🔍';
console.log(prefix, msg);
}
function $1(s) { return document.querySelector(s); }
// ==================== UI面板 ====================
function createPanel() {
var existing = $1('#jxsl-panel');
if (existing) existing.remove();
var isRunning = getRunning();
var el = document.createElement('div');
el.id = 'jxsl-panel';
el.innerHTML =
'
' +
'
' +
'
🎓' +
'
江西水利职业学院刷课助手
' +
'
' + (isRunning ? '● 运行中' : '○ 已暂停') + '
' +
'
' +
'
播放速度 (最高16x):
' +
'
' +
'
' +
'' +
'' +
'
' +
'
运行中
' +
'
' +
'
' +
'
' +
'
' +
'💬 ' +
'wkds995' +
' 客服微信' +
'
' +
'
';
document.body.appendChild(el);
// 拖拽
var dragEl = $1('#jxsl-drag');
var isDragging = false, startX, startY, origX, origY;
dragEl.addEventListener('mousedown', function (e) {
isDragging = true;
startX = e.clientX; startY = e.clientY;
origX = dragEl.offsetLeft; origY = dragEl.offsetTop;
});
document.addEventListener('mousemove', function (e) {
if (!isDragging) return;
var dx = e.clientX - startX, dy = e.clientY - startY;
var newX = Math.max(0, Math.min(origX + dx, window.innerWidth - dragEl.offsetWidth));
var newY = Math.max(0, Math.min(origY + dy, window.innerHeight - dragEl.offsetHeight));
dragEl.style.left = newX + 'px';
dragEl.style.top = newY + 'px';
dragEl.style.right = 'auto';
});
document.addEventListener('mouseup', function () { isDragging = false; });
// 速度选择
$1('#jxsl-speed').value = config.speed;
$1('#jxsl-speed').addEventListener('change', function () {
config.speed = parseFloat(this.value);
localStorage.setItem('jxsl_speed', config.speed);
applySpeed();
updateStatus('\u901f\u5ea6: ' + config.speed + 'x', 'success');
});
$1('#jxsl-autoplay').addEventListener('change', function () {
config.autoPlay = this.checked;
});
$1('#jxsl-autonext').addEventListener('change', function () {
config.autoNext = this.checked;
});
// 启动/暂停按钮
$1('#jxsl-btn-toggle').addEventListener('click', function () {
if (getRunning()) {
setRunning(false);
var badge = $1('#jxsl-badge');
if (badge) { badge.textContent = '\u25cb \u5df2\u6697\u505c'; badge.style.background = '#6b7280'; }
this.textContent = '\u25b6 \u5f00\u59cb\u5237\u8bfe';
this.style.background = '#1890ff';
updateStatus('\u5df2\u6697\u505c', 'warning');
} else {
setRunning(true);
var badge = $1('#jxsl-badge');
if (badge) { badge.textContent = '\u25cf \u8fd0\u884c\u4e2d'; badge.style.background = '#10b981'; }
this.textContent = '\u23f8 \u6682\u505c\u5237\u8bfe';
this.style.background = '#ef4444';
updateStatus('\u6b63\u5728\u5237\u8bfe...', 'success');
}
});
}
function updateStatus(text, type) {
var el = $1('#jxsl-status');
if (!el) return;
el.textContent = text;
var colors = { success: '#4ade80', warning: '#fbbf24', error: '#ef4444', info: '#60a5fa' };
el.style.color = colors[type] || '#4ade80';
log(text, type);
}
function updateInfo(text) { var el = $1('#jxsl-info'); if (el) el.textContent = text; }
function updateChapter(text) { var el = $1('#jxsl-chapter'); if (el) el.textContent = text; }
// ==================== 视频检测 ====================
function findVideo() {
var frames = document.querySelectorAll('iframe');
for (var i = 0; i < frames.length; i++) {
try {
var v = frames[i].contentDocument.querySelector('video');
if (v) { log('\u5728iframe[' + i + ']\u4e2d\u627e\u5230\u89c6\u9891', 'success'); return v; }
} catch (e) {}
}
var videos = document.querySelectorAll('video');
if (videos.length > 0) {
for (var j = 0; j < videos.length; j++) {
if (videos[j].offsetParent !== null || videos[j].style.display !== 'none') return videos[j];
}
return videos[0];
}
return null;
}
function applySpeed() {
var v = findVideo();
if (v) v.playbackRate = config.speed;
}
// ==================== 章节检测 ====================
function findChapters() {
var allChapters = [];
var frames = document.querySelectorAll('iframe');
for (var fi = 0; fi < frames.length; fi++) {
try {
var doc = frames[fi].contentDocument;
var sections = doc.querySelectorAll('.childSection');
log('iframe[' + fi + '] \u627e\u5230 ' + sections.length + ' \u4e2a .childSection', 'info');
for (var k = 0; k < sections.length; k++) {
var sec = sections[k];
var style = window.getComputedStyle(sec);
if (style.display === 'none' || style.visibility === 'hidden') continue;
var text = (sec.textContent || '').trim();
if (!text || text.length < 5) continue;
var numMatch = text.match(/(\d+\.\d+\.\d+)/);
if (!numMatch) continue;
var isActive = false;
if (location.href.includes(numMatch[1])) isActive = true;
var children = sec.querySelectorAll('*');
for (var c = 0; c < children.length; c++) {
var bg = (window.getComputedStyle(children[c]).backgroundColor || '').toLowerCase();
if (bg.includes('blue') || bg.includes('33, 150, 243') || bg.includes('30, 144')) { isActive = true; break; }
}
var cn = (sec.className || '').toLowerCase();
if (cn.includes('active') || cn.includes('current') || cn.includes('playing')) isActive = true;
allChapters.push({ element: sec, text: text.substring(0, 80), num: numMatch[1], isActive: isActive });
}
} catch (e) { log('iframe[' + fi + '] \u8bbf\u95ee\u5931\u8d25: ' + e.message, 'warning'); }
}
return allChapters;
}
function findNextChapter() {
var chapters = findChapters();
if (chapters.length === 0) { updateStatus('\u672a\u627e\u5230\u7ae0\u8282', 'error'); return null; }
var currentIndex = -1;
for (var i = 0; i < chapters.length; i++) {
if (chapters[i].isActive) { currentIndex = i; break; }
}
if (currentIndex === -1) { updateStatus('\u672a\u8bc6\u522b\u5f53\u524d\u7ae0\u8282', 'warning'); return null; }
if (currentIndex >= chapters.length - 1) { updateStatus('\u5df2\u5b8c\u6210\u6240\u6709\u7ae0\u8282', 'success'); return null; }
var next = chapters[currentIndex + 1];
updateChapter('\u5f53\u524d: ' + chapters[currentIndex].text.substring(0, 25) + '\n\u4e0b\u4e00\u7ae0: ' + next.text.substring(0, 25));
return next.element;
}
// ==================== 主循环 ====================
var counter = 0;
var videoFinished = false;
var switchCooldown = false;
var retryCount = 0;
var lastVideoSrc = '';
function mainLoop() {
counter++;
if (!getRunning()) {
if (counter % 120 === 0) updateStatus('\u5df2\u6697\u505c', 'warning');
return;
}
var v = findVideo();
if (!v) {
if (counter % 60 === 0) { updateStatus('\u672a\u68c0\u6d4b\u5230\u89c6\u9891', 'warning'); updateInfo('\u8bf7\u8fdb\u5165\u89c6\u9891\u64ad\u653e\u9875\u9762'); }
return;
}
if (v.src && v.src !== lastVideoSrc) {
log('\u89c6\u9891\u5207\u6362: ' + (lastVideoSrc ? '\u65b0\u89c6\u9891' : '\u9996\u6b21\u68c0\u6d4b'), 'info');
lastVideoSrc = v.src;
videoFinished = false;
retryCount = 0;
}
if (Math.abs(v.playbackRate - config.speed) > 0.01) applySpeed();
if (config.autoPlay && v.paused && !v.ended && v.readyState >= 2) {
v.play().catch(function (e) { log('\u64ad\u653e\u5931\u8d25: ' + e.message, 'error'); });
updateStatus('\u6b63\u5728\u64ad\u653e', 'success');
videoFinished = false;
}
if (v.duration && !isNaN(v.duration)) {
var cur = Math.floor(v.currentTime);
var total = Math.floor(v.duration);
var pct = Math.floor((cur / total) * 100);
var cm = Math.floor(cur / 60), cs = cur % 60, tm = Math.floor(total / 60), ts = total % 60;
updateInfo('\u2705 ' + pct + '% | ' + cm + ':' + String(cs).padStart(2, '0') + ' / ' + tm + ':' + String(ts).padStart(2, '0') + ' | ' + config.speed + 'x');
if (cur >= total - 2 && !videoFinished && config.autoNext && !switchCooldown) {
videoFinished = true;
updateStatus('\u89c6\u9891\u5373\u5c06\u5b8c\u6210\uff0c\u51c6\u5907\u5207\u6362...', 'warning');
trySwitchNextChapter();
}
}
}
function trySwitchNextChapter() {
var next = findNextChapter();
if (next) {
updateStatus('\u70b9\u51fb\u4e0b\u4e00\u7ae0', 'success');
switchCooldown = true;
retryCount = 0;
try { next.click(); var evt = new MouseEvent('click', { bubbles: true, cancelable: true, view: window }); next.dispatchEvent(evt); } catch (e) {}
setTimeout(function () { switchCooldown = false; }, 15000);
} else {
if (retryCount < 5) {
retryCount++;
log('\u7b2c ' + retryCount + ' \u6b21\u91cd\u8bd5...', 'warning');
setTimeout(function () { if (videoFinished) trySwitchNextChapter(); }, 2000);
} else {
updateStatus('\u672a\u627e\u5230\u4e0b\u4e00\u7ae0', 'error');
retryCount = 0;
}
}
}
function init() {
log('\u521d\u59cb\u5316...', 'info');
createPanel();
setInterval(mainLoop, 500);
updateStatus('\u811a\u672c\u5df2\u542f\u52a8', 'success');
}
if (document.readyState === 'complete') { init(); }
else { window.addEventListener('load', init); }
window.__jxslHelper = { config: config, version: '1.0.0' };
})();