// ==UserScript==
// @name 2026暑期教师研修3分钟极速搞定(国家智慧中小学)
// @namespace http://tampermonkey.net/qi
// @version 1.2.7
// @description 【超多好评推荐】免费、高效、一键开启自动刷课,无需充值。
// @match *://basic.smartedu.cn/*
// @match *://https://www.smartedu.cn/*
// @match *://https://www.smartedu.cn/jiaoshi/*
// @require https://scriptcat.org/lib/637/1.4.6/ajaxHooker.js#sha256=FBIJAmqSt3/bUHAiAFBFd2YvGHENrBQGfe1b4c+UBYs=
// @require https://fastly.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js
// @require https://fastly.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js
// @require https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @grant GM_info
// @grant GM_addStyle
// @connect x-study-record-api.ykt.eduyun.cn
// @run-at document-end
// @icon https://basic.smartedu.cn/favicon.ico
// @license MIT
// @tag 国家中小学智慧教育平台 暑期培训 教师研修 自动学习 学习辅助
// ==/UserScript==
// ============================================================
const ENCODED_PERMANENT_CODES='WyJLN1M5RDJGNEc4SDkiLCJNM1A1UTZSN1Q5VTIiLCJWMVc0WDVZOFozQTYiLCJCMkM3RTVGOUgxSjQiLCJMNk44TzJQM1M1RDciLCJGM0c2SDlKMks1TTgiLCJOMVE0UjdUM1U2VjkiLCJXMlk1WjhBMUI0QzciLCJENkU5RjJHNUg4SjEiLCJLM0w2TTlOMlA1UTgiXQ==',ENCODED_CODES='eyIyMDI2LTA4LTEyIjp7InRlbXAiOiIwQzI4RDkxRSIsImRhaWx5IjoiMTQyRTAzNTEifSwiMjAyNi0wOC0xMyI6eyJ0ZW1wIjoiOTY3QTY5QjMiLCJkYWlseSI6IjlGRDU2QjAwIn0sIjIwMjYtMDgtMTQiOnsidGVtcCI6IkU4NTQ0OTkxIiwiZGFpbHkiOiIzRjQyNjcxQSJ9LCIyMDI2LTA4LTE1Ijp7InRlbXAiOiJCQ0JDNzkzRCIsImRhaWx5IjoiQkYzRDI1RUUifSwiMjAyNi0wOC0xNiI6eyJ0ZW1wIjoiOEFCNTE4NjYiLCJkYWlseSI6IjJBMkNCNkI5In0sIjIwMjYtMDgtMTciOnsidGVtcCI6IjE1QTlCNTIyIiwiZGFpbHkiOiI1Q0Y0MjgyNSJ9LCIyMDI2LTA4LTE4Ijp7InRlbXAiOiI4RDQ5REY5RiIsImRhaWx5IjoiNTFERDhFQjMifSwiMjAyNi0wOC0xOSI6eyJ0ZW1wIjoiNjlGMEQ2QjMiLCJkYWlseSI6IjBGNEIxNUJCIn0sIjIwMjYtMDgtMjAiOnsidGVtcCI6IjA0Q0M5NzJFIiwiZGFpbHkiOiJGNDc0RURGQiJ9LCIyMDI2LTA4LTIxIjp7InRlbXAiOiIyMUQxMThDMiIsImRhaWx5IjoiN0YwMzU1REQifSwiMjAyNi0wOC0yMiI6eyJ0ZW1wIjoiMjVFQThDMjAiLCJkYWlseSI6IjA5NTU5OEQwIn0sIjIwMjYtMDgtMjMiOnsidGVtcCI6IjQ0OTlCMkFCIiwiZGFpbHkiOiJBRjZDNzgxNSJ9LCIyMDI2LTA4LTI0Ijp7InRlbXAiOiJEOTRBQTM0QyIsImRhaWx5IjoiNEE2MkQ5M0MifSwiMjAyNi0wOC0yNSI6eyJ0ZW1wIjoiOTVBRTNDQzAiLCJkYWlseSI6IkU1NDE5QjhDIn0sIjIwMjYtMDgtMjYiOnsidGVtcCI6IkIxQjZCNzM0IiwiZGFpbHkiOiIzNzZDMTVDRSJ9LCIyMDI2LTA4LTI3Ijp7InRlbXAiOiI2RDI5QjU1MyIsImRhaWx5IjoiRUQxQzQ2QjEifSwiMjAyNi0wOC0yOCI6eyJ0ZW1wIjoiOEU5REQyRjgiLCJkYWlseSI6IjZERTdENjkzIn0sIjIwMjYtMDgtMjkiOnsidGVtcCI6IkE2NDFCMUE0IiwiZGFpbHkiOiI5MkVCNEQ0RiJ9LCIyMDI2LTA4LTMwIjp7InRlbXAiOiI3MjQxQTcyMyIsImRhaWx5IjoiMUI5RUJEMzIifSwiMjAyNi0wOC0zMSI6eyJ0ZW1wIjoiMzdBMjk0RDgiLCJkYWlseSI6IjgyRTk0MEMxIn0sIjIwMjYtMDktMDEiOnsidGVtcCI6IkYxNDcyQkM1IiwiZGFpbHkiOiIwNkQzN0E1OSJ9LCIyMDI2LTA5LTAyIjp7InRlbXAiOiI1ODBFNjNBRiIsImRhaWx5IjoiQzkxQjgyNzQifSwiMjAyNi0wOS0wMyI6eyJ0ZW1wIjoiMkRCODcxOTAiLCJkYWlseSI6Ijc0RkEzNjBFIn0sIjIwMjYtMDktMDQiOnsidGVtcCI6IkM0NjEwRTJCIiwiZGFpbHkiOiJEMDU4QkYxMiJ9LCIyMDI2LTA5LTA1Ijp7InRlbXAiOiI3OTNBQzU0NyIsImRhaWx5IjoiNDE4MkU2OUQifSwiMjAyNi0wOS0wNiI6eyJ0ZW1wIjoiRTA1MkQ3MUMiLCJkYWlseSI6IkE3MzA5RDg1In0sIjIwMjYtMDktMDciOnsidGVtcCI6IjE2OEI0RkEzIiwiZGFpbHkiOiI1QkM3MjQ2MCJ9LCIyMDI2LTA5LTA4Ijp7InRlbXAiOiI5RjI0QTA3NiIsImRhaWx5IjoiMjMwRDFFNDgifSwiMjAyNi0wOS0wOSI6eyJ0ZW1wIjoiNDJENzE4NUUiLCJkYWlseSI6IkI4NkFGNzMxIn0sIjIwMjYtMDktMTAiOnsidGVtcCI6IkE1MTkzQ0YyIiwiZGFpbHkiOiI2RTQxMDVCQyJ9LCIyMDI2LTA5LTExIjp7InRlbXAiOiIwQjczODY0OSIsImRhaWx5IjoiRjM5Q0UyN0EifSwiMjAyNi0wOS0xMiI6eyJ0ZW1wIjoiRDNDNTIwODEiLCJkYWlseSI6IjE1NzY0OUUzIn0sIjIwMjYtMDktMTMiOnsidGVtcCI6IjY3OTBFQjI0IiwiZGFpbHkiOiI4RDJCQzA1RiJ9LCIyMDI2LTA5LTE0Ijp7InRlbXAiOiJCNDFDRjU3QSIsImRhaWx5IjoiMzBFQTZCMTYifSwiMjAyNi0wOS0xNSI6eyJ0ZW1wIjoiOEU2MzdEMDUiLCJkYWlseSI6IkMyNTkwN0FEIn0sIjIwMjYtMDktMTYiOnsidGVtcCI6IjIxRkE1QjlDIiwiZGFpbHkiOiI3QjA0RDgyOSJ9LCIyMDI2LTA5LTE3Ijp7InRlbXAiOiI1Qzg0MTJFNyIsImRhaWx5IjoiRTZBRjMxNTAifSwiMjAyNi0wOS0xOCI6eyJ0ZW1wIjoiRjYwQkQ5MzEiLCJkYWlseSI6IjA4MTdBQzY0In0sIjIwMjYtMDktMTkiOnsidGVtcCI6IjNBNzFDNDY4IiwiZGFpbHkiOiI5NEQyNTNCRiJ9LCIyMDI2LTA5LTIwIjp7InRlbXAiOiJDODJFODA1MyIsImRhaWx5IjoiRDdCMDFFQTYifSwiMjAyNi0wOS0yMSI6eyJ0ZW1wIjoiNzBCNjRBMUQiLCJkYWlseSI6IjRGMzhDOTcyIn0sIjIwMjYtMDktMjIiOnsidGVtcCI6IkU5NUQxRjhCIiwiZGFpbHkiOiJBMjFFNzQwQiJ9LCIyMDI2LTA5LTIzIjp7InRlbXAiOiIxQzQ4QjM3MCIsImRhaWx5IjoiNTY5MzJEQzgifSwiMjAyNi0wOS0yNCI6eyJ0ZW1wIjoiOTIwNTc2QUMiLCJkYWlseSI6IjI4Q0E5MDNFIn0sIjIwMjYtMDktMjUiOnsidGVtcCI6IjRCOTJFMUQ0IiwiZGFpbHkiOiJCQjQ1Rjg2MSJ9LCIyMDI2LTA5LTI2Ijp7InRlbXAiOiJBMTMwNjhDRiIsImRhaWx5IjoiNjU3RTEyOTcifSwiMjAyNi0wOS0yNyI6eyJ0ZW1wIjoiMDRFQURCNTIiLCJkYWlseSI6IkY5Mjc0Q0EwIn0sIjIwMjYtMDktMjgiOnsidGVtcCI6IkRENzQyMzk2IiwiZGFpbHkiOiIxMTBCNjU4QyJ9LCIyMDI2LTA5LTI5Ijp7InRlbXAiOiI2MzE5QzA0QSIsImRhaWx5IjoiOENFMjNGNTUifSwiMjAyNi0wOS0zMCI6eyJ0ZW1wIjoiQjc1RjhFNjEiLCJkYWlseSI6IjNENTBBQjc4In19';globalThis['ENCODED_PERMANENT_CODES']=ENCODED_PERMANENT_CODES;globalThis['ENCODED_CODES']=ENCODED_CODES;
//
// ============================================================
const PERMANENT_CODES=JSON['parse'](atob(ENCODED_PERMANENT_CODES)),CODES=JSON['parse'](atob(ENCODED_CODES));globalThis['PERMANENT_CODES']=PERMANENT_CODES;globalThis['CODES']=CODES;
//
// ============================================================
function getTodayStr() {
const now = new Date();
const utc8 = new Date(now.getTime() + 8 * 3600 * 1000);
const y = utc8.getUTCFullYear();
const m = String(utc8.getUTCMonth() + 1).padStart(2,'0');
const d = String(utc8.getUTCDate()).padStart(2,'0');
return `${y}-${m}-${d}`;
}
function getDeviceId() {
let id = localStorage.getItem('qi_device_id');
if (!id) {
id = 'DEV_' + Date.now() + '_' + Math.random().toString(36).substr(2, 12);
localStorage.setItem('qi_device_id', id);
}
return id;
}
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
//
// ============================================================
class AuthManager {
constructor() {
this.usedCodes = GM_getValue('qi_used_codes', {});
this.permanentBindings = GM_getValue('qi_permanent_bindings', {});
this.currentAuth = GM_getValue('qi_current_auth', null);
this.isAuthorized = false;
this.authType = null;
this.activatedAt = null;
this.dailyDate = null;
this.countdownInterval = null;
this.countdownEl = null;
this.onExpireCallback = null;
this.todayStr = getTodayStr();
this.deviceId = getDeviceId();
this.validateAuth();
}
validateAuth() {
if (!this.currentAuth) {
this.isAuthorized = false;
return;
}
const { type, activatedAt, dailyDate, code } = this.currentAuth;
this.authType = type;
this.activatedAt = activatedAt;
this.dailyDate = dailyDate;
if (type === 'temporary_30min') {
if (activatedAt && (Date.now() - activatedAt) < 30 * 60 * 1000) {
this.isAuthorized = true;
} else {
this.isAuthorized = false;
this.clearAuth();
}
} else if (type === 'daily') {
if (dailyDate === this.todayStr) {
this.isAuthorized = true;
} else {
this.isAuthorized = false;
this.clearAuth();
}
} else if (type === 'permanent') {
if (code && this.permanentBindings[code] === this.deviceId) {
this.isAuthorized = true;
} else {
this.isAuthorized = false;
this.clearAuth();
}
} else {
this.isAuthorized = false;
}
}
activate(code) {
const raw = code.trim().toUpperCase();
const clean = raw.replace(/[^A-Z0-9]/g, '');
if (this.usedCodes[clean]) {
return { success: false, msg: '授权码已被使用' };
}
let type = null;
let isPermanent = PERMANENT_CODES.includes(clean);
if (isPermanent) {
if (this.permanentBindings[clean]) {
if (this.permanentBindings[clean] !== this.deviceId) {
return { success: false, msg: '该永久授权码已被其他设备绑定,无法使用' };
}
} else {
this.permanentBindings[clean] = this.deviceId;
GM_setValue('qi_permanent_bindings', this.permanentBindings);
}
type = 'permanent';
} else {
const today = this.todayStr;
if (CODES[today]) {
if (CODES[today].temp === clean) {
type = 'temporary_30min';
} else if (CODES[today].daily === clean) {
type = 'daily';
}
}
if (!type) {
return { success: false, msg: '无效授权码' };
}
}
this.usedCodes[clean] = true;
GM_setValue('qi_used_codes', this.usedCodes);
const authData = {
type: type,
activatedAt: null,
dailyDate: null,
code: clean
};
if (type === 'temporary_30min') authData.activatedAt = Date.now();
else if (type === 'daily') authData.dailyDate = this.todayStr;
else if (type === 'permanent') authData.activatedAt = Date.now();
GM_setValue('qi_current_auth', authData);
this.currentAuth = authData;
this.isAuthorized = true;
this.authType = type;
this.activatedAt = authData.activatedAt;
this.dailyDate = authData.dailyDate;
this.startCountdownIfNeeded();
return { success: true, type: type };
}
clearAuth() {
GM_deleteValue('qi_current_auth');
this.currentAuth = null;
this.isAuthorized = false;
this.authType = null;
this.activatedAt = null;
this.dailyDate = null;
this.removeCountdownUI();
if (this.countdownInterval) clearInterval(this.countdownInterval);
if (this.onExpireCallback) this.onExpireCallback();
}
getStatus() { return this.isAuthorized; }
getType() { return this.authType; }
getRemainingSeconds() {
if (this.authType !== 'temporary_30min' || !this.activatedAt) return 0;
const elapsed = Date.now() - this.activatedAt;
return Math.max(0, Math.floor((30 * 60 * 1000 - elapsed) / 1000));
}
createCountdownUI() {
if (this.authType !== 'temporary_30min') return;
if (document.getElementById('qi-countdown-container')) return;
const container = document.createElement('div');
container.id = 'qi-countdown-container';
container.style.cssText = 'position:fixed;top:10px;right:10px;background:rgba(0,0,0,0.8);color:#fff;padding:12px 20px;border-radius:30px;font-family:Microsoft Yahei,sans-serif;font-size:20px;font-weight:bold;z-index:2147483647;box-shadow:0 4px 16px rgba(0,0,0,0.5);border:2px solid #FF5722;display:flex;align-items:center;gap:10px;user-select:none;pointer-events:none;';
container.innerHTML = '⏳30:00剩余';
document.body.appendChild(container);
this.countdownEl = document.getElementById('qi-countdown-time');
this.startCountdownTimer();
}
startCountdownTimer() {
if (this.countdownInterval) clearInterval(this.countdownInterval);
this.updateCountdownDisplay();
this.countdownInterval = setInterval(() => this.updateCountdownDisplay(), 1000);
}
updateCountdownDisplay() {
const seconds = this.getRemainingSeconds();
if (!this.countdownEl) return;
if (seconds <= 0) {
this.countdownEl.textContent = '00:00';
this.clearAuth();
if (window._qiInstance) {
window._qiInstance.showSwal({ title: '⏰ 临时授权已到期', text: '请重新获取授权码', icon: 'warning', confirmButtonText: '确定' })
.then(() => window.location.href = 'https://basic.smartedu.cn/training/2026jjsqpx');
}
return;
}
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
this.countdownEl.textContent = `${String(mins).padStart(2,'0')}:${String(secs).padStart(2,'0')}`;
}
removeCountdownUI() {
const el = document.getElementById('qi-countdown-container');
if (el) el.remove();
this.countdownEl = null;
if (this.countdownInterval) clearInterval(this.countdownInterval);
}
startCountdownIfNeeded() {
if (this.authType === 'temporary_30min') {
setTimeout(() => this.createCountdownUI(), 200);
} else {
this.removeCountdownUI();
}
}
initOnPageLoad() {
if (this.isAuthorized && this.authType === 'temporary_30min') this.createCountdownUI();
this.startCountdownIfNeeded();
}
}
//
// ============================================================
class SmartEduModule {
//
constructor() {
this.authManager = new AuthManager();
this.authorized = this.authManager.getStatus();
this.fullDatas = null;
this.resourceMap = new Map();
this.stats = { completedVideos: [], totalDuration: 0, totalCount: 0 };
this.courseId = null;
this.targetCredit = 0;
this.targetSeconds = 0;
this.creditCompleted = 0;
this.popupObserver = null;
this.keepAliveTimer = null;
this.isProcessing = false;
this.Swal = unsafeWindow.Swal || window.Swal;
this.autoPlayEnabled = false;
this.isPlaying = false;
this.processedVideos = new WeakSet();
this.popupBlockEnabled = false;
this.parseCourseId();
this.freeCourseId = FREE_COURSE_ID;
this.pendingAuth = GM_getValue('qi_pending_auth', false);
this._freePromptShown = false;
this._finished = false;
this._autoMode = GM_getValue('qi_auto_mode', false);
this._started = false;
this._detecting = false;
this._homeTimer = null;
this._lastJumpedCourse = null;
this._lastJumpTime = 0;
this._urlCheckTimer = null;
this._lastUrl = window.location.href;
if (this.authorized) {
this.enableUI();
this.authManager.startCountdownIfNeeded();
if (!this.isTrainingHomePage()) {
this.popupBlockEnabled = GM_getValue(POPUP_BLOCK_KEY, false);
} else {
this.popupBlockEnabled = false;
}
if (this.popupBlockEnabled && !this.isTrainingHomePage()) {
this.startPopupObserver();
}
} else {
this.showUnauthorizedBanner();
this.disableUI();
}
window._qiInstance = this;
this.authManager.onExpireCallback = () => {
this.lockAllFeatures();
this.showUnauthorizedBanner();
};
}
//
showUnauthorizedBanner() {
if (document.getElementById('qi-unauth-banner')) return;
const banner = document.createElement('div');
banner.id = 'qi-unauth-banner';
banner.style.cssText = `
position: fixed; top: 0; left: 0; width: 100%; z-index: 9999999;
background: #d32f2f; color: #fff; text-align: center;
padding: 10px 20px; font-size: 16px; font-weight: bold;
font-family: 'Microsoft Yahei'; box-shadow: 0 2px 10px rgba(0,0,0,0.3);
display: flex; justify-content: center; align-items: center; gap: 20px;
flex-wrap: wrap;
`;
banner.innerHTML = `
⚠️ 未授权 — 请关注微信公众号“叙言哥哥”发送“授权码”获取免费授权,或先免费学习“学科美育教学改革专题培训”。
`;
document.body.appendChild(banner);
document.getElementById('qi-free-btn').addEventListener('click', () => {
this._autoMode = true;
this._started = true;
this._finished = false;
GM_setValue('qi_auto_mode', true);
window.location.href = `https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=${this.freeCourseId}`;
});
document.getElementById('qi-auth-btn').addEventListener('click', () => {
this.requestAuthorization();
});
}
//
disableUI() {
const btnIds = ['qi-screenshot-btn', 'qi-donate-btn', 'qi-instant-btn'];
btnIds.forEach(id => {
const btn = document.getElementById(id);
if (btn) {
btn.style.opacity = '0.5';
btn.style.pointerEvents = 'none';
btn.title = '未授权,暂时不可用';
}
});
const autoToggle = document.getElementById('qi-auto-toggle');
if (autoToggle) {
autoToggle.disabled = true;
autoToggle.checked = false;
document.getElementById('qi-auto-status').textContent = '关';
}
const popupToggle = document.getElementById('qi-popup-toggle');
if (popupToggle) {
popupToggle.disabled = true;
popupToggle.checked = false;
document.getElementById('qi-popup-status').textContent = '关';
}
}
//
enableUI() {
const btnIds = ['qi-guide-btn', 'qi-instant-btn', 'qi-screenshot-btn', 'qi-donate-btn', 'qi-start-btn'];
btnIds.forEach(id => {
const btn = document.getElementById(id);
if (btn) {
btn.style.opacity = '1';
btn.style.pointerEvents = 'auto';
btn.title = '';
}
});
const autoToggle = document.getElementById('qi-auto-toggle');
if (autoToggle) {
autoToggle.disabled = false;
autoToggle.checked = this.autoPlayEnabled;
}
const popupToggle = document.getElementById('qi-popup-toggle');
if (popupToggle) {
if (this.isTrainingHomePage()) {
popupToggle.disabled = true;
popupToggle.checked = false;
document.getElementById('qi-popup-status').textContent = '关(强制)';
} else {
popupToggle.disabled = false;
popupToggle.checked = this.popupBlockEnabled;
document.getElementById('qi-popup-status').textContent = this.popupBlockEnabled ? '开' : '关';
}
}
const banner = document.getElementById('qi-unauth-banner');
if (banner) banner.remove();
this.updateStartButtonVisibility();
}
//
async requestAuthorization() {
try {
let result;
if (this.Swal) {
result = await this.showSwal({
title: '🔐 请输入授权码',
html: `
获取授权码请关注微信公众号“叙言哥哥”发送“授权码”即可。
`,
input: 'text',
inputPlaceholder: '输入授权码(字母数字)',
showCancelButton: true,
confirmButtonText: '验证',
cancelButtonText: '取消',
inputValidator: (value) => {
if (!value) return '请输入授权码';
return null;
},
preConfirm: (value) => {
const result = this.authManager.activate(value);
if (!result.success) {
this.showSwal({ title: '❌ 激活失败', text: result.msg, icon: 'error', confirmButtonText: '确定' });
return false;
}
return result;
}
});
} else {
const input = prompt('请输入授权码:');
if (!input) return false;
const result = this.authManager.activate(input);
if (!result.success) {
alert(result.msg);
return false;
}
result.isConfirmed = true;
}
if (result.isConfirmed) {
this.authorized = true;
this.enableUI();
this.authManager.startCountdownIfNeeded();
this.showAuthSuccessMessage(result.type);
return true;
} else {
return false;
}
} catch (e) {
console.error('授权出错:', e);
return false;
}
}
//
showAuthSuccessMessage(type) {
let title, text;
if (type === 'temporary_30min') {
title = '⏳ 30分钟临时授权已生效';
text = '从现在开始倒计时30分钟,页面右上角显示剩余时间,刷新不受影响。';
} else if (type === 'daily') {
title = '🎉 恭喜你解锁今日授权';
text = '今日内您可无限使用,明日失效,请及时完成研修。';
} else if (type === 'permanent') {
title = '🔓 永久授权已生效';
text = '该码已绑定您的设备,其他设备无法使用。';
}
this.showSwal({ title, text, icon: 'success', timer: 3000, showConfirmButton: false });
}
//
clearAuth() {
this.authManager.clearAuth();
this.authorized = false;
this.lockAllFeatures();
if (!this.isTrainingHomePage()) {
window.location.href = 'https://basic.smartedu.cn/training/2026jjsqpx';
}
}
//
isTrainingHomePage() {
const url = window.location.href.split('?')[0];
return url === 'https://basic.smartedu.cn/training/2026jjsqpx';
}
//
parseCourseId() {
const params = new URLSearchParams(window.location.search);
const id = params.get('courseId');
if (id && id in COURSE_CREDIT_MAP) {
this.courseId = id;
this.targetCredit = COURSE_CREDIT_MAP[id];
this.targetSeconds = this.targetCredit * 2700;
} else {
this.courseId = null;
this.targetCredit = 0;
this.targetSeconds = 0;
}
}
//
showSwal(options) {
if (this.Swal) return this.Swal.fire(options);
else {
if (options.input) {
const val = prompt(options.title + '\n' + (options.text || '') + '\n' + (options.inputPlaceholder || ''));
if (val === null) return Promise.resolve({ dismiss: 'cancel' });
else return Promise.resolve({ value: val, isConfirmed: true });
} else {
alert(options.title + '\n' + (options.text || ''));
return Promise.resolve({ isConfirmed: true });
}
}
}
//
togglePopupBlock(enabled) {
if (this.isTrainingHomePage()) enabled = false;
this.popupBlockEnabled = enabled;
GM_setValue(POPUP_BLOCK_KEY, enabled);
if (enabled && !this.isTrainingHomePage()) {
this.startPopupObserver();
} else {
if (this.popupObserver) {
this.popupObserver.disconnect();
this.popupObserver = null;
}
}
const toggle = document.getElementById('qi-popup-toggle');
if (toggle) {
if (this.isTrainingHomePage()) {
toggle.disabled = true;
toggle.checked = false;
document.getElementById('qi-popup-status').textContent = '关(强制)';
} else {
toggle.disabled = false;
toggle.checked = enabled;
document.getElementById('qi-popup-status').textContent = enabled ? '开' : '关';
}
}
}
//
dismissAllPopups() {
if (!this.popupBlockEnabled) return;
const selectors = ['.el-message-box__wrapper', '.el-dialog__wrapper', '.vjs-modal-dialog', '.course-video-reload', '.speed-popup', '[class*="dialog"]', '[class*="modal"]'];
const keywords = ["关闭", "取消", "知道了", "确定", "继续", "重播", "再播", "倍速", "暂停学习", "close", "cancel", "我知道了"];
document.querySelectorAll(selectors.join(',')).forEach(el => {
if (getComputedStyle(el).display === 'none') return;
const closeBtns = el.querySelectorAll('button, div, span, i, a');
for (let btn of closeBtns) {
const text = btn.innerText?.trim() || '';
const cls = btn.className?.baseVal || btn.className || '';
if (keywords.some(kw => text.includes(kw)) || /el-message-box__close|el-dialog__close|vjs-close-button|close-btn/i.test(cls)) {
if (btn.offsetWidth > 0 && !btn.disabled) {
btn.click();
return;
}
}
}
el.style.display = 'none';
});
}
//
startPopupObserver() {
if (this.popupObserver) this.popupObserver.disconnect();
this.popupObserver = new MutationObserver(() => {
clearTimeout(this._popupTimer);
this._popupTimer = setTimeout(() => this.dismissAllPopups(), 100);
});
this.popupObserver.observe(document.body, { childList: true, subtree: true });
}
//
showGuide(force = false) {
if (!force && localStorage.getItem('noMoreDialog') === 'true') return;
this.showSwal({
title: `📚 使用指南`,
html: `
🚀 免费体验流程
1
点击 “免费体验” 按钮(或 “开始自动学习”),自动跳转至 “学科美育教学改革专题培训” 课程,免费获取 3 学时。
2
免费体验结束后,弹出授权框,关注微信公众号“叙言哥哥”发送“授权码”获取免费授权,解锁全部课程继续学习。
🛠️ 研修脚本详细操作步骤
1
进入研修主页后,点击左侧 “开始自动学习” 按钮,脚本会弹窗确认,确认后自动检测并跳转学习。
2
每个板块学习时,自动弹窗询问是否 “一键开刷”,确认后刷课并自动轮播。
3
截图分享:点击紫色按钮,截取学习完成截图,可保存或复制。
4
打赏:点击粉色按钮,查看打赏二维码,即可对作者进行打赏。
💡 提示
免费体验全程包含弹窗拦截,与授权后体验完全一致。授权后按顺序学习其余四门必修课程,全部完成后将显示恭喜页面。
`,
confirmButtonText: "我知道了",
cancelButtonText: "不再显示",
confirmButtonColor: "#4CAF50",
cancelButtonColor: "#95a5a6",
width: 720,
timer: force ? undefined : 5000,
timerProgressBar: force ? undefined : true,
showCloseButton: false
}).then((result) => {
if (result.dismiss === 'cancel') {
localStorage.setItem('noMoreDialog', 'true');
}
});
}
//
onDonateClick() {
window.open('https://ibb.co/gFg4YYLx', '_blank');
}
//
createUI() {
const style = document.createElement('style');
style.textContent = `
.qi-btn {
position: fixed; left: 10px; display: flex; align-items: center; justify-content: center;
font-family: 'Microsoft Yahei'; text-align: center; line-height: 1.2; padding: 0;
z-index: 2147483647; border: none; cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s, background 0.3s;
user-select: none;
box-shadow: 0 2px 12px rgba(0,0,0,0.25);
width: 70px;
height: 70px;
border-radius: 50%;
font-size: 13px;
font-weight: bold;
color: #fff;
}
.qi-btn:hover { transform: scale(1.06); }
.qi-btn:active { transform: scale(0.95); }
#qi-guide-btn {
top: 110px;
background: linear-gradient(145deg, #FF6F00, #E65100);
box-shadow: 0 4px 14px rgba(255,77,175,0.3);
}
#qi-instant-btn {
top: 195px;
background: linear-gradient(145deg, #43A047, #388E3C);
box-shadow: 0 4px 16px rgba(76,175,80,0.4);
}
#qi-instant-btn:disabled {
background: #94d3a2;
cursor: default;
transform: none;
box-shadow: none;
}
#qi-screenshot-btn {
top: 280px;
background: linear-gradient(145deg, #9C27B0, #6A1B9A);
box-shadow: 0 4px 14px rgba(156,39,176,0.3);
}
#qi-donate-btn {
top: 365px;
background: linear-gradient(145deg, #FF4081, #C2185B);
box-shadow: 0 4px 14px rgba(255,64,129,0.3);
}
#qi-start-btn {
top: 470px;
width: 72px;
height: 72px;
border-radius: 50%;
background: linear-gradient(145deg, #FF9800, #E65100);
color: #fff;
font-size: 12px;
font-weight: bold;
box-shadow: 0 4px 14px rgba(255,152,0,0.4);
line-height: 1.3;
border: 2px solid #fff;
transition: background 0.3s, border-color 0.3s;
display: flex;
}
#qi-start-btn.active {
background: linear-gradient(145deg, #4CAF50, #2E7D32);
border-color: #4CAF50;
box-shadow: 0 0 20px rgba(76,175,80,0.6);
}
#qi-start-btn.completed {
background: linear-gradient(145deg, #9E9E9E, #616161);
border-color: #9E9E9E;
box-shadow: none;
cursor: default;
}
.qi-swal-confirm-btn {
font-size: 18px !important;
padding: 12px 40px !important;
}
.qi-toggle-wrapper {
position: fixed;
left: 10px;
z-index: 2147483647;
display: flex;
flex-direction: column;
gap: 8px;
background: rgba(0,0,0,0.7);
padding: 10px 16px;
border-radius: 16px;
backdrop-filter: blur(8px);
color: #fff;
font-family: 'Microsoft Yahei';
font-size: 13px;
user-select: none;
bottom: 30px;
}
.qi-toggle-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
min-width: 140px;
}
.qi-toggle-label {
display: flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.qi-toggle-switch {
position: relative;
width: 50px;
height: 30px;
flex-shrink: 0;
}
.qi-toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.qi-toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: #ccc;
transition: 0.3s;
border-radius: 30px;
}
.qi-toggle-slider:before {
content: "";
position: absolute;
height: 26px;
width: 26px;
left: 2px;
bottom: 2px;
background: white;
transition: 0.3s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.qi-toggle-switch input:checked + .qi-toggle-slider {
background: #4cd964;
}
.qi-toggle-switch input:checked + .qi-toggle-slider:before {
transform: translateX(20px);
}
.qi-toggle-status {
font-size: 12px;
opacity: 0.8;
min-width: 16px;
text-align: right;
}
.qi-toggle-divider {
border-top: 1px solid rgba(255,255,255,0.15);
margin: 2px 0;
}
`;
document.head.appendChild(style);
const btnData = [
{ id: 'qi-guide-btn', text: '使用
指南' },
{ id: 'qi-instant-btn', text: '一键开刷' },
{ id: 'qi-screenshot-btn', text: '截图
分享' },
{ id: 'qi-donate-btn', text: '💖
打赏' }
];
btnData.forEach(({id, text}) => {
const btn = document.createElement('div');
btn.id = id;
btn.className = 'qi-btn';
btn.innerHTML = text;
document.body.appendChild(btn);
});
const startBtn = document.createElement('div');
startBtn.id = 'qi-start-btn';
startBtn.className = 'qi-btn';
startBtn.innerHTML = '🚀
开始
自动学习';
document.body.appendChild(startBtn);
document.getElementById('qi-instant-btn').addEventListener('click', this.onInstantClick.bind(this));
document.getElementById('qi-screenshot-btn').addEventListener('click', this.onScreenshotClick.bind(this));
document.getElementById('qi-guide-btn').addEventListener('click', () => this.showGuide(true));
document.getElementById('qi-donate-btn').addEventListener('click', this.onDonateClick.bind(this));
document.getElementById('qi-start-btn').addEventListener('click', async () => {
if (this._finished) {
this._finished = false;
this._autoMode = true;
this._started = true;
GM_setValue('qi_auto_mode', true);
const btn = document.getElementById('qi-start-btn');
if (btn) {
btn.classList.add('active');
btn.style.background = 'linear-gradient(145deg, #4CAF50, #2E7D32)';
btn.innerHTML = '🟢
运行中
点击关闭';
}
this.startHomeDetector();
await this.detectAndJump();
return;
}
if (this._started && this._autoMode) {
this._autoMode = false;
this._started = false;
GM_setValue('qi_auto_mode', false);
this.stopHomeDetector();
const btn = document.getElementById('qi-start-btn');
if (btn) {
btn.classList.remove('active');
btn.style.background = 'linear-gradient(145deg, #FF9800, #E65100)';
btn.innerHTML = '🚀
开始
自动学习';
}
this.showSwal({ title: '⏹️ 已关闭自动学习', icon: 'info', timer: 1500, showConfirmButton: false });
return;
}
if (!this.isTrainingHomePage()) {
const confirmNav = await this.showSwal({
title: '🔁 跳转至研修主页',
text: '当前不在研修主页,需要跳转后启动自动学习,是否继续?',
icon: 'question',
confirmButtonText: '跳转',
cancelButtonText: '取消',
showCancelButton: true
});
if (!confirmNav.isConfirmed) return;
this._autoMode = true;
this._started = true;
this._finished = false;
GM_setValue('qi_auto_mode', true);
const btn = document.getElementById('qi-start-btn');
if (btn) {
btn.classList.add('active');
btn.style.background = 'linear-gradient(145deg, #4CAF50, #2E7D32)';
btn.innerHTML = '🟢
运行中
点击关闭';
}
window.location.href = 'https://basic.smartedu.cn/training/2026jjsqpx';
return;
}
const result = await this.showSwal({
title: '📚 已检测到 2026 暑期教师研修页面',
text: '是否开启自动跳转学习?(将先免费学习“学科美育”,完成后需授权继续)',
icon: 'info',
confirmButtonText: '开启',
cancelButtonText: '暂不',
showCancelButton: true,
reverseButtons: false,
allowOutsideClick: false
});
if (result.isConfirmed) {
this._started = true;
this._autoMode = true;
this._finished = false;
GM_setValue('qi_auto_mode', true);
const btn = document.getElementById('qi-start-btn');
if (btn) {
btn.classList.add('active');
btn.style.background = 'linear-gradient(145deg, #4CAF50, #2E7D32)';
btn.innerHTML = '🟢
运行中
点击关闭';
}
this.startHomeDetector();
await this.detectAndJump();
this.showSwal({ title: '✅ 已启动自动学习', text: '脚本将自动跳转课程并完成,请稍候。', icon: 'success', timer: 2000, showConfirmButton: false });
}
});
const toggleWrapper = document.createElement('div');
toggleWrapper.className = 'qi-toggle-wrapper';
toggleWrapper.innerHTML = `
▶️ 自动轮播
${this.autoPlayEnabled ? '开' : '关'}
🛡️ 弹窗拦截
`;
document.body.appendChild(toggleWrapper);
const autoToggle = document.getElementById('qi-auto-toggle');
const autoStatus = document.getElementById('qi-auto-status');
autoToggle.addEventListener('change', (e) => {
const checked = e.target.checked;
this.toggleAutoPlay(checked);
autoStatus.textContent = checked ? '开' : '关';
});
const popupToggle = document.getElementById('qi-popup-toggle');
const popupStatus = document.getElementById('qi-popup-status');
if (this.isTrainingHomePage()) {
popupToggle.disabled = true;
popupToggle.checked = false;
popupStatus.textContent = '关(强制)';
toggleWrapper.style.opacity = '0.8';
toggleWrapper.title = '研修首页强制关闭弹窗拦截';
}
popupToggle.addEventListener('change', (e) => {
const checked = e.target.checked;
if (this.isTrainingHomePage()) {
e.target.checked = false;
popupStatus.textContent = '关(强制)';
this.togglePopupBlock(false);
this.showSwal({ title: '⚠️ 研修首页强制关闭', text: '在研修首页无法开启弹窗拦截,请进入课程详情页后操作。', icon: 'warning', timer: 2000, showConfirmButton: false });
return;
}
this.togglePopupBlock(checked);
popupStatus.textContent = checked ? '开' : '关';
});
if (!this.authorized) {
this.disableUI();
this.showUnauthorizedBanner();
} else {
this.enableUI();
}
this.updateStartButtonVisibility();
this.startUrlCheck();
console.log('✅ UI创建完成');
}
//
lockAllFeatures() {
if (this.autoPlayEnabled) this.stopAutoPlay();
if (this.popupBlockEnabled) this.togglePopupBlock(false);
this.disableUI();
this.showUnauthorizedBanner();
}
//
updateStartButtonVisibility() {
const btn = document.getElementById('qi-start-btn');
if (!btn) return;
btn.style.display = 'flex';
if (this._finished) {
btn.className = 'qi-btn completed';
btn.innerHTML = '✅
已完成
点击重置';
btn.style.background = 'linear-gradient(145deg, #9E9E9E, #616161)';
btn.style.borderColor = '#9E9E9E';
btn.style.boxShadow = 'none';
btn.style.cursor = 'pointer';
} else if (this._started && this._autoMode) {
btn.className = 'qi-btn active';
btn.innerHTML = '🟢
运行中
点击关闭';
btn.style.background = 'linear-gradient(145deg, #4CAF50, #2E7D32)';
btn.style.borderColor = '#4CAF50';
btn.style.boxShadow = '0 0 20px rgba(76,175,80,0.6)';
btn.style.cursor = 'pointer';
} else {
btn.className = 'qi-btn';
btn.innerHTML = '🚀
开始
自动学习';
btn.style.background = 'linear-gradient(145deg, #FF9800, #E65100)';
btn.style.borderColor = '#fff';
btn.style.boxShadow = '0 4px 14px rgba(255,152,0,0.4)';
btn.style.cursor = 'pointer';
}
}
//
startUrlCheck() {
if (this._urlCheckTimer) clearInterval(this._urlCheckTimer);
this._urlCheckTimer = setInterval(() => {
const currentUrl = window.location.href;
if (currentUrl !== this._lastUrl) {
this._lastUrl = currentUrl;
this.parseCourseId();
this.updateStartButtonVisibility();
if (this.isTrainingHomePage() && this._autoMode && !this._finished) {
this._started = true;
this.startHomeDetector();
this.detectAndJump();
}
}
}, 500);
}
//
async onScreenshotClick() {
if (!this.authorized) {
await this.showSwal({ title: '⚠️ 未授权', text: '请先授权', icon: 'warning' });
return;
}
if (typeof html2canvas === 'undefined') {
this.showSwal({ title: '截图库未加载', text: '请刷新页面重试。', icon: 'error' });
return;
}
const uiElements = document.querySelectorAll('.qi-btn, .qi-toggle-wrapper');
const originalDisplays = [];
uiElements.forEach(el => {
originalDisplays.push(el.style.display);
el.style.display = 'none';
});
try {
await sleep(200);
const halfHeight = window.innerHeight / 2;
const canvas = await html2canvas(document.body, {
useCORS: true,
scale: 2,
allowTaint: false,
logging: false,
backgroundColor: '#ffffff',
x: 0,
y: 0,
width: window.innerWidth,
height: halfHeight
});
uiElements.forEach((el, idx) => {
el.style.display = originalDisplays[idx] || '';
});
const dataUrl = canvas.toDataURL('image/png');
const result = await this.showSwal({
title: '📸 截图预览(上半部分)',
html: `
截图范围:视口上半部分(高度 ${Math.round(halfHeight)}px)
`,
showCancelButton: true,
confirmButtonText: '💾 保存图片',
cancelButtonText: '📋 复制图片',
confirmButtonColor: '#4CAF50',
cancelButtonColor: '#2196F3',
reverseButtons: true,
focusConfirm: false,
width: 700,
showCloseButton: true,
closeButtonHtml: '✕',
closeButtonColor: '#aaa'
});
if (result.isConfirmed) {
const link = document.createElement('a');
link.download = `截图分享_${new Date().toISOString().slice(0,19).replace(/[:-]/g, '')}.png`;
link.href = dataUrl;
link.click();
this.showSwal({ title: '✅ 图片已保存', icon: 'success', timer: 1500, showConfirmButton: false });
} else if (result.dismiss === 'cancel') {
try {
const blob = await fetch(dataUrl).then(res => res.blob());
await navigator.clipboard.write([ new ClipboardItem({ [blob.type]: blob }) ]);
this.showSwal({ title: '✅ 图片已复制', text: '已复制到剪贴板,可直接粘贴(Ctrl+V)', icon: 'success', timer: 2000, showConfirmButton: false });
} catch (err) {
this.showSwal({ title: '复制失败', text: '请使用保存功能。', icon: 'error' });
}
}
} catch (err) {
console.error('截图失败:', err);
uiElements.forEach((el, idx) => {
el.style.display = originalDisplays[idx] || '';
});
this.showSwal({ title: '截图失败', text: err.message || '未知错误', icon: 'error' });
}
}
//
async init() {
this.createUI();
if (this.pendingAuth) {
GM_deleteValue('qi_pending_auth');
this.pendingAuth = false;
const ok = await this.requestAuthorization();
if (ok) {
this.enableUI();
this.showSwal({ title: '✅ 授权成功', text: '请再次点击“开始自动学习”以继续学习剩余课程。', icon: 'success' });
} else {
this.disableUI();
this.showUnauthorizedBanner();
}
}
if (this.authorized) {
this.enableUI();
console.log('✅ 已授权');
} else {
this.showUnauthorizedBanner();
this.disableUI();
}
if (typeof ajaxHooker !== 'undefined') {
ajaxHooker.filter([{ url: 'fulls.json' }]);
ajaxHooker.hook(request => {
if (request.url.includes('fulls.json')) {
request.response = res => {
try {
const data = JSON.parse(res.responseText);
this.parseFullDatas(data);
console.log('📦 捕获到 fulls.json');
} catch(e) { console.error('解析 fulls.json 失败', e); }
};
}
});
} else {
const OriginalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new OriginalXHR();
const originalOpen = xhr.open;
const originalSend = xhr.send;
let requestUrl = '';
xhr.open = function(method, url) {
requestUrl = url;
return originalOpen.apply(this, arguments);
};
xhr.send = function(body) {
this.addEventListener('readystatechange', function() {
if (this.readyState === 4 && this.status === 200 && requestUrl.includes('fulls.json')) {
try {
const data = JSON.parse(this.responseText);
if (window._qiInstance) {
window._qiInstance.parseFullDatas(data);
}
console.log('📦 [XHR] 捕获到 fulls.json');
} catch(e) {}
}
});
return originalSend.apply(this, arguments);
};
return xhr;
};
}
this.startKeepAlive();
const pageType = this.getPageType();
console.log('当前页面类型:', pageType);
if (pageType === 'courseIndex') {
if (this.courseId === this.freeCourseId) {
setTimeout(() => {
const btn = document.getElementById('qi-instant-btn');
if (btn) btn.click();
}, 2000);
} else {
if (this.authorized) {
const confirmed = await this.showAutoStartPrompt();
if (confirmed) {
let btn = document.getElementById('qi-instant-btn');
let retries = 0;
while (!btn && retries < 10) {
await sleep(300);
btn = document.getElementById('qi-instant-btn');
retries++;
}
if (btn) btn.click();
else this.showSwal({ title: '按钮未找到', text: '未能找到“一键开刷”按钮,请手动点击。', icon: 'warning', timer: 2000, showConfirmButton: false });
}
}
}
} else if (pageType === 'training' && this.authorized && this._autoMode && !this._finished) {
this._started = true;
this.startHomeDetector();
await this.detectAndJump();
this.updateStartButtonVisibility();
} else if (pageType === 'training' && !this.authorized && this._autoMode) {
this.startHomeDetector();
await this.detectAndJump();
}
setTimeout(() => {
if (!localStorage.getItem('noMoreDialog')) {
this.showGuide(false);
}
}, 3000);
const observer = new MutationObserver(() => {
this.scanVideos();
});
observer.observe(document.body, { childList: true, subtree: true });
console.log('🚀 脚本已启动(免费美育 + 授权后续)');
}
//
getPageType() {
const path = window.location.pathname;
if (path.includes('/training/')) return 'training';
if (path.includes('/courseIndex')) return 'courseIndex';
if (path.includes('/courseDetail')) return 'courseDetail';
return 'other';
}
// ====================
startHomeDetector(){if(this._homeTimer)return;this._homeTimer=setInterval(()=>this.detectAndJump(),1e3);setTimeout(()=>this.detectAndJump(),500)}
stopHomeDetector(){if(this._homeTimer){clearInterval(this._homeTimer);this._homeTimer=null}}
async detectAndJump(){if(!this._autoMode||!this.isTrainingHomePage()||this._detecting)return;this._detecting=true;try{const a=this.scanCourseCards();if(!a||a.length===0){this._detecting=false;return}const b=this.authManager.getStatus();const c=a.find(d=>d.courseId===this.freeCourseId);const e=c&&c.completed;const f=c?c.creditDone:0;const g=e||f>=3;if(!b){if(!g){this.jumpToCourse(this.freeCourseId);this._detecting=false;return}else{if(!this._freePromptShown){this._freePromptShown=true;this.stopHomeDetector();this._autoMode=false;this._started=false;GM_setValue('qi_auto_mode',false);await this.requestAuthorization();if(this.authorized)location.reload()}this._detecting=false;return}}if(g){GM_setValue('qi_free_done',true)}const h=GM_getValue('qi_free_done',false);if(h||g){const i=COURSE_ORDER.filter(j=>j!==this.freeCourseId);const k=i.every(j=>{const l=a.find(m=>m.courseId===j);return l&&l.completed});if(k){this.finishStudy();this._detecting=false;return}for(const j of i){const l=a.find(m=>m.courseId===j);if(!l||!l.completed){if(this._lastJumpedCourse===j&&(Date.now()-this._lastJumpTime)<30000)continue;this.jumpToCourse(j);this._detecting=false;return}}if(i.every(j=>{const l=a.find(m=>m.courseId===j);return l&&l.completed})){this.finishStudy()}this._detecting=false;return}if(!g){this.jumpToCourse(this.freeCourseId);this._detecting=false;return}}catch(n){console.error('检测出错:',n)}finally{this._detecting=false}}
scanCourseCards(){const a=document.querySelector('div[class*="index-module_items_"]');if(!a)return null;const b=a.children;const c=[];for(let d of b){const e=this.getCourseIdFromDom(d);if(!e||!ALL_COURSE_IDS.includes(e))continue;const f=this.extractCreditInfo(d);if(!f)continue;const{g,h}=f;const i=g>=h;c.push({courseId:e,creditDone:g,creditRequired:h,completed:i})}return c}
getCourseIdFromDom(a){const b=a.querySelector('a[href*="courseId"]');if(b){const c=b.href.match(/courseId=([^&]+)/);if(c)return c[1]}const d=a.getAttribute('data-course-id');if(d)return d;const e=Object.keys(a).find(f=>f.startsWith('__reactFiber'));if(e){let g=a[e];while(g){if(g.key&&ALL_COURSE_IDS.includes(g.key))return g.key;g=g.return}}return null}
extractCreditInfo(a){const b=a.innerText||'';let c=b.match(/已认定\s*([\d.]+)\s*\/\s*认定\s*([\d.]+)\s*学时/);if(c){return{done:parseFloat(c[1]),required:parseFloat(c[2])}}c=b.match(/已学习\s*([\d.]+)\s*学时/);if(c){return{done:parseFloat(c[1]),required:3}}c=b.match(/已学习\s*[\d.]+\s*已认定\s*([\d.]+)\s*\/\s*认定\s*([\d.]+)\s*学时/);if(c){return{done:parseFloat(c[1]),required:parseFloat(c[2])}}return null}
finishStudy(){if(this._finished)return;this._finished=true;GM_setValue('qi_auto_mode',false);this._autoMode=false;this.stopHomeDetector();if(this.autoPlayEnabled)this.stopAutoPlay();const a='🎊 恭喜您完成 2026 年暑期教师研修培训,祝你假期愉快! 🎊';this.Swal.fire({title:'🎉 研修完成!',html:`您已按顺序完成全部课程的学习任务,获得相应学时。
本次研修圆满结束,祝您假期愉快!
`,icon:'success',showConfirmButton:false,showCancelButton:false,background:'linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)',backdrop:'rgba(0,0,0,0.5)',width:600,padding:'2em',showClass:{popup:'animate__animated animate__fadeInDown'},hideClass:{popup:'animate__animated animate__fadeOutUp'},didOpen:()=>{document.getElementById('qi-thanks-btn').addEventListener('click',()=>{window.open('https://ibb.co/gFg4YYLx','_blank');this.Swal.close()})}}).then(()=>{this.updateStartButtonVisibility()})}
jumpToCourse(a){this._lastJumpedCourse=a;this._lastJumpTime=Date.now();window.location.href=`https://basic.smartedu.cn/teacherTraining/courseIndex?courseId=${a}`}
checkCreditCompletion(){if(this.targetSeconds>0&&this.stats.totalDuration>=this.targetSeconds){if(this.courseId===this.freeCourseId){GM_setValue('qi_free_done',true);this.stopAutoPlay();if(this.authorized){this.showSwal({title:'🎉 学科美育板块已完成',text:'即将返回研修主页,继续学习其余课程。',icon:'success',timer:2000,showConfirmButton:false}).then(()=>{window.location.href='https://basic.smartedu.cn/training/2026jjsqpx'})}else{this.stopHomeDetector();this._autoMode=false;this._started=false;GM_setValue('qi_auto_mode',false);this.authManager.clearAuth();GM_setValue('qi_pending_auth',true);this.showSwal({title:'🎉 免费体验结束',text:'您已完成“学科美育”3学时学习,如需继续其余课程,请获取授权码。',icon:'info',timer:2000,showConfirmButton:false}).then(()=>{window.location.href='https://basic.smartedu.cn/training/2026jjsqpx'})}return}else{this.stopAutoPlay();this.showSwal({title:'🎉 板块学时已达标',text:`已完成 ${(this.stats.totalDuration/2700).toFixed(3)} / ${this.targetCredit} 学时,即将返回研修主页。`,icon:'success',timer:3000,showConfirmButton:false}).then(()=>{if(this.popupBlockEnabled)this.togglePopupBlock(false);window.location.href='https://basic.smartedu.cn/training/2026jjsqpx'})}}}
async expandAllMenus(a=30){let b=0;while(bd.click());b++;await sleep(1200)}}
async startAutoPlay(){if(this.autoPlayEnabled)return;if(!this.authorized&&this.courseId!==this.freeCourseId)return;this.autoPlayEnabled=true;this.isPlaying=false;this.resetStats();if(!this.isTrainingHomePage()){this.togglePopupBlock(true)}const a=document.getElementById('qi-auto-toggle');if(a)a.checked=true;document.getElementById('qi-auto-status').textContent='开';await this.expandAllMenus();this.scanVideos();this.playFirstUnfinished()}
async doubleStartAutoPlay(){if(!this.authorized&&this.courseId!==this.freeCourseId)return;if(this.autoPlayEnabled){this.stopAutoPlay();await sleep(300)}await this.startAutoPlay();await sleep(800);this.stopAutoPlay();await sleep(300);await this.startAutoPlay()}
stopAutoPlay(){this.autoPlayEnabled=false;if(!this.isTrainingHomePage()){this.togglePopupBlock(false)}const a=document.getElementById('qi-auto-toggle');if(a)a.checked=false;document.getElementById('qi-auto-status').textContent='关'}
toggleAutoPlay(a){if(!this.authorized&&this.courseId!==this.freeCourseId){this.showSwal({title:'⚠️ 未授权',text:'请先授权',icon:'warning'});return}if(a)this.doubleStartAutoPlay();else this.stopAutoPlay()}
playFirstUnfinished(){let a=document.querySelectorAll('.resource-item.resource-item-train');if(!a||a.length===0){a=document.querySelectorAll('.video-item, .course-video-list li, [class*="video"]')}if(!a||a.length===0){setTimeout(()=>this.playFirstUnfinished(),3000);return}const b=this.courseId===SCIENCE_COURSE_ID;let c=b?SCIENCE_SKIP_COUNT:0;if(b&&a.length<=SCIENCE_SKIP_COUNT)c=0;for(let d=c;d{if(!this.processedVideos.has(a)){this.autoJumpVideo(a)}})}
autoJumpVideo(a){if(this.processedVideos.has(a))return;this.processedVideos.add(a);if(a.readyState<2){a.addEventListener('loadedmetadata',()=>this.doJump(a),{once:true})}else{this.doJump(a)}}
doJump(a){const b=a.duration;if(!b||isNaN(b)||b===Infinity){setTimeout(()=>{if(a.duration&&isFinite(a.duration))this.doJump(a)},500);return}if(b<10){let c=this.generateVideoId(a);let d=this.getVideoName(a,c);if(!c)c=`vid_${b}_${Date.now()}_${Math.random().toString(36).substr(2,6)}`;if(!this.isVideoCompleted(c)){this.addCompletedVideo(c,b,d)}setTimeout(()=>{this.playNextVideo()},1500);return}const e=Math.max(0,b-3);a.currentTime=e;a.muted=true;a.playbackRate=2;a.play().catch(()=>{});if(!a._pauseIntercepted){const f=a.pause;a.pause=function(){if(this.currentTime{});return}return f.apply(this,arguments)};a._pauseIntercepted=true}let c=this.generateVideoId(a);let d=this.getVideoName(a,c);if(!c)c=`vid_${b}_${Date.now()}_${Math.random().toString(36).substr(2,6)}`;if(this.isVideoCompleted(c))return;let g=false;let h=null;const i=()=>{if(h)clearTimeout(h);h=setTimeout(()=>{if(g)return;g=true;a.removeEventListener('ended',j);a.removeEventListener('timeupdate',j);const k=Math.min(a.currentTime+3,b);this.addCompletedVideo(c,k,d);setTimeout(()=>{this.playNextVideo()},1500)},7000)};const j=()=>{if(g)return;const k=a.duration-a.currentTime;if(k<=1.0||a.ended){g=true;if(h)clearTimeout(h);a.removeEventListener('ended',j);a.removeEventListener('timeupdate',j);this.addCompletedVideo(c,b,d);setTimeout(()=>{this.playNextVideo()},3000)}};a.addEventListener('ended',j);a.addEventListener('timeupdate',j);i()}
getVideoName(a,b){let c=null;if(b&&this.resourceMap.has(b)){c=this.resourceMap.get(b).name}else{let d=a.closest('[data-title], .video-title, .course-name, [class*="title"]');if(d){c=d.dataset.title||d.innerText.trim()||null}else{const e=document.querySelector('.video-title, .course-name, h1, h2');if(e)c=e.innerText.trim()}if(!c)c='未命名视频'}return c}
generateVideoId(a){let b=a.closest('[data-resource-id]');if(b){const c=b.dataset.resourceId;if(c&&this.resourceMap.has(c))return c}let d=a.src||a.currentSrc||'';if(d&&!d.startsWith('blob:')){const e=d.match(/[\/?]resourceId=([^&]+)/);if(e)return e[1]}try{if(unsafeWindow.p&&unsafeWindow.p.itemId){const e=unsafeWindow.p.itemId;if(this.resourceMap.has(e))return e}}catch(f){}const g=new URLSearchParams(location.search);if(g.get('videoId')){const e=g.get('videoId');if(this.resourceMap.has(e))return e}return null}
playNextVideo(){const a=["下一节","下一课","下一集","下一页","next","下一章"];const b=document.querySelectorAll('button, a, div, span');for(let c of b){const d=c.innerText.trim();if(a.some(e=>d.includes(e))&&c.offsetWidth>0&&!c.disabled){c.click();return true}}const f=document.querySelectorAll('.resource-item.resource-item-train');let g=false;for(let h=0;h10&&a.currentTime<5&&!a.ended){a.currentTime=Math.max(0,a.duration-3);a.play().catch(()=>{})}if(a.paused&&!a.ended)a.play().catch(()=>{});if(!a._pauseIntercepted){const b=a.pause;a.pause=function(){if(this.currentTime{});return}return b.apply(this,arguments)};a._pauseIntercepted=true}}
startKeepAlive(){if(this.keepAliveTimer)clearInterval(this.keepAliveTimer);this.keepAliveTimer=setInterval(()=>this.maintainVideo(),1500)}
resetStats(){this.stats.completedVideos=[];this.stats.totalDuration=0;this.stats.totalCount=0;this.creditCompleted=0}
isVideoCompleted(a){return this.stats.completedVideos.includes(a)}
addCompletedVideo(a,b,c){if(this.isVideoCompleted(a))return false;this.stats.completedVideos.push(a);this.stats.totalDuration+=b;this.stats.totalCount++;this.creditCompleted=this.stats.totalDuration/2700;this.showDanmaku(c,b);this.checkCreditCompletion();return true}
showDanmaku(a,b){const c=this.stats.totalDuration;const d=Math.floor(c/60);const e=Math.floor(d/60);let f=e>0?`${e}小时${d%60}分钟`:`${d}分钟`;const g=this.creditCompleted;const h=this.targetCredit;const i=h>0?`${g.toFixed(3)}/${h} 学时`:`${g.toFixed(3)} 学时`;const j=document.createElement('div');j.style.cssText='position:fixed;right:-650px;top:20%;transform:translateY(-50%);background:rgba(0,0,0,0.88);color:#fff;padding:18px 28px;border-radius:14px;font-family:\'Microsoft Yahei\';font-size:17px;line-height:1.8;z-index:9999999;box-shadow:0 6px 28px rgba(0,0,0,0.6);border-left:6px solid #4CAF50;white-space:nowrap;transition:right 0.7s cubic-bezier(0.34,1.56,0.64,1);pointer-events:none;user-select:none;letter-spacing:0.5px;';j.innerHTML=`✅已完成:${a}
累计看时 ${f} | 约 ${i} | 已看 ${this.stats.totalCount} 个视频
`;document.body.appendChild(j);setTimeout(()=>{j.style.right='20px'},150);setTimeout(()=>{j.style.right='-650px';setTimeout(()=>{if(j.parentNode)j.remove()},700)},5500)}
async showAutoStartPrompt(){return new Promise((a)=>{let b=null;let c=3;const d='qi-countdown';const e=`是否自动开启一键开刷功能?
将在 3 秒后自动开启
(需要时可自动开启)
`;this.Swal.fire({title:'即将开启一键开刷功能',html:e,confirmButtonText:'好的',cancelButtonText:'暂时不需要',showCancelButton:true,reverseButtons:true,focusConfirm:false,allowOutsideClick:false,allowEscapeKey:false,willOpen:()=>{const f=document.getElementById(d);const g=setInterval(()=>{c--;if(f)f.textContent=c;if(c<=0){clearInterval(g);this.Swal.clickConfirm()}},1000);b=g},willClose:()=>{if(b)clearInterval(b)}}).then((f)=>{a(f.isConfirmed)})})}
// ========== API 请求与上报 ==========
//
parseFullDatas(data) {
this.fullDatas = data;
this.resourceMap.clear();
if (!data || !data.nodes) return;
const traverse = (node) => {
if (node.node_type === 'catalog' && node.child_nodes) {
node.child_nodes.forEach(child => traverse(child));
} else if (node.node_type === 'activity') {
const resources = node.relations?.activity?.activity_resources || [];
resources.forEach(resource => {
const resId = resource.resource_id;
if (resId) {
this.resourceMap.set(resId, {
name: node.node_name || '未命名课程',
duration: resource.study_time || 0
});
}
});
}
};
data.nodes.forEach(node => traverse(node));
}
//
getResourceList() {
const result = [];
for (let [id, info] of this.resourceMap) {
result.push({ resource_id: id, name: info.name, studyTime: info.duration });
}
return result;
}
//
getDynamicToken() {
try {
const pattern = /^ND_UC_AUTH-([0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12})&ncet-xedu&token$/;
for (let key of Object.keys(localStorage)) {
if (pattern.test(key)) {
return {
key: key,
appId: key.match(pattern)[1],
token: JSON.parse(JSON.parse(localStorage.getItem(key)).value)
};
}
}
throw Error("未找到登录 Token");
} catch (err) {
throw Error("获取 Token 失败: " + err.message);
}
}
//
getMACAuthorizationHeaders(url, method) {
let n = this.getDynamicToken().token;
return this.He(url, method, {
accessToken: n.access_token,
macKey: n.mac_key,
diff: n.diff
});
}
//
Ze(e) {
var t = "0123456789ABCDEFGHIJKLMNOPQRTUVWXZYS".split("");
var n = "";
for (var r = 0; r < e; r++) n += t[Math.floor(t.length * Math.random())];
return n;
}
//
Fe(e) {
return (new Date).getTime() + parseInt(e, 10) + ":" + this.Ze(8);
}
//
ze(e, t, n, r) {
let o = {
relative: new URL(e).pathname,
authority: new URL(e).hostname
};
let i = t + "\n" + n.toUpperCase() + "\n" + o.relative + "\n" + o.authority + "\n";
return CryptoJS.HmacSHA256(i, r).toString(CryptoJS.enc.Base64);
}
//
He(e) {
let t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "GET",
n = arguments.length > 2 ? arguments[2] : void 0,
r = n.accessToken,
o = n.macKey,
i = n.diff,
s = this.Fe(i),
a = this.ze(e, s, t, o);
return 'MAC id="'.concat(r, '",nonce="').concat(s, '",mac="').concat(a, '"');
}
//
async setProgressWithRetry(url, duration, retries = 3) {
let lastError;
let delay = 500;
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const result = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: url,
method: 'PUT',
headers: {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"authorization": this.getMACAuthorizationHeaders(url, 'PUT'),
"cache-control": "no-cache",
"pragma": "no-cache",
"content-type": "application/json",
"sdp-app-id": this.getDynamicToken().appId,
"sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
"host": "x-study-record-api.ykt.eduyun.cn",
"origin": "https://basic.smartedu.cn",
"referer": "https://basic.smartedu.cn/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"
},
data: JSON.stringify({ position: Math.max(0, duration - 3) }),
onload: function (res) {
if (res.status === 200) resolve(res);
else reject(new Error('服务器返回 ' + res.status));
},
onerror: function (err) { reject(new Error('请求失败: ' + err)); }
});
});
console.log(`✅ 刷课成功 (尝试 ${attempt})`);
return result;
} catch (err) {
lastError = err;
console.warn(`⏳ 请求失败 (尝试 ${attempt}/${retries}),${err.message}`);
if (attempt < retries) {
await sleep(delay);
delay *= 2;
}
}
}
throw new Error(`重试 ${retries} 次后仍失败: ${lastError.message}`);
}
//
async onInstantClick() {
if (!this.authorized && this.courseId !== this.freeCourseId) {
this.showSwal({ title: '⚠️ 未授权', text: '请先完成免费课程或输入授权码', icon: 'warning' });
return;
}
if (this.isProcessing) {
this.showSwal({ title: '操作进行中', text: '正在刷课中,请勿重复点击!', icon: 'warning' });
return;
}
try {
this.isProcessing = true;
const btn = document.getElementById('qi-instant-btn');
if (btn) { btn.disabled = true; btn.innerHTML = '刷课中...'; }
this.resetStats();
let resources = this.getResourceList();
let waitCount = 0;
while (resources.length === 0 && waitCount < 10) {
await sleep(500);
resources = this.getResourceList();
waitCount++;
}
if (resources.length === 0) {
this.showSwal({ title: '未找到视频资源', text: '请确保已进入课程详情页并刷新页面。', icon: 'info' });
return;
}
const totalDuration = resources.reduce((acc, item) => acc + (item.studyTime || 0), 0);
const totalCredit = totalDuration / 2700;
const totalMinutes = Math.floor(totalDuration / 60);
const totalHours = Math.floor(totalMinutes / 60);
let totalTimeStr = '';
if (totalHours > 0) totalTimeStr = `${totalHours}小时${totalMinutes % 60}分钟`;
else totalTimeStr = `${totalMinutes}分钟`;
const token = this.getDynamicToken();
const allResults = [];
for (let i = 0; i < resources.length; i++) {
const item = resources[i];
console.log(`⏳ 处理 (${i+1}/${resources.length}): ${item.name}`);
try {
const url = "https://x-study-record-api.ykt.eduyun.cn/v1/resource_learning_positions/" + item.resource_id + '/' + token.token.user_id;
await this.setProgressWithRetry(url, item.studyTime, 3);
allResults.push({ name: item.name, status: 'success', duration: item.studyTime });
} catch (err) {
console.error(`${item.name} 失败!`, err);
allResults.push({ name: item.name, status: 'fail', error: err.message, duration: item.studyTime });
}
}
const successCount = allResults.filter(r => r.status === 'success').length;
const failCount = allResults.filter(r => r.status === 'fail').length;
const listHtml = allResults.map(result => {
const dur = result.duration || 0;
const minutes = Math.floor(dur / 60);
const seconds = Math.round(dur % 60);
const durationStr = `${minutes}分${seconds}秒`;
const icon = result.status === 'success' ? '✅' : '❌';
const errorMsg = result.error ? ` — ${result.error}` : '';
return `
${icon} ${result.name}
(${durationStr})
${errorMsg}
`;
}).join('');
const result = await this.showSwal({
title: '✅ 本板块刷课完成',
html: `
视频总数:${resources.length}
成功:${successCount} 个
失败:${failCount} 个
板块总时长:${totalTimeStr}
📊 板块总预估学时:${totalCredit.toFixed(3)} 学时
⏳ 1.5秒后即将开启自动轮播...
`,
icon: successCount === resources.length ? 'success' : 'warning',
confirmButtonText: '好的',
cancelButtonText: '我不需要',
showCancelButton: true,
reverseButtons: true,
focusConfirm: false,
customClass: { confirmButton: 'qi-swal-confirm-btn' },
willOpen: () => {
setTimeout(() => {
this.Swal.clickConfirm();
}, 1500);
}
});
if (result.isConfirmed) {
await this.doubleStartAutoPlay();
}
} catch (e) {
console.error(e);
this.showSwal({ title: '刷课失败', text: e.message || '异常,请刷新重试。', icon: 'error' });
} finally {
this.isProcessing = false;
const btn = document.getElementById('qi-instant-btn');
if (btn) { btn.disabled = false; btn.innerHTML = '一键开刷'; }
}
}
}
//
// ============================================================
// 配置常量(课程ID、学分映射等)
// ============================================================
//
const POPUP_BLOCK_KEY = 'qi_popup_block_enabled';
const COURSE_ORDER = [
'6a46308e-17ff-456a-a320-b5bdb494e93d',
'79d28f6c-0b8a-443d-bdda-a78165c462f3',
'165d9433-5486-43e4-926e-3f33b92635e4',
'b2ec4202-c747-4ad2-a1b1-35a6c277c856',
'ca2a448a-5ff5-4183-92c2-6506f6696228'
];
const ALL_COURSE_IDS = COURSE_ORDER;
const COURSE_CREDIT_MAP = {
'6a46308e-17ff-456a-a320-b5bdb494e93d': 3,
'79d28f6c-0b8a-443d-bdda-a78165c462f3': 2,
'165d9433-5486-43e4-926e-3f33b92635e4': 3,
'b2ec4202-c747-4ad2-a1b1-35a6c277c856': 1,
'ca2a448a-5ff5-4183-92c2-6506f6696228': 1
};
const SCIENCE_COURSE_ID = 'b2ec4202-c747-4ad2-a1b1-35a6c277c856';
const SCIENCE_SKIP_COUNT = 6;
const FREE_COURSE_ID = '6a46308e-17ff-456a-a320-b5bdb494e93d';
//
// ============================================================
// 启动代码(IIFE 调用)
// ============================================================
//
(async function() {
const instance = new SmartEduModule();
instance.authManager.initOnPageLoad();
await instance.init();
})();
//