// ==UserScript==
// @name 江西新余学院-成教继续教育-柠檬文才-全自动刷课助手(jw.wencaischool.net/xyxy)
// @version 1.0.0
// @description 新余学院继续教育专用版(柠檬文才系统):全自动刷视频+自动切换章节+倍速,静音/自动播放
// @author 晓晓
// @match *://jw.wencaischool.net/xyxy/*
// @match *://learning.wencaischool.net/*
// @match *://site.wencaischool.net/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
var STATUS_KEY = 'xyxy_running';
var SETTINGS_KEY = 'xyxy_speed_settings';
function getRunning() { return GM_getValue(STATUS_KEY, false); }
function setRunning(v) { GM_setValue(STATUS_KEY, v); }
var defaults = { speed: 2, muted: true, autoplay: true, autonext: true };
var S = Object.assign({}, defaults, GM_getValue(SETTINGS_KEY, {}));
function save() { GM_setValue(SETTINGS_KEY, S); }
function log(msg, type) {
type = type || 'info';
var prefix = { error: '❌', success: '✅', warning: '⚠️' }[type] || '🔍';
console.log(prefix, msg);
var el = document.querySelector('#xyxy-log');
if (el) {
var t = new Date().toLocaleTimeString('zh-CN', { hour12: false });
el.value += '[' + t + '] ' + msg + '\n';
el.scrollTop = el.scrollHeight;
}
}
// ==================== UI面板 ====================
function $1(s) { return document.querySelector(s); }
function createPanel() {
var existing = $1('#xyxy-panel');
if (existing) existing.remove();
var isRunning = getRunning();
var el = document.createElement('div');
el.id = 'xyxy-panel';
el.style.cssText = [
'position:fixed;top:20px;right:20px;z-index:999999;',
'background:linear-gradient(135deg,#1a1a2e,#16213e);',
'color:#e0e0e0;padding:14px 18px;border-radius:12px;',
'min-width:300px;box-shadow:0 8px 32px rgba(0,0,0,0.5);',
'font-family:"PingFang SC","Microsoft YaHei",sans-serif;',
'border:1px solid rgba(255,255,255,0.08);font-size:13px;'
].join('');
el.innerHTML =
'
' +
'
🎂' +
'
新余学院续教学习
' +
'
' + (isRunning ? '● 运行中' : '○ 已暗停') + '
' +
'
' +
'倍速: ' + S.speed + 'x
' +
'' +
'0.5x16x
' +
'' +
'' +
'' +
'' +
'
' +
'等待启动
' +
'' +
'' +
'' +
'' +
'
' +
'' +
'' +
'💬 ' +
'wkds995' +
' 客服微信' +
'
';
document.body.appendChild(el);
// 启动/暂停
$1('#xyxy-btn-toggle').addEventListener('click', function () {
if (getRunning()) {
setRunning(false);
$1('#xyxy-badge').textContent = '○ 已暂停';
$1('#xyxy-badge').style.background = '#6b7280';
this.textContent = '▶ 开始刷课';
this.style.background = '#1890ff';
updateStatus('已暂停', 'warning');
} else {
setRunning(true);
$1('#xyxy-badge').textContent = '● 运行中';
$1('#xyxy-badge').style.background = '#10b981';
this.textContent = '⏸ 暂停刷课';
this.style.background = '#ef4444';
updateStatus('开始刷课...', 'success');
}
});
// 倍速滑块
$1('#xyxy-speed').addEventListener('input', function () {
S.speed = parseFloat(this.value);
$1('#xyxy-speed-val').textContent = S.speed + 'x';
save();
applySpeed();
log('倍速: ' + S.speed + 'x', 'success');
});
// 静音
$1('#xyxy-muted-btn').addEventListener('click', function () {
S.muted = !S.muted;
this.style.background = S.muted ? '#ef4444' : '#374151';
this.innerHTML = S.muted ? '☕ 静音' : '🔊 有声';
save();
applyMuted();
log(S.muted ? '已静音' : '开启声音');
});
// 自动播放
$1('#xyxy-ap-btn').addEventListener('click', function () {
S.autoplay = !S.autoplay;
this.style.background = S.autoplay ? '#10b981' : '#374151';
this.innerHTML = S.autoplay ? '▶ 自动' : '⏸ 手动';
save();
log(S.autoplay ? '自动播放开启' : '手动播放');
});
// 自动切换
$1('#xyxy-next-btn').addEventListener('click', function () {
S.autonext = !S.autonext;
this.style.background = S.autonext ? '#3b82f6' : '#374151';
this.innerHTML = S.autonext ? '➤ 自动切换' : '⏮ 手动切换';
save();
log(S.autonext ? '自动切换章节开启' : '手动切换章节');
});
}
function updateStatus(text, type) {
var el = $1('#xyxy-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 updateProgress(text) { var el = $1('#xyxy-progress'); if (el) el.textContent = text; }
function updateChapter(text) { var el = $1('#xyxy-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('在iframe[' + i + ']中找到视频', 'info'); return v; }
} catch (e) {}
}
var videos = document.querySelectorAll('video');
for (var j = 0; j < videos.length; j++) {
if (videos[j].offsetParent !== null || videos[j].style.display !== 'none') return videos[j];
}
return videos.length > 0 ? videos[0] : null;
}
function applySpeed() {
var v = findVideo();
if (v && Math.abs(v.playbackRate - S.speed) > 0.01) v.playbackRate = S.speed;
}
function applyMuted() {
var v = findVideo();
if (v) v.muted = S.muted;
}
// ==================== 章节检测(柠檬文才标准结构)====================
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 + '] 找到 ' + sections.length + ' 个 .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 + '] 访问失败: ' + e.message, 'warning'); }
}
return allChapters;
}
function findNextChapter() {
var chapters = findChapters();
if (chapters.length === 0) return null;
var currentIndex = -1;
for (var i = 0; i < chapters.length; i++) {
if (chapters[i].isActive) { currentIndex = i; break; }
}
if (currentIndex === -1 || currentIndex >= chapters.length - 1) return null;
var next = chapters[currentIndex + 1];
updateChapter('当前: ' + chapters[currentIndex].text.substring(0, 20) + '\n下一章: ' + next.text.substring(0, 20));
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('已暂停(点击"开始刷课"继续)', 'warning');
return;
}
var v = findVideo();
if (!v) {
if (counter % 60 === 0) updateStatus('未检测到视频,请进入视频播放页面', 'warning');
return;
}
if (v.src && v.src !== lastVideoSrc) {
log('视频切换: ' + (lastVideoSrc ? '新视频' : '首次检测'), 'info');
lastVideoSrc = v.src;
videoFinished = false;
retryCount = 0;
}
if (Math.abs(v.playbackRate - S.speed) > 0.01) applySpeed();
if (v.muted !== S.muted) v.muted = S.muted;
if (S.autoplay && v.paused && !v.ended && v.readyState >= 2) {
v.play().catch(function (e) { log('播放失败: ' + e.message, 'error'); });
updateStatus('正在播放', '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;
updateProgress('📊 ' + pct + '% | ' + cm + ':' + String(cs).padStart(2, '0') + ' / ' + tm + ':' + String(ts).padStart(2, '0') + ' | ' + S.speed + 'x ' + (v.muted ? '🔇' : '🔊'));
if (cur >= total - 2 && !videoFinished && S.autonext && !switchCooldown) {
videoFinished = true;
updateStatus('视频即将完成,准备切换下一章...', 'warning');
trySwitchNextChapter();
}
}
}
function trySwitchNextChapter() {
var next = findNextChapter();
if (next) {
updateStatus('点击下一章', 'success');
switchCooldown = true;
retryCount = 0;
try {
next.click();
next.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
} catch (e) {}
setTimeout(function () { switchCooldown = false; }, 15000);
} else {
if (retryCount < 5) {
retryCount++;
log('第 ' + retryCount + ' 次重试...', 'warning');
setTimeout(function () { if (videoFinished) trySwitchNextChapter(); }, 2000);
} else {
updateStatus('已完成所有章节(或未找到下一章)', 'success');
retryCount = 0;
}
}
}
// ==================== 入口 ====================
function main() {
log('新余学院继续教育刷课助手 v1.0 已启动', 'success');
log('当前倍速: ' + S.speed + 'x | 静音: ' + (S.muted?'是':'否') + ' | 自动: ' + (S.autoplay?'是':'否') + ' | 自动切换: ' + (S.autonext?'是':'否'), 'info');
createPanel();
setInterval(mainLoop, 500);
if (getRunning()) {
updateStatus('正在刷课...', 'success');
}
}
if (document.readyState === 'complete') { main(); }
else { window.addEventListener('load', main); }
})();