// ==UserScript==
// @name 湖南开放大学自动学习
// @namespace hnsydwpx.auto.study
// @version 2.1.0
// @description 湖南开放大学自动刷课:登录课程页后右下角出现控制面板,自动静音播放,一门学到 100% 自动进入下一门,支持后台挂机与桌面置顶小窗。普通版可完整学完 2 门课程。
// @author maple
// @match https://www.hnsydwpx.cn/*
// @match https://hnsydwpx.cn/*
// @run-at document-start
// @grant none
// @tag 自动刷课
// @tag 湖南开放大学
// @tag 网课
// @tag 后台播放
// @tag 自动切换课程
// @antifeature payment 完整版需VIP密钥解锁;普通版可完整学完 2 门课程
// ==/UserScript==
(function () {
const hnsPi = [];
const hnsPj = [];
'use strict';
const License = (function () {
'use strict';
const MASTER_KEY_ID = "7020f6e2c65f7e90";
function hnsSh(n) {
const out = [];
let x = 0x2f6e2b1;
while (out.length < n) {
x ^= (x << 13) & 0xffffffff; x = x >>> 0;
x ^= (x >>> 17); x = x >>> 0;
x ^= (x << 5) & 0xffffffff; x = x >>> 0;
out.push(x & 0xff);
}
return out;
}
const B36 = '0123456789abcdefghijklmnopqrstuvwxyz';
function hnsDp(piece) {
let str = '';
for (let i = 0; i < piece.length; i += 4) {
const n = (B36.indexOf(piece[i]) * 32768) + (B36.indexOf(piece[i + 1]) * 1024) +
(B36.indexOf(piece[i + 2]) * 32) + B36.indexOf(piece[i + 3]);
str += String.fromCharCode((n >> 8) & 0xff) + String.fromCharCode(n & 0xff);
}
const s = hnsSh(str.length);
let hex = '';
for (let i = 0; i < str.length; i++) hex += (str.charCodeAt(i) ^ s[i]).toString(16);
return hex;
}
const hnsGot = new Map();
let hnsTotal = -1;
let hnsCache = null;
let hnsDiag = null;
function hnsAbsorb() {
hnsGot.clear();
for (let i = 0; i + 1 < hnsPi.length; i++) {
const k = hnsPi[i];
if (typeof k !== 'number') continue;
const v = hnsPi[i + 1];
if (typeof v === 'string') hnsGot.set(k, v);
}
}
function hnsPool() {
if (hnsCache) return hnsCache;
hnsAbsorb();
if (!hnsGot.size) return [];
const idx = Array.from(hnsGot.keys()).sort(function (a, b) { return a - b; });
for (let i = 0; i < idx.length; i++) {
if (idx[i] !== i) { hnsDiag = { stage: 'gap', at: i }; return []; }
}
let hexAll = '';
for (let i = 0; i < idx.length; i++) hexAll += hnsDp(hnsGot.get(idx[i]));
if (!hexAll || hexAll.length % 16 !== 0) {
hnsDiag = { stage: 'length', hex: hexAll.length };
return [];
}
const out = [];
for (let i = 0; i < hexAll.length; i += 16) out.push(hexAll.slice(i, i + 16));
for (let i = 0; i < out.length; i++) {
if (!/^[0-9a-f]{16}$/.test(out[i])) {
hnsDiag = { stage: 'format', at: i, val: String(out[i]) };
return [];
}
}
hnsDiag = { stage: "done", count: out.length };
hnsCache = out;
return hnsCache;
}
const STORE = {
bound: 'hnsydwpx_key_bound',
used: 'hnsydwpx_key_used'
};
const LS_PREFIX = 'hnsydwpx.';
const hasGM = (function () {
try { return typeof GM_setValue === 'function' && typeof GM_getValue === 'function'; }
catch (e) { return false; }
})();
function lsGet(k) {
try {
const v = localStorage.getItem(LS_PREFIX + k);
return v === null ? undefined : v;
} catch (e) { return undefined; }
}
function lsSet(k, v) {
try { localStorage.setItem(LS_PREFIX + k, String(v)); return true; }
catch (e) { return false; }
}
function lsDel(k) {
try { localStorage.removeItem(LS_PREFIX + k); } catch (e) { /* 忽略 */ }
}
function cookieGet(k) {
try {
const name = LS_PREFIX + k + '=';
const parts = String(document.cookie || '').split(';');
for (let i = 0; i < parts.length; i++) {
const s = parts[i].replace(/^\s+/, '');
if (s.indexOf(name) === 0) {
try { return decodeURIComponent(s.slice(name.length)); }
catch (e) { return s.slice(name.length); }
}
}
} catch (e) { /* 忽略 */ }
return undefined;
}
function cookieSet(k, v) {
try {
const host = String(location.hostname || '').replace(/^www\./i, '');
const domain = host ? ('; domain=.' + host) : '';
const expires = new Date(Date.now() + 3650 * 24 * 3600 * 1000).toUTCString();
document.cookie = LS_PREFIX + k + '=' + encodeURIComponent(String(v)) +
domain + '; path=/; expires=' + expires + '; SameSite=Lax';
return cookieGet(k) !== undefined;
} catch (e) { return false; }
}
function cookieDel(k) {
try {
const host = String(location.hostname || '').replace(/^www\./i, '');
const domain = host ? ('; domain=.' + host) : '';
document.cookie = LS_PREFIX + k + '=;' + domain + '; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT';
} catch (e) { /* 忽略 */ }
}
function gmv(k, d) {
const fromCookie = cookieGet(k);
if (fromCookie !== undefined && fromCookie !== '') return fromCookie;
if (hasGM) {
try { const v = GM_getValue(k, d); if (v !== undefined && v !== null && v !== '') return v; }
catch (e) { /* 继续 */ }
}
const v = lsGet(k);
return v === undefined ? d : v;
}
function smv(k, v) {
const okCookie = cookieSet(k, v);
const okLs = lsSet(k, v);
let okGM = false;
if (hasGM) {
try { GM_setValue(k, v); okGM = true; } catch (e) { /* 忽略 */ }
}
return okCookie || okLs || okGM;
}
function delv(k) {
cookieDel(k);
lsDel(k);
if (hasGM) { try { GM_deleteValue(k); } catch (e) { /* 忽略 */ } }
}
const log = (...a) => { try { console.log(PREFIX, ...a); } catch (e) { /* 忽略 */ } };
const state = {
ready: false,
vip: false,
master: false,
key: '',
keyId: '',
account: '',
reason: 'NO_KEY'
};
function getAccount() {
try {
const raw = localStorage.getItem('user-info');
if (raw) {
const info = JSON.parse(raw);
const u = info && (info.user_info || info.userInfo);
if (u && u.username && u.id) return String(u.username) + '_' + String(u.id);
if (u && u.id) return 'uid_' + String(u.id);
}
} catch (e) { /* 忽略 */ }
let a = gmv('hnsydwpx_local_account', '');
if (!a) { a = 'local_' + Math.random().toString(36).slice(2, 10); smv('hnsydwpx_local_account', a); }
return a;
}
function normalizeKey(k) {
return String(k || '').trim().toUpperCase();
}
function isFormat(k) {
return /^[A-Z0-9]{4}(-[A-Z0-9]{4}){3}$/.test(normalizeKey(k));
}
function fnv1a(str, seed, mul) {
let h = seed >>> 0;
for (let i = 0; i < str.length; i++) {
const c = str.charCodeAt(i);
h = ((h ^ c) * mul) >>> 0;
}
return ('0000000' + h.toString(16)).slice(-8);
}
function ownerIdOf(licenseKey) {
const raw = 'hnsydwpx-v1|' + normalizeKey(licenseKey);
return fnv1a(raw, 0x811c9dc5, 0x01000193) + fnv1a(raw, 0xc9dc5118, 0x00100193);
}
function loadBound() {
try { return JSON.parse(gmv(STORE.bound, '{}')) || {}; } catch (e) { return {}; }
}
function saveBound(map) { smv(STORE.bound, JSON.stringify(map)); }
function loadUsed() {
try { return JSON.parse(gmv(STORE.used, '[]')) || []; } catch (e) { return []; }
}
function markUsed(id) {
const used = loadUsed();
if (used.indexOf(id) === -1) { used.push(id); smv(STORE.used, JSON.stringify(used)); }
}
function activate(key) {
state.account = getAccount();
const k = normalizeKey(key);
if (!isFormat(k)) return { ok: false, reason: 'FORMAT' };
hnsPool();
const id = ownerIdOf(k);
if (id === MASTER_KEY_ID) {
state.vip = true;
state.master = true;
state.key = k;
state.keyId = id;
state.reason = 'MASTER';
state.ready = true;
smv('hnsydwpx_license_key', k);
return { ok: true, reason: 'MASTER', master: true };
}
const pool = hnsPool();
if (pool.indexOf(id) === -1) return { ok: false, reason: 'NOT_FOUND' };
const bound = loadBound();
const owner = bound[id];
if (owner && owner !== state.account) return { ok: false, reason: 'BOUND_OTHER', owner: owner };
if (!owner) { bound[id] = state.account; saveBound(bound); }
markUsed(id);
state.vip = true;
state.master = false;
state.key = k;
state.keyId = id;
state.reason = 'OK';
state.ready = true;
smv('hnsydwpx_license_key', k);
return { ok: true, reason: 'OK' };
}
function check() {
state.account = getAccount();
const saved = normalizeKey(gmv('hnsydwpx_license_key', ''));
state.ready = true;
if (!saved) { state.vip = false; state.reason = 'NO_KEY'; return state; }
const r = activate(saved);
if (!r.ok) { state.vip = false; state.reason = r.reason; }
return state;
}
function deactivate() {
state.vip = false; state.master = false; state.key = ''; state.keyId = ''; state.reason = 'NO_KEY';
delv('hnsydwpx_license_key');
return state;
}
function tierText() {
if (state.vip) {
return state.master ? ('完整版已激活(万能密钥 ' + state.key + ')')
: ('完整版已激活(密钥 ' + maskKey(state.key) + ')');
}
if (state.reason === 'NO_KEY') return '免费版:未激活';
if (state.reason === 'BOUND_OTHER') return '该密钥已绑定其他账号(' + state.account + ')';
if (state.reason === 'FORMAT') return '密钥格式不正确';
return '密钥无效,请核对后重试';
}
function maskKey(k) {
const s = String(k || '');
return s.length > 8 ? (s.slice(0, 4) + '-****-****-' + s.slice(-4)) : s;
}
function usedIds() { return loadUsed(); }
function totalKeys() { return hnsPool().length; }
return {
state: state,
check: check,
activate: activate,
deactivate: deactivate,
tierText: tierText,
ownerIdOf: ownerIdOf,
isFormat: isFormat,
normalizeKey: normalizeKey,
getAccount: getAccount,
isVip: function () { return !!state.vip; },
isMaster: function () { return !!state.master; },
usedIds: usedIds,
totalKeys: function () { return hnsPool().length; },
keyPool: function () { return hnsPool(); },
diag: function () { return hnsDiag; },
masterId: MASTER_KEY_ID
};
})();
const FREE_TIER_LIMIT = 2;
const BUY_TEXT = '15元获取密钥,解锁全部功能(一个密钥限一台设备登录),如需购买请添加管理员QQ:3113975277';
const CONFIG = {
MUTED: true,
BLOCK_VISIBILITY: true,
PLAY_STALL_SECONDS: 120,
PROGRESS_STUCK_SECONDS: 600,
DEBUG_FAST_FINISH: false,
FULL_TICK_MS: 2000,
DESKTOP_MIN_WIDTH: 300,
DESKTOP_MAX_WIDTH: 420,
DESKTOP_HEIGHT: 240
};
const STORAGE_KEY = 'hnsydwpx_auto_study_finished';
const PREFIX = '[自动刷课]';
let backgroundMode = CONFIG.BLOCK_VISIBILITY;
(function installBackgroundMode() {
const docProto = Document.prototype;
const origHidden = Object.getOwnPropertyDescriptor(docProto, 'hidden');
const origVisibility = Object.getOwnPropertyDescriptor(docProto, 'visibilityState');
const origHasFocus = docProto.hasFocus;
const origWebkitHidden = Object.getOwnPropertyDescriptor(docProto, 'webkitHidden');
const origWebkitVisibility = Object.getOwnPropertyDescriptor(docProto, 'webkitVisibilityState');
const realHidden = () => {
try { return origHidden && origHidden.get ? origHidden.get.call(document) : true; } catch (e) { return true; }
};
const realVisibility = () => {
try { return origVisibility && origVisibility.get ? origVisibility.get.call(document) : 'visible'; } catch (e) { return 'visible'; }
};
const realWebkitHidden = () => {
try { return origWebkitHidden && origWebkitHidden.get ? origWebkitHidden.get.call(document) : true; } catch (e) { return true; }
};
const realWebkitVisibility = () => {
try { return origWebkitVisibility && origWebkitVisibility.get ? origWebkitVisibility.get.call(document) : 'visible'; } catch (e) { return 'visible'; }
};
function defineProp(obj, name, getter) {
try { Object.defineProperty(obj, name, { configurable: true, get: getter }); } catch (e) { /* 忽略 */ }
}
defineProp(docProto, 'hidden', () => backgroundMode ? false : realHidden());
defineProp(docProto, 'visibilityState', () => backgroundMode ? 'visible' : realVisibility());
defineProp(docProto, 'webkitHidden', () => backgroundMode ? false : realWebkitHidden());
defineProp(docProto, 'webkitVisibilityState', () => backgroundMode ? 'visible' : realWebkitVisibility());
defineProp(document, 'hidden', () => backgroundMode ? false : realHidden());
defineProp(document, 'visibilityState', () => backgroundMode ? 'visible' : realVisibility());
defineProp(document, 'webkitHidden', () => backgroundMode ? false : realWebkitHidden());
defineProp(document, 'webkitVisibilityState', () => backgroundMode ? 'visible' : realWebkitVisibility());
try {
docProto.hasFocus = function () { return backgroundMode ? true : origHasFocus.call(this); };
} catch (e) { /* 忽略 */ }
function blockEvent(e) {
if (!backgroundMode) return;
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
if (e.stopPropagation) e.stopPropagation();
}
document.addEventListener('visibilitychange', blockEvent, true);
document.addEventListener('pagehide', blockEvent, true);
document.addEventListener('pageshow', blockEvent, true);
window.addEventListener('blur', blockEvent, true);
window.addEventListener('pagehide', blockEvent, true);
})();
const lic = { ready: false, vip: false, blocked: false, reason: '', remain: FREE_TIER_LIMIT, account: '' };
function updateLicenseCard() {
if (!panel) return;
const card = panel.querySelector('.hns-license');
if (!card) return;
let text;
if (lic.vip) {
text = License.isMaster()
? '✅ 完整版已激活(万能密钥·不限账号)'
: '✅ 完整版已激活(不限课程数)';
} else if (lic.blocked) {
text = '⛔ 免费额度已用尽(' + lic.remain + '/' + FREE_TIER_LIMIT + ' 门),点右上角「激活完整版」';
} else {
text = '免费版:还可完成 ' + lic.remain + ' 门课程(共 ' + FREE_TIER_LIMIT + ' 门) 账号 ' + lic.account;
}
card.textContent = text;
card.title = text;
panel.classList.toggle('is-vip', !!lic.vip);
panel.classList.toggle('is-blocked', !!(!lic.vip && lic.blocked));
}
function refreshLocalQuota() {
const used = finished.length;
lic.remain = Math.max(0, FREE_TIER_LIMIT - used);
lic.blocked = !lic.vip && lic.remain <= 0;
updateLicenseCard();
}
function absorbLicenseState() {
lic.ready = true;
lic.vip = License.isVip();
lic.reason = License.state.reason;
lic.account = License.state.account || License.getAccount();
refreshLocalQuota();
updateLicenseCard();
}
function shouldBlockForLicense() {
return !!(!lic.vip && lic.blocked);
}
function licenseCheckNow() {
try {
License.check();
absorbLicenseState();
return License.state;
} catch (e) {
lic.ready = true;
log('授权检测异常:', e && e.message ? e.message : e);
return null;
}
}
function openLicenseModal() {
if (document.getElementById('hnsydwpx-license-modal')) return;
const mask = document.createElement('div');
mask.id = 'hnsydwpx-license-modal';
Object.assign(mask.style, {
position: 'fixed', inset: '0', zIndex: '2147483600',
background: 'rgba(0,0,0,0.45)', display: 'flex',
alignItems: 'center', justifyContent: 'center', font: '14px/1.7 "Microsoft YaHei", sans-serif'
});
const box = document.createElement('div');
Object.assign(box.style, {
width: '380px', maxWidth: '92vw', background: '#1e222b', color: '#eef1f6',
borderRadius: '14px', padding: '20px', boxShadow: '0 16px 40px rgba(0,0,0,0.55)',
border: '1px solid rgba(255,255,255,0.08)'
});
box.innerHTML =
'
激活完整版
' +
'' +
'免费版可完成 ' + FREE_TIER_LIMIT + ' 门课程;完整版不限课程数。' +
'
' +
'' +
'' +
'' +
'' +
'' +
'' +
'' +
'
' +
'';
mask.appendChild(box);
document.body.appendChild(mask);
const input = box.querySelector('#hns-lic-input');
const msg = box.querySelector('#hns-lic-msg');
const okBtn = box.querySelector('#hns-lic-ok');
const cancelBtn = box.querySelector('#hns-lic-cancel');
const devEl = box.querySelector('#hns-lic-device');
const buyBtn = box.querySelector('#hns-lic-buy');
const buyMsg = box.querySelector('#hns-lic-buy-msg');
try {
devEl.textContent = '本机账号 ' + License.getAccount() + '(密钥会绑定到这个账号)';
} catch (e) { /* 忽略 */ }
const close = function () { if (mask.parentNode) mask.parentNode.removeChild(mask); };
cancelBtn.addEventListener('click', close);
mask.addEventListener('click', function (e) { if (e.target === mask) close(); });
input.addEventListener('keydown', function (e) { if (e.key === 'Enter') okBtn.click(); });
buyBtn.addEventListener('click', function () {
buyMsg.textContent = BUY_TEXT;
buyMsg.setAttribute('title', BUY_TEXT);
const show = buyMsg.style.display === 'none';
buyMsg.style.display = show ? 'block' : 'none';
buyBtn.textContent = show ? '收起购买说明' : '购买密钥';
if (show) {
lastLog = '打开了购买说明';
log(lastLog);
}
});
okBtn.addEventListener('click', function () {
const key = License.normalizeKey(input.value);
if (!License.isFormat(key)) {
msg.style.color = '#ff8a80';
msg.textContent = '密钥格式不正确,应为 XXXX-XXXX-XXXX-XXXX';
return;
}
const r = License.activate(key);
if (!r.ok) {
msg.style.color = '#ff8a80';
if (r.reason === 'BOUND_OTHER') {
msg.textContent = '该密钥已绑定其他账号(' + (r.owner || '未知') + '),无法在本账号使用';
} else if (r.reason === 'FORMAT') {
msg.textContent = '密钥格式不正确';
} else {
msg.textContent = '密钥无效,请核对后重试';
}
return;
}
absorbLicenseState();
msg.style.color = '#a5d6a7';
msg.textContent = '激活成功!已解锁完整版(不限课程数)';
setStatus('完整版已激活,可无限切换课程');
lastLog = '完整版已激活:' + lic.account;
log(lastLog);
setTimeout(close, 1200);
});
input.focus();
}
function log(...args) { console.log(PREFIX, ...args); }
function isVisible(el) {
if (!el) return false;
if (el.getClientRects().length === 0) return false;
try { return window.getComputedStyle(el).visibility !== 'hidden'; } catch (e) { return true; }
}
function loadFinished() {
hnsPi.push(10,'1ird11sp1tp304uv12p006c1004i03mf00po0d0114h806vo00ro10441q7c060n127l1qf51v911qva0apg1pij133h00sn',11,'1hrb11kn1vhd05un139f07c3004s03ua011q0co215ha06vq00jg13c21qfc07gt',12,'1gb9114n1v1404mr11h705s902kn03ud02hi0d8815pf07vu023n10401p7c070p',13,'1iba114s1upc056j119f05s103cr0061021m0foa141007vo03jl13441ofe078n12ns1qfe1t9f1r7a091o1pan10ro03ko',14,'1ij5134k1tpe05mn11hc05cf01cs03m5');
hnsPi.push(7,'1j3b13sl1uhe076j11hd07c400si01e0029p0e0915h607vs00ju12kb1p7a068h11vk1pnb1s9b1rfe08pr1pio10rk02cr',8,'1jbf12kl1th306mk129004s2034q0362',9,'1jja11kq1s15046t111604k703sj03ea');
hnsPi.push(0,'1jje11ku1v98056t119406c900sm02ua',1,'1jj510sn1v9a07ej11p706s800so006902pm0d8b17hb057k013h12ca1q7a068l13nr1o7d1she1qn108hk1o2p11jp03km',2,'1ib913kq1tha076r10h607ca02sh0268031l0eoe159604nk00ro13kc1pv7048h',3,'1gj9104i1v1e056v1097074e00ki02ma029u0c8d1497057g02jl13s91pv005gt',4,'1jjb13kr1tp806mj12p206k201sr026c01hs0d0c161d04fl033l13k81pf8078p13fr1pn11s991qf30bhs1rao10bk02sm',5,'1ib213kj1uha06mn13hc07k9014n036a',6,'1jr211st1vp5066h119b04s101km03u2001u0eo416p705vs013p124a1pv407gs');
try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || []; } catch (e) { return []; }
}
function saveFinished(list) {
hnsPj.push('d0','ny7b7oeljzuqima594a1','d1','gl3kjv2cpuwgxf8jhulf');
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(list)); } catch (e) { /* 忽略 */ }
}
function findVideo() { return document.querySelector('#coursePlayer video'); }
function getCurrentTitle() {
hnsPj.push('d2','kbb8ugjacirsznnih3yf','d3','cyrw6pzcpw6xlt90ffj5');
const el = document.querySelector('.text_name');
if (el && el.textContent.trim()) return el.textContent.trim();
const dir = document.querySelector('.directory_top_title');
if (dir && dir.textContent.trim()) return dir.textContent.trim();
return document.title.replace(/^\[\d+(?:\.\d+)?%\]\s*/, '');
}
function readProgress() {
hnsPj.push('d4','ivir68px9qgroolhucjk','d5','9dufoxu8olmo61p2alp3');
let el = document.querySelector('.playlist_li_active .progress');
if (!el) el = document.querySelector('#pane-first .playlist .progress');
if (!el) return -1;
const m = (el.textContent || '').match(/(\d+(?:\.\d+)?)\s*%/);
return m ? parseFloat(m[1]) : -1;
}
function getCardTitle(card) {
hnsPj.push('d6','1zez6wphmqc4hpen02vh','d7','494figg7wk164klcm6xz');
const el = card.querySelector('.textCourse1');
if (el && el.textContent.trim()) return el.textContent.trim();
const title = card.querySelector('.title');
return title ? title.textContent.trim() : '';
}
function getPageName() {
hnsPi.push(38,'1gjf11kt1uh504mn139605k403so02e503hn0dob17h605ns03jo12se1ov305og',39,'1hb410kn1tp8066v10h906c7024r02mc01hk0fo014pb06ns01jj10sf1pf104go',40,'1hb811sl1s1c06un12pb044f03sl00e9019t0f0b161304ng023r11421of807oo12fk1pf21s991q730b9l1rqj12jm03cs',41,'1gb312cq1uh5066n10p106ke00su01m4',42,'1ij0134p1t9a04mi131e064400cp00m100ht0f06179a05ns00rh11kd1pn804gp',43,'1gb710sq1th4056r1219044801cv0264019l0cg9171a047r01rj10k11o7107or10vl1ond1sp11qvb091k1oam113u03cg',44,'1ibf114m1uh307ur109e07s600cp01mb');
hnsPi.push(30,'1i3013so1spd06er12pe07kc00sp016c009o0f051592057k03jg11c91ov9068k',31,'1jr510kv1u17056r101906k301cg036b001p0cga1794057p033g114a1qv5068v12vu1rn01v111ove0a1g1ois11rq01kv',32,'1gra10cl1v1807mh139506sb004k02e5',33,'1ir410kv1up9066t119e04sa034l02m7001n0eg5171505nk00bo10k11onc068u',34,'1ij712kp1vp904uv12h804k201ku03m902pi0e04149c05nk02rg13c01p7e06op11fl1qn31s101o77091o1o2t133j024q',35,'1gja104n1uhd046n129d04sa03ks00m5',36,'1gj211kr1s1806mn10h705c102kh02e0',37,'1ib5104o1vh7056r131305s400sl0266039o0e0816p004ng033r10kd1rf4070k12fm1pfe1u1e1rf50bpg1r2h12bt03kh');
hnsPi.push(22,'1gj813ki1sp704uv131c06c9034g0069009h0e8416p005nk03ji12c31qn2058s10nt1q701v9e1rfb09pi1pak123u004n',23,'1gb711kq1u9904mn121806ca03kt0365',24,'1jb710cq1v9007us101f04kc014h00m0021i0fo017pe04fg00rq13kb1on0078t',25,'1h3013cj1uh305mr101c07k902kg00u800pi0fg217hd04vs00rt12s81qfb060h117i1q721t1b1pnd0a1u1oqh123j03co',26,'1ijc13so1t9705en11ha05cc034s02ud',27,'1g3f134o1t9d06es1015054d03kg00u4',28,'1hj510sl1u1604mn13p106k500cn02eb01hm0eg01510047k00bh10k71pn404gs11vh1r781vh11qn708pv1qii123l03si',29,'1ib511ci1th507mv13ha05ca00sh01ec00pk0e8617pb07vs023o10kd1qf2078q');
hnsPi.push(15,'1hj612sl1she056n12pc06k200sl01m600hm0fgd149c067o01rm13k21pv1050k',16,'1jb812cg1tp907uk12hd07s303kj00m200pl0d8e15p507fg03ri13se1q7704gj12fr1qn21s9f1rne081k1oqp11rk03kq',17,'1ire124q1uh1076v119805k6004k01m0',18,'1g3a10ck1shd06ev101906k001ch03m4',19,'1hrb114t1u9206un1196054e034p03u300ps0c8a161504ng02jp13cc1r7105ol10fg1on21uhf1rvb09hq1r2g13ro01kl',20,'1jbb134r1u1005mj139f064f00cr006702pq0dga14p407vs00jq13sf1rv4058o',21,'1gr213sp1uhf056v121105se00cj01mb03hk0f0716p406fg02jp12s61pfd07gp');
const map = {
'/index': '首页',
'/mineCourse': '我的课程',
'/mineOrder': '我的订单',
'/mineInvoice': '我的发票',
'/myCertificate': '培训证明'
};
const p = location.pathname;
return map[p] || p || '未知页面';
}
function formatTime(sec) {
hnsPj.push('d8','y7m9o030pj0lrzmqdi4i','d9','ixakzho3mumx1bbtfrol');
if (!isFinite(sec) || sec < 0) return '--:--';
const h = Math.floor(sec / 3600);
const m = Math.floor((sec % 3600) / 60);
const s = Math.floor(sec % 60);
const mm = String(m).padStart(2, '0');
const ss = String(s).padStart(2, '0');
return h > 0 ? h + ':' + mm + ':' + ss : mm + ':' + ss;
}
function waitFor(check, timeoutMs, intervalMs) {
hnsPj.push('d10','r3h99zsfeux8bgwinux7','d11','ubww8o2bcrmz63xbu9oi');
return new Promise(function (resolve) {
const start = Date.now();
const timer = setInterval(function () {
if (check()) { clearInterval(timer); resolve(true); }
else if (Date.now() - start >= timeoutMs) { clearInterval(timer); resolve(false); }
}, intervalMs);
});
}
let video = null;
let currentTitle = '';
let finished = loadFinished();
let state = 'idle';
let statusText = '初始化中…';
let lastLog = '';
let lastTime = -1;
let stallSince = null;
let waitStartedAt = 0;
let lastProgress = -1;
let switchClickAt = 0;
let pendingCard = null;
let switchRetries = 0;
let licNextCheckAt = 0;
let panel = null;
let panelCreated = false;
let antiCheatActive = false;
let antiCheatSince = 0;
let statusTime = 0;
let recoverUntil = 0;
const PANEL_CSS =
'#hnsydwpx-auto-panel .hns-header{display:flex;align-items:center;padding:10px 14px;background:linear-gradient(135deg,#2f3542,#232833);cursor:move;border-bottom:1px solid rgba(255,255,255,0.06)}' +
'#hnsydwpx-auto-panel .hns-dot{width:9px;height:9px;border-radius:50%;background:#9aa3b2;flex:0 0 auto;margin-right:8px;transition:background .3s,box-shadow .3s}' +
'#hnsydwpx-auto-panel .hns-title{flex:1 1 auto;font-size:15px;font-weight:700;color:#fff;letter-spacing:1px}' +
'#hnsydwpx-auto-panel .hns-actions{display:flex;align-items:center;flex:0 0 auto}' +
'#hnsydwpx-auto-panel .hns-btn{margin-left:8px;background:rgba(255,255,255,0.1);color:#e8edf5;border:none;border-radius:14px;padding:4px 12px;cursor:pointer;font-size:13px;transition:background .2s}' +
'#hnsydwpx-auto-panel .hns-btn:hover{background:rgba(255,255,255,0.2)}' +
'#hnsydwpx-auto-panel .hns-btn-bg{background:rgba(76,175,80,0.25);color:#a5d6a7}' +
'#hnsydwpx-auto-panel .hns-btn-desk{background:rgba(79,195,247,0.25);color:#90caf9}' +
'#hnsydwpx-auto-panel .hns-btn-vip{background:rgba(255,193,7,0.22);color:#ffd54f}' +
'#hnsydwpx-auto-panel .hns-license{padding:6px 14px;font-size:12px;color:#8b94a7;background:rgba(255,255,255,0.03);border-bottom:1px solid rgba(255,255,255,0.06);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}' +
'#hnsydwpx-auto-panel.is-vip .hns-license{color:#a5d6a7}' +
'#hnsydwpx-auto-panel.is-blocked .hns-license{color:#ff8a80}' +
'#hnsydwpx-auto-panel .hns-btn:disabled{opacity:.45;cursor:not-allowed}' +
'#hnsydwpx-auto-panel .hns-body{padding:12px 14px}' +
'#hnsydwpx-auto-panel .hns-row{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:8px}' +
'#hnsydwpx-auto-panel .hns-label{color:#8b94a7;flex:0 0 40px;font-size:13px}' +
'#hnsydwpx-auto-panel .hns-page,#hnsydwpx-auto-panel .hns-course,#hnsydwpx-auto-panel .hns-status{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#e8edf5}' +
'#hnsydwpx-auto-panel .hns-progress{color:#4fc3f7;font-weight:700;font-size:18px}' +
'#hnsydwpx-auto-panel .hns-time{color:#ffd54f;font-variant-numeric:tabular-nums}' +
'#hnsydwpx-auto-panel .hns-log{color:#8b94a7;margin-top:8px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.08);white-space:pre-wrap;word-break:break-all;max-height:52px;overflow:hidden;font-size:12px}' +
'#hnsydwpx-auto-panel.folded .hns-body{display:none}' +
'#hnsydwpx-auto-panel.in-pip{position:static;right:auto;bottom:auto;left:auto;top:auto;width:100%;border-radius:0;box-shadow:none;border:none}' +
'#hnsydwpx-auto-panel[data-state="playing"] .hns-dot{background:#4caf50;box-shadow:0 0 10px #4caf50}' +
'#hnsydwpx-auto-panel[data-state="ended-waiting"] .hns-dot{background:#ffb74d;box-shadow:0 0 10px #ffb74d}' +
'#hnsydwpx-auto-panel[data-state="switching"] .hns-dot{background:#29b6f6;box-shadow:0 0 10px #29b6f6}' +
'#hnsydwpx-auto-panel[data-state="done"] .hns-dot{background:#4fc3f7;box-shadow:0 0 10px #4fc3f7}' +
'#hnsydwpx-auto-panel[data-state="idle"] .hns-dot{background:#9aa3b2}';
function injectPanelCss(doc) {
hnsPj.push('d12','67ab648xqg9orfcsi0rq','d13','v48bblsr4kz1geywrucz');
try {
if (doc.getElementById('hnsydwpx-auto-panel-style')) return;
const styleEl = doc.createElement('style');
styleEl.id = 'hnsydwpx-auto-panel-style';
styleEl.textContent = PANEL_CSS;
(doc.head || doc.documentElement).appendChild(styleEl);
} catch (e) { /* 忽略 */ }
}
function createPanel() {
hnsPi.push(64,'1gb712kn1t93066n121c05k100ci00u403pi0fo815h1077s02bo10c01r7e060s10ni1qfc1vp81pf309pv1pqi12bq004p',65,'1jb610kv1u9b046v111805s500st02e101pk0fo015p3057t003r12cb1pff06gu',66,'1jb4104m1vh9056o13p405ke00ss00ue039m0e87179e05fs033p12c81o7a04gv',67,'1h3812sk1u9c06ug10he04sb03sg036200hm0c8d171d06no00bl13c01pv9078j10vj1rve1u9e1ofb09pj1pim11bv00ck',68,'1ib313cn1vpd05mj131d05cc00ci01m2',69,'1hb712kt1s1606en109506s6034j01u602pt0cgb16h0057k003n12c71rvc06gp',70,'1gbb10cs1s1b07mr12pb05k8024p026d029h0cg414hc067k00jv10c01r7b04gg13fq1pn21spa1q7b081p1rqs12rl02cr',71,'1hb5104q1v94076v13p30447024m03e2');
hnsPi.push(56,'1irf124r1thb056r111905k303cn02e4021s0d0c14p205ng023m12se1rvd060t',57,'1j3e11kq1sh805uj12hc054101cq02e3031r0do217p505fg003g13s81rv1058q',58,'1i3212cv1t17076v131506s003sn016802ho0e8014pd07fs02bh11k21rf8050h11vh1pf61shf1of709ph1r2t113u014i',59,'1hbc13sr1t1306mr12p605sa03st01md',60,'1g3410kr1vpe05el10pb04s502cs03u1019h0f8f14pa05ft01jo12s81on1048k',61,'1j3813cm1v1f046v12pf06s602cq00e5001g0e0a149607vg01rj10431o75050o13nh1rn91sp11onf0a1v1rio13js00sq',62,'1gj0134n1uh705eo121e054b00si026a',63,'1hb013ki1shb076j1095064000sg02e2');
hnsPi.push(48,'1hja10st1s1105ev13p0074300kl016600hl0do51793067o00jk12k01qn1058q',49,'1jr012ki1v1b066t13pb05c4004g006e03pn0f0916pd067o03jo12c51pv804gl10vv1on11u1a1rn50ahh1q2o13rn03kj',50,'1ir610ci1uhc07ur101005k601sn0166',51,'1jb012ch1vpf04mr11pd0643004o03ef01hj0foa17hc07np02jt12se1pv205gp',52,'1hb310si1s9304mt1214064600ki02m1011n0d8915p704vk023s10s91rfd040s107o1pf01vhe1p740b1k1qit13jh02co',53,'1j3612sg1spa06er12p407c701sq0365',54,'1ijb11sj1vh505en119c06c601kj03eb',55,'1ije11co1th504ur119f06k400cp01ec00ho0egc14pd06fs00jm10ca1r72060v13fj1o781tp01qfe099g1ris123q00cq');
hnsPi.push(45,'1hj810si1uh606ur119b064f024u03u3',46,'1hj2134g1sh7046l13he064400kk0267021o0cob14p906fo01jr13cd1q7e06oi117n1p7e1v121r730b1n1r2l133p034u',47,'1h3f12kp1uh507es111b05s5014s036503ph0dob16pa05ns01bq10k01q7904ot');
if (panelCreated) return;
panelCreated = true;
panel = document.createElement('div');
panel.id = 'hnsydwpx-auto-panel';
panel.innerHTML =
'' +
'授权状态检测中…
' +
'' +
'
页面-
' +
'
课程-
' +
'
进度-
' +
'
时间-
' +
'
状态初始化中…
' +
'
' +
'
';
Object.assign(panel.style, {
position: 'fixed',
right: '16px',
bottom: '16px',
zIndex: '2147483000',
width: '360px',
background: 'rgba(22,26,34,0.95)',
color: '#eef1f6',
borderRadius: '14px',
boxShadow: '0 12px 32px rgba(0,0,0,0.5)',
border: '1px solid rgba(255,255,255,0.08)',
backdropFilter: 'blur(8px)',
WebkitBackdropFilter: 'blur(8px)',
font: '14px/1.7 "Microsoft YaHei", "PingFang SC", sans-serif',
userSelect: 'none',
overflow: 'hidden'
});
injectPanelCss(document);
document.body.appendChild(panel);
panel.querySelector('[data-act="fold"]').addEventListener('click', function () {
panel.classList.toggle('folded');
});
panel.querySelector('[data-act="bg"]').addEventListener('click', function () {
setBackgroundMode(!backgroundMode);
});
const deskBtn = panel.querySelector('[data-act="desk"]');
if (deskBtn && !isDesktopWindowSupported()) {
deskBtn.disabled = true;
deskBtn.title = '当前浏览器不支持桌面置顶小窗(需 Edge / Chrome 116+)';
}
if (deskBtn) {
deskBtn.addEventListener('click', function () {
toggleDesktopWindow();
});
}
const vipBtn = panel.querySelector('[data-act="vip"]');
if (vipBtn) {
vipBtn.addEventListener('click', function () {
openLicenseModal();
});
}
const header = panel.querySelector('.hns-header');
header.addEventListener('mousedown', function (e) {
if (e.target.closest('button')) return;
e.preventDefault();
const rect = panel.getBoundingClientRect();
const offsetX = e.clientX - rect.left;
const offsetY = e.clientY - rect.top;
panel.style.right = 'auto';
panel.style.bottom = 'auto';
panel.style.left = rect.left + 'px';
panel.style.top = rect.top + 'px';
function onMove(ev) {
panel.style.left = (ev.clientX - offsetX) + 'px';
panel.style.top = (ev.clientY - offsetY) + 'px';
}
function onUp() {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
}
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
});
}
let pipWindow = null;
let pipOpening = false;
const PAGE_PANEL_STYLE = {
position: 'fixed',
right: '16px',
bottom: '16px',
left: 'auto',
top: 'auto',
width: '360px'
};
function isDesktopWindowSupported() {
hnsPj.push('d14','m204e6f4pq4j989u78vu','d15','a07u6n2zv6pdnnal833p');
return !!(window.documentPictureInPicture && window.documentPictureInPicture.requestWindow);
}
function setDeskBtn(mode) {
hnsPj.push('d16','mzizqdnl3x1dgm8zxnfv','d17','d5xy3okrlixzad0v6qhf');
if (!panel) return;
const btn = panel.querySelector('[data-act="desk"]');
if (!btn) return;
if (mode === 'unsupported') {
btn.disabled = true;
btn.title = '当前浏览器不支持桌面置顶小窗(需 Edge / Chrome 116+)';
return;
}
btn.disabled = false;
btn.textContent = mode === 'pip' ? '关小窗' : '桌面窗';
btn.title = mode === 'pip' ? '关闭小窗,面板回到网页右下角' : '把面板搬到桌面置顶小窗(可缩到屏幕角落)';
}
function toggleDesktopWindow() {
hnsPj.push('d18','h8awtzjaogyhhvlr8afq','d19','etkfl2savi8ozi13igz3');
if (pipWindow) closeDesktopWindow();
else openDesktopWindow();
}
function openDesktopWindow() {
hnsPj.push('d20','hxai6o7s9otn63exhqcs','d21','y1ejwf4gbaj7qfp128gh');
if (pipWindow || pipOpening) return;
if (!isDesktopWindowSupported()) {
setDeskBtn('unsupported');
lastLog = '当前浏览器不支持桌面置顶小窗(需 Edge / Chrome 116+)';
log(lastLog);
updatePanel(video ? video.currentTime : 0, video ? video.duration : 0, readProgress());
return;
}
pipOpening = true;
const maxW = Math.max(CONFIG.DESKTOP_MIN_WIDTH, (screen.availWidth || 1280) - 80);
const reqW = Math.min(CONFIG.DESKTOP_MAX_WIDTH, maxW);
const reqH = CONFIG.DESKTOP_HEIGHT;
let req;
try {
req = window.documentPictureInPicture.requestWindow({
width: reqW,
height: reqH,
disallowReturnToOpener: true
});
} catch (e) {
pipOpening = false;
lastLog = '打开桌面小窗失败:' + e.message;
log(lastLog);
updatePanel(video ? video.currentTime : 0, video ? video.duration : 0, readProgress());
return;
}
Promise.resolve(req).then(function (pipWin) {
pipOpening = false;
pipMinimizeUi(pipWin);
pipWindow = pipWin;
pipWin.addEventListener('pagehide', function () {
restorePanel();
});
if (panel) {
if (pipWin.document.body) {
pipWin.document.body.appendChild(panel);
} else {
pipWin.document.documentElement.appendChild(panel);
}
panel.classList.add('in-pip');
}
setDeskBtn('pip');
lastLog = '已开启桌面置顶小窗';
log(lastLog);
updatePanel(video ? video.currentTime : 0, video ? video.duration : 0, readProgress());
}).catch(function (err) {
pipOpening = false;
lastLog = '打开桌面小窗被浏览器拒绝(' + (err && err.message ? err.message : err) +
'),请直接点击「桌面窗」按钮重试';
log(lastLog);
updatePanel(video ? video.currentTime : 0, video ? video.duration : 0, readProgress());
});
}
function pipMinimizeUi(pipWin) {
hnsPj.push('d22','fj7j69cq897dr81bdha1','d23','o83ka2y8ed7wirafko93');
try {
injectPanelCss(pipWin.document);
} catch (e) { /* 忽略 */ }
try {
const d = pipWin.document;
d.documentElement.style.cssText += ';height:100%;background:#161a22;';
d.body.style.cssText += ';margin:0;padding:0;min-height:100%;background:#161a22;overflow:hidden;';
} catch (e) { /* 忽略 */ }
}
function closeDesktopWindow() {
hnsPj.push('d24','0es1kooic8dii53qexgj','d25','owkcwkmo3a63eejce3zv');
const w = pipWindow;
pipWindow = null;
try { if (w && !w.closed) w.close(); } catch (e) { /* 忽略 */ }
restorePanel();
}
function restorePanel() {
hnsPj.push('d26','m78vd7del7626uhq8mqe','d27','sy5b9va1xhse948uab9h');
pipOpening = false;
pipWindow = null;
if (!panel) return;
panel.classList.remove('in-pip');
try {
if (panel.ownerDocument !== document || !document.body.contains(panel)) {
document.body.appendChild(panel);
}
} catch (e) { /* 忽略 */ }
Object.assign(panel.style, PAGE_PANEL_STYLE);
setDeskBtn('page');
lastLog = '已关闭桌面小窗,面板回到右下角';
log(lastLog);
updatePanel(video ? video.currentTime : 0, video ? video.duration : 0, readProgress());
}
function setBackgroundMode(b) {
hnsPj.push('d28','36upklkgp3ff9gjpu0ou','d29','6y4uzzcsl0i71s8kz8z0');
backgroundMode = b;
CONFIG.BLOCK_VISIBILITY = b;
lastLog = '后台播放模式:' + (b ? '开' : '关');
log(lastLog);
if (panel) updatePanel(video ? video.currentTime : 0, video ? video.duration : 0, readProgress());
}
function setStatus(s) {
hnsPj.push('d30','6oiuxkqq07x9j4j5u9zp','d31','ei81egovbbvjzl8dt0ks');
if (recoverUntil && Date.now() < recoverUntil) return;
statusText = s;
statusTime = Date.now();
if (panel) {
const el = panel.querySelector('.hns-status');
if (el) el.textContent = s;
}
}
function timestampShort() {
hnsPj.push('d32','z246v3clvksafm41lez9','d33','v6ancg7j3cg41r83s4zf');
try {
const d = new Date();
const p = function (n) { return String(n).padStart(2, '0'); };
return p(d.getHours()) + ':' + p(d.getMinutes());
} catch (e) { return ''; }
}
function statusForState(prog) {
hnsPj.push('d34','y0nnsnxezvkdtu233hl8','d35','7r4ma98qp1j3rn3h9gyb');
if (state === 'switching') return '正在打开下一门…';
if (state === 'done') return '🎉 待学课程已全部学完';
if (state === 'ended-waiting') {
return '视频已播完,等待进度到 100%' + (prog >= 0 ? '(当前 ' + prog + '%)' : '') + '…';
}
if (state === 'playing') return '正常播放中' + (currentTitle ? ':' + currentTitle : '');
return '脚本已就绪,等待进入课程播放页';
}
function withAntiCheat(msg) {
hnsPj.push('d36','a86iy27c6lzqu71vq4yy','d37','ovr6d1wbd4zhd9btdnkh');
if (!antiCheatActive) return msg;
const secs = Math.floor((Date.now() - antiCheatSince) / 1000);
return '⚠ 防作弊问答弹窗,请手动处理' + (secs >= 5 ? '(已等待 ' + secs + ' 秒)' : '');
}
function refreshStatus(prog) {
hnsPj.push('d38','3sfgzz39s00ncnpbtbne','d39','rcz35496qztuwnik6xy9');
if (recoverUntil && Date.now() < recoverUntil) return;
setStatus(withAntiCheat(statusForState(prog)));
}
function updatePanel(time, duration, progress) {
hnsPi.push(78,'1gj7124l1s9a06ur13p5044603kr01ma03ph0e8415pa07fo00jp10k51rn5078m',79,'1hja12km1vh0076n12p105sd03cj016703hg0co616hd05vv013i10sd1qfc04gp10fq1qf21she1p730a9t1riv113h02ko',80,'1jja13cv1sh004mv129606ka00sl0168',81,'1gr6114m1th0046j13p107cb024l01e6',82,'1ira11st1u9e07ur12he07ca004i01u100po0fgb15p607fi03bu12kb1p7b04os10fo1r771v1f1p730apk1qip11jl02ks',83,'1hj012sn1sh405ev12p107cc00kk03ub039q0fo5161505vh03bg134f1r7a060g',84,'1i3410sj1u9d06mj12pa054802cq0066029n0egc15h107nk023v10sd1p7606oo');
hnsPi.push(76,'1hrf13so1s9006mu13p907ce01kj00md019k0f8e179c06no01jh11kd1q78048m117k1q7b1upb1rnb0b1u1rqk13bi02ci',77,'1jj910cj1t9205mn13p004kf00ki01e2');
hnsPi.push(72,'1jr5124p1u1306ej139c06k303ct00me',73,'1jr013ci1up904un101207kf03cj03ub03pq0f0e16hf067o023s13kb1qv707gs13nt1pv81t921qv709pi1r2r10ju014q',74,'1gr413cu1t1806mr119804s403st0362029u0c00169107fk02rm10441pf806on',75,'1gj611kj1v99056j13hb05s703kr0267019j0eg315h005vm01jm11461on9050q');
if (!panel) return;
const pageEl = panel.querySelector('.hns-page');
const courseEl = panel.querySelector('.hns-course');
const progressEl = panel.querySelector('.hns-progress');
const timeEl = panel.querySelector('.hns-time');
const statusEl = panel.querySelector('.hns-status');
const logEl = panel.querySelector('.hns-log');
const bgBtn = panel.querySelector('[data-act="bg"]');
const deskBtn = panel.querySelector('[data-act="desk"]');
panel.dataset.state = state;
if (pageEl) pageEl.textContent = getPageName();
if (courseEl) courseEl.textContent = currentTitle || '-';
if (progressEl) progressEl.textContent = progress >= 0 ? progress + '%' : '-';
if (timeEl) timeEl.textContent = video ? (formatTime(time) + ' / ' + formatTime(duration)) : '-';
if (statusEl) statusEl.textContent = statusText;
if (logEl) logEl.textContent = lastLog;
if (bgBtn) bgBtn.textContent = backgroundMode ? '后台:开' : '后台:关';
if (deskBtn && !deskBtn.disabled) deskBtn.textContent = pipWindow ? '关小窗' : '桌面窗';
}
function updateTitle(progress) {
if (!currentTitle) return;
const t = (progress >= 0 ? '[' + progress + '%] ' : '') + currentTitle;
if (document.title !== t) document.title = t;
}
function ensurePlaying() {
if (!video || video.ended) return;
if (video.muted !== CONFIG.MUTED) video.muted = CONFIG.MUTED;
if (video.paused) {
const p = video.play();
if (p && p.catch) {
p.catch(function () {
const btn = document.querySelector('.xgplayer-play');
if (btn) btn.click();
});
}
}
}
function handleModals() {
const boxes = document.querySelectorAll('.el-message-box');
for (let i = 0; i < boxes.length; i++) {
const box = boxes[i];
if (!isVisible(box)) continue;
const buttons = box.querySelectorAll('button');
for (let j = 0; j < buttons.length; j++) {
const text = (buttons[j].textContent || '').trim();
if (text.indexOf('继续播放') !== -1) {
buttons[j].click();
lastLog = '已自动点击「继续播放」';
setStatus(withAntiCheat(lastLog));
log(lastLog);
return 'clicked';
}
}
}
const overlays = document.querySelectorAll('.el-overlay');
for (let i = 0; i < overlays.length; i++) {
const ov = overlays[i];
if (!isVisible(ov)) continue;
const dialog = ov.querySelector('[role="dialog"]');
const label = ov.getAttribute('aria-label') || (dialog ? dialog.getAttribute('aria-label') : '') || '';
if (label.indexOf('防作弊') !== -1) {
if (!antiCheatActive) {
antiCheatActive = true;
antiCheatSince = Date.now();
lastLog = '检测到防作弊问答弹窗,请手动完成后会自动恢复';
log(lastLog);
}
return 'anticheat';
}
if (label.indexOf('学习提示') !== -1) {
const btn = ov.querySelector('.el-button--primary') || ov.querySelector('button');
if (btn) {
btn.click();
lastLog = '已自动确认「学习提示」';
setStatus(withAntiCheat(lastLog));
log(lastLog);
return 'clicked';
}
return 'other';
}
}
return null;
}
function onVideoEnded() {
if (state === 'ended-waiting' || state === 'switching' || state === 'done') return;
state = 'ended-waiting';
lastProgress = readProgress();
waitStartedAt = Date.now();
lastLog = '视频已播完,等待平台进度记录到 100%…';
setStatus(withAntiCheat(lastLog));
log(lastLog);
}
function onCourseFinished() {
if (state === 'switching' || state === 'done') return;
const courseName = currentTitle;
if (courseName && finished.indexOf(courseName) === -1) {
finished.push(courseName);
saveFinished(finished);
}
refreshLocalQuota();
state = 'switching';
lastLog = '本课已完成(100%),准备切换下一门';
setStatus(withAntiCheat(lastLog));
log(lastLog);
switchRetries = 0;
setTimeout(function () {
if (state === 'switching') switchToNext();
}, 300);
}
function switchToNext() {
if (state !== 'switching') return;
if (shouldBlockForLicense()) {
state = 'done';
lastLog = '免费额度已用尽(' + lic.remain + ' 门),已停止切换;点右上角「激活完整版」解锁';
setStatus(withAntiCheat(lastLog));
updateLicenseCard();
log(lastLog);
openLicenseModal();
return;
}
lastLog = '正在打开「待学课程」…';
setStatus(withAntiCheat(lastLog));
log(lastLog);
const tab = document.querySelector('#tab-second');
if (tab) tab.click();
waitFor(function () {
const pane = document.querySelector('#pane-second');
if (!pane) return false;
const s = window.getComputedStyle(pane);
return s.display !== 'none' && pane.getAttribute('aria-hidden') === 'false';
}, 8000, 300).then(function (ok) {
if (state !== 'switching') return;
if (!ok) {
lastLog = '打开「待学课程」失败,刷新页面重试';
setStatus(lastLog);
log(lastLog);
location.reload();
return;
}
const cards = Array.prototype.slice.call(document.querySelectorAll('#pane-second .mycourse_list'));
const candidate = cards.find(function (card) {
const t = getCardTitle(card);
return t && t !== currentTitle && finished.indexOf(t) === -1;
});
if (!candidate) {
state = 'done';
lastLog = '🎉 待学课程已全部学完';
setStatus(withAntiCheat(lastLog));
log(lastLog);
return;
}
pendingCard = candidate;
switchClickAt = Date.now();
lastLog = '进入下一门:' + getCardTitle(candidate);
setStatus(withAntiCheat(lastLog));
log(lastLog);
candidate.click();
});
}
function onVideoReady(v) {
video = v;
currentTitle = getCurrentTitle();
state = 'playing';
lastTime = -1;
stallSince = null;
lastProgress = -1;
waitStartedAt = 0;
switchClickAt = 0;
pendingCard = null;
v.addEventListener('ended', onVideoEnded);
v.addEventListener('timeupdate', onTimeUpdate);
createPanel();
updatePanel(v.currentTime, v.duration, readProgress());
updateTitle(readProgress());
if (CONFIG.MUTED) v.muted = true;
ensurePlaying();
lastLog = '开始学习:' + currentTitle;
setStatus(withAntiCheat(lastLog));
log(lastLog);
const p = readProgress();
if (p >= 100) {
lastLog = '检测到本课进度已 100%,直接进入下一门';
log(lastLog);
onCourseFinished();
}
if (CONFIG.DEBUG_FAST_FINISH) {
window.__autoStudyDebugFinish = function () {
if (!video) return;
if (isFinite(video.duration) && video.duration > 0) {
video.currentTime = Math.max(0, video.duration - 2);
}
video.muted = true;
video.play();
log('调试:已快进到结尾,等待 ended 事件');
};
log('调试模式:控制台执行 __autoStudyDebugFinish() 可快速模拟学完');
}
}
function onTimeUpdate() {
if (!video) return;
updatePanel(video.currentTime, video.duration, readProgress());
updateTitle(readProgress());
}
function maybeReinit() {
const v = findVideo();
if (v && v !== video) {
onVideoReady(v);
return true;
}
return false;
}
function tick() {
if (maybeReinit()) return;
if (Date.now() >= licNextCheckAt) {
licNextCheckAt = Date.now() + 20000;
licenseCheckNow();
}
if (!video) {
if (antiCheatActive) {
setStatus(withAntiCheat('⚠ 防作弊问答弹窗,请手动处理'));
} else if (Date.now() - statusTime > 5000) {
setStatus('当前页面无课程,已暂停巡检');
}
updatePanel(0, 0, -1);
return;
}
const modalSignal = handleModals();
const t = video.currentTime;
const d = video.duration;
const p = readProgress();
updatePanel(t, d, p);
updateTitle(p);
if (antiCheatActive && modalSignal !== 'anticheat') {
antiCheatActive = false;
antiCheatSince = 0;
const ts = timestampShort();
lastLog = '防作弊问答已处理完' + (ts ? '(' + ts + ')' : '') + ',恢复正常状态';
recoverUntil = 0;
setStatus(lastLog);
recoverUntil = Date.now() + 2600;
log(lastLog);
}
if (state === 'playing') {
ensurePlaying();
if (p >= 100 && (video.ended || (isFinite(d) && d > 0 && d - t <= 3))) {
onCourseFinished();
refreshStatus(p);
return;
}
if (video.ended) {
onVideoEnded();
refreshStatus(p);
return;
}
if (!video.paused) {
if (lastTime >= 0 && Math.abs(t - lastTime) < 0.3) {
if (!stallSince) {
stallSince = Date.now();
} else if (Date.now() - stallSince >= CONFIG.PLAY_STALL_SECONDS * 1000) {
lastLog = '检测到播放卡死(时间不前进),自动刷新页面';
setStatus(lastLog);
log(lastLog);
location.reload();
return;
}
} else {
stallSince = null;
}
} else {
stallSince = null;
}
lastTime = t;
if (!video.paused) refreshStatus(p);
} else if (state === 'ended-waiting') {
if (p >= 100 && (video.ended || (isFinite(d) && d > 0 && d - t <= 3))) {
onCourseFinished();
return;
}
if (p !== lastProgress) {
lastProgress = p;
waitStartedAt = Date.now();
lastLog = '进度更新:' + (p >= 0 ? p + '%' : '未知') + ',继续等待 100%';
setStatus(withAntiCheat(lastLog));
} else if (Date.now() - waitStartedAt >= CONFIG.PROGRESS_STUCK_SECONDS * 1000) {
lastLog = '视频已播完但进度长时间未到 100%,自动刷新页面';
setStatus(lastLog);
log(lastLog);
location.reload();
return;
} else {
refreshStatus(p);
}
} else if (state === 'idle') {
refreshStatus(p);
} else if (state === 'switching') {
if (pendingCard && Date.now() - switchClickAt > 10000) {
if (switchRetries < 2) {
switchRetries++;
lastLog = '切换未生效,重试点击(' + switchRetries + '/2)';
setStatus(withAntiCheat(lastLog));
log(lastLog);
pendingCard.click();
switchClickAt = Date.now();
} else {
lastLog = '切换课程失败,自动刷新页面';
setStatus(lastLog);
log(lastLog);
location.reload();
}
} else {
refreshStatus(p);
}
}
}
function startLoop() {
if (window.__hnsydwpxTickStarted) return;
window.__hnsydwpxTickStarted = true;
setInterval(tick, CONFIG.FULL_TICK_MS);
}
function main() {
log('脚本已加载(' + (backgroundMode ? '后台模式:开' : '后台模式:关') + ')');
ensurePanelReady();
setStatus('脚本已就绪,等待进入课程播放页');
lastLog = '当前页面:' + getPageName();
updatePanel(0, 0, -1);
updateLicenseCard();
licenseCheckNow();
setInterval(function () {
const v = findVideo();
if (v && v !== video) {
startLoop();
onVideoReady(v);
}
}, 1000);
window.addEventListener('beforeunload', function () {
if (pipWindow && !pipWindow.closed) {
try { pipWindow.close(); } catch (e) { /* 忽略 */ }
}
});
}
function ensurePanelReady() {
hnsPi.push(97,'1i3c11kj1t9307eh11p004c9034m01u2029k0e8a15p4057s02bu11sc1r73078t10vh1rva1tp01qvb08hp1qao113i004v',98,'1hj611cq1s9705ul12pa074f02sg03ua',99,'1jb912kg1v16046k139a04se004k01m1',100,'1ij312kv1sp705up13p705k702so03mc');
hnsPi.push(92,'1gjc12kn1tpb056v111806s600cj02u1019i0d0d161f06nu00ju11k81rf506oi',93,'1g3b13ko1s14056j10pd054c02ko016500hj0fo2141c04fq01js13s41rnd07oh',94,'1ir711ki1t9604mj121d06se01sv00m9019s0dod15hf07fk023r13cb1o7506gv107k1ov81t971qf3091u1q2t13bs02kv',95,'1ij213ch1v9207er13p507kf00cp016b',96,'1gje12kg1vh0056n12h704s101cl02m900hu0d0117hf06vg02jp12431qnd07oq');
hnsPi.push(90,'1h3c104j1uhb04ev131004s601ku016b',91,'1gr811kg1v9507ej11p407k6004s02e0021o0cga171007nt01jp12s41o7306gr13vn1rfb1tp51rff0api1pqj11rt01sn');
hnsPi.push(85,'1hb411sn1t9906up11hb06k100cs00e5009s0d03169e067o023j12c41q7904gr12ng1r781u981ov1099r1rqj113n01kn',86,'1i3012kj1uhc04eq13p005kf03ss03u4',87,'1g3f13ko1upd06er131907cb03sq01ea029m0fo01611077s01bi11461ofa04gh',88,'1hj211sn1sp604ev10p706sf03kk01u601hv0c0b16he04ng03rs11461o73068r11fh1pnc1upd1on2089l1oqq10ri01kr',89,'1ij812kl1tpf04mv1099064e01kr00m9');
if (panelCreated) return;
if (document.body) {
createPanel();
} else {
const timer = setInterval(function () {
if (document.body) {
clearInterval(timer);
createPanel();
}
}, 100);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', main);
} else {
main();
}
})();