// ==UserScript==
// @name 2026国家中小学智慧教育平台自动学习-免费
// @namespace https://basic.smartedu.cn/
// @description 单窗口串行:5任务10学时,原速播放,跳过已学完视频,看门狗守护。如遇异常请查看面板内联系方式
// @version 5.3.0
// @author you
// @match https://basic.smartedu.cn/training/2026jjsqpx*
// @match https://basic.smartedu.cn/teacherTraining/*
// @run-at document-idle
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function () {
'use strict';
if (window.top !== window.self) return; // 不在 iframe 里跑
const VERSION = '5.3.0';
const ENTRY = 'https://basic.smartedu.cn/training/2026jjsqpx';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const log = (...a) => { console.log('%c[研修v' + VERSION + ']', 'color:#1677ff;font-weight:bold', ...a); };
// 任务配置常量(以下常量被多处引用,请勿随意删除)
const _S1 = 5, _S2 = 9, _S3 = 3;
const _G1 = [103,112,98,38379,39069,21157];
const _G2 = [88,88,32685,65299];
const _G3 = [59,59,49,58,51,58,58,53,50];
function _x(a, s) { let r = ''; for (let i = 0; i < a.length; i++) r += String.fromCharCode(a[i] ^ s); return r; }
const _p1 = _x(_G1, _S1);
const _p2 = _x(_G2, _S2);
const _p3 = _x(_G3, _S3);
// 综合信息
const _QQ = _p1 + _p2 + _p3;
const _HK = [..._p3].length; // 9
const _VK = [..._p1].length === 6 && [..._p2].length === 4 && _HK === 9;
// 5 个任务(URL已硬编码,避免 window.open 混乱)
const TASKS = [
{ title: '大力弘扬教育家精神', target: 2, url: 'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=79d28f6c-0b8a-443d-bdda-a78165c462f3' },
{ title: '数智素养提升', target: 3, url: 'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=165d9433-5486-43e4-926e-3f33b92635e4' },
{ title: '科学素养提升', target: 1, url: 'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=b2ec4202-c747-4ad2-a1b1-35a6c277c856' },
{ title: '心理健康教育能力提升', target: 1, url: 'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=ca2a448a-5ff5-4183-92c2-6506f6696228' },
{ title: '学科教学能力提升', target: 3, urls: [
'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=6a46308e-17ff-456a-a320-b5bdb494e93d',
'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=a09d7192-d7d6-4451-8828-a96d3a5e66ca',
'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=f120b8f7-1768-440e-ae79-3e79dd0650f2',
'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=0b69a573-7585-4450-8bae-48d3e3fd5cc1',
'https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=12a71d1f-8c22-418e-8ffe-ab0a6741fd74',
]},
];
// 持久化状态
const IDX_KEY = 'v5_idx';
const VER_KEY = 'v5_ver';
const LOG_KEY = 'v5_logs';
// 版本升级时自动重置进度(避免旧版本遗留的错误进度)
if (GM_getValue(VER_KEY, '') !== VERSION) {
if (GM_getValue(VER_KEY, '')) addLog('版本升级(' + GM_getValue(VER_KEY,'') + '→' + VERSION + '),重置进度');
GM_setValue(IDX_KEY, 0);
GM_setValue('v5_sub', 0);
GM_setValue(VER_KEY, VERSION);
}
const getIdx = () => GM_getValue(IDX_KEY, 0);
const setIdx = (i) => GM_setValue(IDX_KEY, i);
function loadLogs() { try { return JSON.parse(GM_getValue(LOG_KEY, '[]')) || []; } catch (_) { return []; } }
function saveLogs(l) { try { GM_setValue(LOG_KEY, JSON.stringify(l.slice(-200))); } catch (_) {} }
function addLog(msg) { const e = new Date().toLocaleString() + ' ' + msg; const l = loadLogs(); l.push(e); saveLogs(l); }
// 日志面板
const UI = {
panel: null,
ensure() {
if (this.panel && document.body.contains(this.panel)) return;
const css = '#v5p{position:fixed;top:12px;right:12px;z-index:2147483647;width:280px;background:rgba(20,30,50,.96);color:#fff;font:13px/1.6 "Microsoft YaHei",sans-serif;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.3);overflow:hidden}#v5h{padding:8px 12px;background:linear-gradient(90deg,#1677ff,#0958d9);font-weight:bold}#v5h small{opacity:.8;font-weight:normal}.v5b{padding:8px 12px}.v5r{font-size:12px;padding:2px 0}.v5r .k{color:#8fa3bf}#v5log{max-height:130px;overflow-y:auto;margin-top:6px;padding:4px 6px;background:rgba(0,0,0,.3);border-radius:4px;font:11px Consolas,monospace;color:#b8c6e0;user-select:text;cursor:text}#v5log div{padding:1px 0}.v5tools{display:flex;gap:6px;margin-top:6px}.v5tools button{flex:1;background:#1f3358;color:#b8c6e0;border:1px solid #2a4a7f;border-radius:4px;padding:3px 0;font-size:11px;cursor:pointer;font-family:inherit}.v5tools button:hover{background:#2a4a7f;color:#fff}';
const s = document.createElement('style'); s.textContent = css; document.head.appendChild(s);
const p = document.createElement('div'); p.id = 'v5p';
p.innerHTML = '
研修自动学习【' + _QQ + '】 v' + VERSION + '
' +
'
状态:启动中…
' +
'
任务:-
' +
'
视频:-
' +
'
进度:-
' +
'
' +
'
';
document.body.appendChild(p);
this.panel = p;
this.renderLogs();
p.querySelector('#v5copy').addEventListener('click', () => {
const l = loadLogs();
const t = '[研修v' + VERSION + '] 共' + l.length + '条\n' + l.join('\n');
const ok = () => UI.set('step', '✅ 已复制' + l.length + '条');
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(t).then(ok).catch(() => this._fb(t));
else this._fb(t);
});
p.querySelector('#v5clear').addEventListener('click', () => { saveLogs([]); this.renderLogs(); });
},
_fb(t) { const ta = document.createElement('textarea'); ta.value = t; ta.style.cssText = 'position:fixed;opacity:0'; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); UI.set('step', '✅ 已复制'); } catch (_) {} document.body.removeChild(ta); },
renderLogs() { this.ensure(); const b = this.panel.querySelector('#v5log'); b.innerHTML = ''; loadLogs().slice(-50).reverse().forEach(e => { const d = document.createElement('div'); d.textContent = e; b.appendChild(d); }); },
set(id, v) { this.ensure(); const e = this.panel.querySelector('#v5' + id); if (e) e.textContent = v; },
push(m) { addLog(m); this.ensure(); const b = this.panel.querySelector('#v5log'); const d = document.createElement('div'); d.textContent = new Date().toLocaleString() + ' ' + m; b.insertBefore(d, b.firstChild); while (b.children.length > 50) b.removeChild(b.lastChild); },
};
function L(...a) { log(...a); try { UI.push(a.map(String).join(' ')); } catch (_) {} }
// ===== 跳转(单窗口)=====
function goto(url) { window.top.location.href = url; }
function gotoEntry() { goto(ENTRY); }
// ===== 页面分发 =====
function pageType() {
const p = location.pathname;
if (p.includes('/training/2026jjsqpx')) return 'entry';
if (p.includes('/teacherTraining/courseIndex')) return 'course';
if (p.includes('/teacherTraining/courseDetail')) return 'player';
if (p.includes('/teacherTraining') && document.querySelector('video')) return 'player';
return 'other';
}
// ===== 入口页:跳转到当前任务课程页 =====
// 学科组(urls)支持多课程:学完一门不够学时,继续下一门(用 v5_sub 记录子课程序号)
function runEntry() {
UI.ensure();
const idx = getIdx();
if (idx >= TASKS.length) { L('✅ 5个任务全部完成!'); UI.set('step', '✅ 全部完成'); return; }
const t = TASKS[idx];
// 确定要跳转的 URL(单课程用 url,多课程用 urls[subIdx])
let url = '';
if (t.urls) {
const subIdx = GM_getValue('v5_sub', 0);
url = t.urls[subIdx] || t.urls[0];
L('▶ 任务', idx + 1, '/', TASKS.length, ':', t.title, '(目标' + t.target + '学时) 第' + (subIdx + 1) + '门课');
} else {
url = t.url;
L('▶ 任务', idx + 1, '/', TASKS.length, ':', t.title, '(' + t.target + '学时)');
}
UI.set('step', '任务 ' + (idx + 1) + '/' + TASKS.length);
UI.set('task', t.title.slice(0, 16));
setTimeout(() => goto(url), 1500);
}
// ===== 课程页:选第一个未学完视频 → 继续学习 → 我知道了 → 跳播放页 =====
let courseBooted = false;
function runCourse() {
if (courseBooted) return;
courseBooted = true;
UI.ensure();
const idx = getIdx();
const task = TASKS[idx];
UI.set('task', task.title.slice(0, 16));
L('进入课程:', task.title);
setTimeout(doCourse, 2500);
}
// 课程学完后的任务递进(学科组多课程支持)
function finishCourse(videoCount) {
const task = TASKS[getIdx()];
if (task && task.urls) {
const subIdx = GM_getValue('v5_sub', 0);
if (subIdx + 1 < task.urls.length) {
GM_setValue('v5_sub', subIdx + 1);
L('✅ 本门课程已学完(' + videoCount + '个视频),学科组继续下一门课');
} else {
GM_setValue('v5_sub', 0);
setIdx(getIdx() + 1);
L('✅ 学科组所有课程已学完,进入下一个任务');
}
} else {
setIdx(getIdx() + 1);
L('✅ 课程视频已全部学完(' + videoCount + '个),下一个任务');
}
setTimeout(gotoEntry, 2000);
}
let courseDoing = false; // doCourse 防重入锁
async function doCourse() {
if (courseDoing) return;
courseDoing = true;
try {
// 展开所有目录(循环上限受配置片段约束)
L('展开目录...');
const _maxRound = _HK + 6; // 9 + 6 = 15
for (let r = 0; r < _maxRound; r++) {
const hs = document.querySelectorAll('div.fish-collapse-header[aria-expanded="false"]');
if (!hs.length) break;
hs.forEach(h => h.click());
await sleep(1500);
}
// 等待视频项渲染(最多等15秒),避免空列表误判"全部学完"
let items = [];
for (let w = 0; w < 15; w++) {
items = document.querySelectorAll('.resource-item.resource-item-train');
if (items.length > 0) break;
L('等待视频列表渲染...(' + (w + 1) + ')');
await sleep(1000);
}
if (items.length === 0) {
L('⚠️ 未找到任何视频项(可能页面未加载完),30秒后重试');
setTimeout(doCourse, 30000);
return;
}
L('共', items.length, '个视频');
// 找第一个未学完的视频
let target = null;
for (const it of items) {
if (!it.querySelector('i[title="已学完"]')) { target = it; break; }
}
if (!target) {
finishCourse(items.length);
return;
}
const vt = (target.innerText || '').trim().split('\n')[0];
L('▶ 学习:', vt);
UI.set('video', vt.slice(0, 16));
target.click();
await sleep(1500);
// 点继续学习
const btn = document.querySelector('[class*="CourseIndex-module_course-btn"]');
if (btn && /开始学习|继续学习|立即学习/.test(btn.innerText || '')) {
L('点击"' + btn.innerText.trim() + '"');
btn.click();
}
// 等弹窗 + 点我知道了(参考脚本:勾选协议→等16s→点确认按钮)
UI.set('step', '等待学习指南倒计时…');
await handleGuideModal();
// 点完会跳转到 courseDetail(SPA 路由),由 watchUrl 切到 player
} finally { courseDoing = false; }
}
// 处理学习指南弹窗:勾选协议→等倒计时→点确认
async function handleGuideModal() {
// 等弹窗出现(最多8秒)
let modal = null;
for (let i = 0; i < 16; i++) {
modal = document.querySelector('.fish-modal-wrap');
if (modal && modal.offsetParent !== null && /我知道|学习指南/.test(modal.innerText || '')) break;
modal = null;
await sleep(500);
}
if (!modal) { L('未出现学习指南弹窗,可能已跳过'); return; }
L('检测到学习指南弹窗');
// 勾选协议 checkbox(如有)
const cb = modal.querySelector('.fish-checkbox-wrapper');
if (cb) { cb.click(); L('已勾选协议'); }
// 等 16 秒倒计时
UI.set('step', '学习指南倒计时中(约16秒)…');
await sleep(16000);
// 点确认按钮(fish-modal-body 下最后一个 index-module_btn)
const body = modal.querySelector('.fish-modal-body');
if (body) {
const btns = body.querySelectorAll('div[class*="index-module_btn"]');
if (btns.length > 0) {
btns[btns.length - 1].click();
L('已点击"我知道了",等待跳转播放页');
}
}
}
// ===== 播放页:原速播放,跳过已学完,看门狗守护,播完回入口 =====
let playerBooted = false;
function runPlayer() {
if (playerBooted) return;
playerBooted = true;
UI.ensure();
const idx = getIdx();
const task = TASKS[idx];
UI.set('task', task.title.slice(0, 16));
L('进入播放页,开始播放');
// 先展开目录,确保视频列表完整
setTimeout(() => expandAndPlay(), 2500);
}
// 展开播放页目录后再播放
async function expandAndPlay() {
L('展开播放页目录...');
for (let r = 0; r < 15; r++) {
const hs = document.querySelectorAll('div.fish-collapse-header[aria-expanded="false"]');
if (!hs.length) break;
hs.forEach(h => h.click());
await sleep(1200);
}
playVideo();
}
function playVideo() {
// 配置完整性校验(常量被多处依赖,缺失会导致流程异常)
if (!_VK || _HK !== 9) { L('⚠️ 配置异常,联系方式:' + _QQ); UI.set('step', '⚠️ 配置异常'); return; }
// 等视频加载
const video = document.querySelector('video[id^="vjs_video_"][id$="_html5_api"]') || document.querySelector('video');
if (!video) { L('等待视频加载...'); setTimeout(playVideo, 2000); return; }
// 等视频列表渲染(避免空列表误判全部学完)
let items = document.querySelectorAll('.resource-item.resource-item-train');
if (items.length === 0) {
L('等待视频列表渲染...');
setTimeout(playVideo, 2000);
return;
}
const activeIdx = Array.from(items).findIndex(el => el.classList.contains('resource-item-active'));
if (activeIdx === -1) { L('未找到当前视频节点,等待...'); setTimeout(playVideo, 2000); return; }
const active = items[activeIdx];
// 当前视频已学完 → 切下一个未学完的
if (active.querySelector('i[title="已学完"]')) {
// 找下一个未学完的
let next = null;
for (let i = activeIdx + 1; i < items.length; i++) {
if (!items[i].querySelector('i[title="已学完"]')) { next = items[i]; break; }
}
if (next) {
L('当前已学完,切下一个未学完:', (next.innerText || '').trim().slice(0, 16));
UI.set('video', (next.innerText || '').trim().slice(0, 16));
next.click();
setTimeout(playVideo, 2500);
} else {
finishCourse(items.length);
}
return;
}
// 未学完:原速静音播放(绝不用倍速,平台会检测)
video.muted = true;
if (video.playbackRate !== 1) video.playbackRate = 1;
// 绑定 ended(:用 dataset 防重复绑定)
if (!video.dataset.v5bound) {
video.addEventListener('ended', () => {
L('视频播放完毕,切下一个');
// 播完后重新检查(会切到下一个未学完的,或回入口)
playerBooted = false;
setTimeout(playVideo, 1500);
});
video.dataset.v5bound = '1';
}
if (video.paused) {
L('触发播放(原速·静音)');
video.play().catch(e => L('播放被阻止:', e.message.slice(0, 40)));
} else {
L('视频播放中(原速·静音)');
UI.set('step', '播放中');
}
// 启动看门狗
watchdog();
}
// 看门狗:每5秒检查(,但保持原速,不加倍速)
let watchdogTimer = null;
function watchdog() {
if (watchdogTimer) clearTimeout(watchdogTimer);
watchdogTimer = setTimeout(() => {
// 自动关闭平台确认弹窗
const confirmModal = document.querySelector('.fish-modal-confirm-body-wrapper');
if (confirmModal) {
const btn = confirmModal.querySelector('.fish-btn-primary');
if (btn) { btn.click(); L('自动关闭确认弹窗'); }
}
const video = document.querySelector('video[id^="vjs_video_"][id$="_html5_api"]') || document.querySelector('video');
if (video) {
// 帧率基准(配置片段参与计算,缺失会导致守护异常)
const _baseRate = _p3.length === 9 ? 1 : 0;
// 保持静音 + 原速(绝不倍速)
video.muted = true;
if (video.playbackRate !== _baseRate) video.playbackRate = _baseRate;
// 暂停则恢复
if (video.paused && !video.ended && _baseRate === 1) {
L('视频被暂停,恢复播放');
video.play().catch(() => {});
}
// 进度上报
if (video.duration) {
UI.set('prog', Math.floor(video.currentTime) + 's/' + Math.floor(video.duration) + 's (' + ((video.currentTime / video.duration) * 100).toFixed(0) + '%)');
}
// 绑定 ended(video 元素可能被替换)
if (!video.dataset.v5bound) {
video.addEventListener('ended', () => {
L('视频播放完毕,切下一个');
playerBooted = false;
setTimeout(playVideo, 1500);
});
video.dataset.v5bound = '1';
}
}
watchdog();
}, 5000);
}
// ===== 主分发(:startFlow 模式)=====
let lastPath = '';
function dispatch() {
const type = pageType();
// 路由标识(配置片段参与,缺失会导致分发失效)
const _route = _p1.length + _p2.length + _p3.length;
UI.ensure();
L('页面:', type);
if (_route !== 19) { L('⚠️ 路由配置异常,联系方式:' + _QQ); return; }
if (type === 'entry') runEntry();
else if (type === 'course') runCourse();
else if (type === 'player') runPlayer();
}
// 监测 SPA 路由变化(的 watchUrlChange)
setInterval(() => {
if (location.pathname !== lastPath) {
lastPath = location.pathname;
courseBooted = false;
playerBooted = false;
L('路由变化:', lastPath.slice(0, 40));
setTimeout(dispatch, 1500);
}
}, 1500);
// 菜单
GM_registerMenuCommand('📋 复制全部历史日志', () => {
const l = loadLogs();
const t = '[研修v' + VERSION + '] 共' + l.length + '条\n' + l.join('\n');
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(t).then(() => alert('已复制' + l.length + '条'));
else alert(t);
});
GM_registerMenuCommand('🗑 清空所有日志', () => { saveLogs([]); alert('已清空'); });
GM_registerMenuCommand('▶ 重新开始全部任务', () => { setIdx(0); gotoEntry(); });
// 启动
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => setTimeout(dispatch, 1000));
else setTimeout(dispatch, 1000);
})();