// ==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(); } })();