// ==UserScript==
// @name 【效率版】2026秋季学期人教版义教新教材网络培训
// @namespace https://card.wlxy.live/details/C8B94027
// @version 2.7
// @icon https://huaweicloudobs.ahjxjy.cn/895789f9086469785b846d30c0ed95f9.png
// @description 2026秋季学期人教版义教新教材网络培训,PEP人教教材秋季培训,支持一键刷回放课程,秒刷完至少间隔12小时通常隔日平台更新进度。
// @antifeature payment 免费体验2课程,升级pro不限制
// @antifeature membership 需云端授权
// @match *://wp.pep.com.cn/*
// @match *://bjpep.gensee.com/*
// @match *://wp.pep.com.cn/web/index.php?/px/*
// @match *://bjpep.gensee.com/webcast/site/vod/*
// @connect oa12.ahzsksw.cn
// @connect wp.pep.com.cn
// @connect bjpep.gensee.com
// @connect albcdnvodproxy.gensee.com
// @connect wscache.gensee.com
// @connect tianyicache.gensee.com
// @connect record68.gensee.net
// @connect *
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-start
// @tag 人教版新教材培训
// @tag 学习辅助
// @tag 自动刷课
// @tag 回放刷课
// @tag PEP教材
// @license MIT
// ==/UserScript==
(function () {
"use strict";
const SCRIPT_VERSION = (() => {
try {
if (typeof GM_info !== "undefined" && GM_info?.script?.version) {
return String(GM_info.script.version);
}
} catch (_) {}
return "2.0.24";
})();
const SCRIPT_TITLE = "\u4eba\u6559\u7248\u4e49\u6559\u65b0\u6559\u6750\u7f51\u7edc\u57f9\u8bad\u52a9\u624b";
const SCRIPT_ICON =
"https://huaweicloudobs.ahjxjy.cn/895789f9086469785b846d30c0ed95f9.png";
const CLOUD_API_BASE = "https://oa12.ahzsksw.cn";
const PRO_BUY_URL = "https://card.wlxy.live/details/C8B94027";
const CLOUD_TOKEN_KEY = "rjpx_cloud_token_v1";
const CLOUD_LEASE_KEY = "rjpx_cloud_lease_v1";
const FINISH_TIP =
"\u6240\u9009\u8bfe\u7a0b\u5df2\u5168\u90e8\u5b66\u5b8c\u3002\u5e73\u53f0\u8fdb\u5ea6\u901a\u5e38\u9694\u65e5\u66f4\u65b0\uff0c\u4e14\u81f3\u5c11\u5237\u5b8c 12 \u5c0f\u65f6\u540e\u518d\u67e5\u770b\uff0c\u65e0\u9700\u62c5\u5fc3\u672a\u751f\u6548\u3002";
const HOST = location.host;
const IS_PEP = /(^|\.)wp\.pep\.com\.cn$/i.test(HOST);
const IS_GENSEE = /(^|\.)bjpep\.gensee\.com$/i.test(HOST);
const IN_IFRAME = (() => {
try {
return window.self !== window.top;
} catch (_) {
return true;
}
})();
if (!IS_PEP && !IS_GENSEE) return;
if (IS_PEP && IN_IFRAME) return;
const playerLicenseCache = {
sessionid: "",
confid: "",
userid: "",
siteid: "",
otherTid: "",
alb: "",
lastType0Body: "",
hits: 0,
};
function parseSessionIdEarly(text) {
const raw = String(text || "").trim();
if (!raw) return "";
const first = raw.split(/\s+/)[0];
if (/^\d{5,}$/.test(first)) return first;
const m = raw.match(/\b(\d{6,})\b/);
return m ? m[1] : "";
}
function observePlayerLicense(url, bodyText) {
try {
const u = String(url || "");
if (!/albcmd\/license/i.test(u)) return;
playerLicenseCache.hits += 1;
const q = new URL(u, location.href);
const host = q.hostname || "";
if (host) playerLicenseCache.alb = host;
const typ = q.searchParams.get("type");
if (typ === "0") {
playerLicenseCache.lastType0Body = String(bodyText || "").slice(0, 200);
const sid = parseSessionIdEarly(bodyText);
if (sid) playerLicenseCache.sessionid = sid;
if (q.searchParams.get("confid")) playerLicenseCache.confid = q.searchParams.get("confid");
if (q.searchParams.get("userid")) playerLicenseCache.userid = q.searchParams.get("userid");
if (q.searchParams.get("siteid")) playerLicenseCache.siteid = q.searchParams.get("siteid");
}
if (typ === "1") {
const sid = q.searchParams.get("sessionid");
if (sid && /^\d{5,}$/.test(sid)) playerLicenseCache.sessionid = playerLicenseCache.sessionid || sid;
try {
const other = decodeURIComponent(q.searchParams.get("other") || "");
const tm = other.match(/tid=([a-f0-9]+)/i);
if (tm) playerLicenseCache.otherTid = tm[1];
} catch (_) {}
if (q.searchParams.get("confid")) playerLicenseCache.confid = playerLicenseCache.confid || q.searchParams.get("confid");
if (q.searchParams.get("userid")) playerLicenseCache.userid = playerLicenseCache.userid || q.searchParams.get("userid");
if (q.searchParams.get("siteid")) playerLicenseCache.siteid = playerLicenseCache.siteid || q.searchParams.get("siteid");
}
} catch (_) {}
}
if (IS_GENSEE) {
try {
const rawOpen = XMLHttpRequest.prototype.open;
const rawSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url) {
this.__rjpxUrl = String(url || "");
return rawOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function () {
try {
if (/albcmd\/license/i.test(this.__rjpxUrl || "")) {
this.addEventListener("load", () => {
observePlayerLicense(this.__rjpxUrl, this.responseText);
});
}
} catch (_) {}
return rawSend.apply(this, arguments);
};
} catch (_) {}
try {
const rawFetch = window.fetch;
if (typeof rawFetch === "function") {
window.fetch = function () {
const args = arguments;
const input = args[0];
const url = typeof input === "string" ? input : input && input.url ? input.url : "";
return rawFetch.apply(this, args).then((res) => {
try {
if (/albcmd\/license/i.test(String(url))) {
res
.clone()
.text()
.then((t) => observePlayerLicense(url, t))
.catch(() => {});
}
} catch (_) {}
return res;
});
};
}
} catch (_) {}
}
const K = {
enabled: "rjpx_auto_enabled_v1",
queue: "rjpx_course_queue_v1",
pos: "rjpx_panel_pos_v1",
collapsed: "rjpx_panel_collapsed_v1",
done: "rjpx_done_ids_v1",
current: "rjpx_current_lesson_v1",
logs: "rjpx_log_lines_v1",
localProg: "rjpx_local_progress_v1",
subject: "rjpx_selected_subject_v1",
job: "rjpx_api_job_v1",
openMode: "rjpx_open_mode_v1", // kept quietly; UI always iframe
cloudToken: CLOUD_TOKEN_KEY,
cloudLease: CLOUD_LEASE_KEY,
learningUserId: "rjpx_learning_user_id_v1",
userProfile: "rjpx_user_profile_v1",
progMigrated: "rjpx_prog_account_migrated_v1",
clearLocalDay: "rjpx_clear_local_day_v1",
};
const PANEL_NOTICE =
"\u6e29\u99a8\u63d0\u793a\uff1a\u5f53\u524d\u57f9\u8bad\u672a\u5f00\u59cb\u65f6\u4ec5\u5904\u7406\u56de\u653e\u8bfe\u7a0b\uff1b\u5237\u5b8c\u540e\u5e73\u53f0\u8fdb\u5ea6\u901a\u5e38\u9694\u65e5\u66f4\u65b0\uff0c\u4e14\u81f3\u5c11\u6ee1 12 \u5c0f\u65f6\u540e\u518d\u67e5\u770b\u3002";
const QQ_GROUP_NUMBER = "903117129";
const QQ_GROUP_LINK =
"https://qun.qq.com/universal-share/share?ac=1&authKey=rxdL6YIJ0%2FxOEemjLqTGULvl5aAfJIVQcIvkvnwvmL%2FAmpFZnSafajYHgSXMUXvx&busi_data=eyJncm91cENvZGUiOiI5MDMxMTcxMjkiLCJ0b2tlbiI6IlB2dkFGSm5XRXBrSEhtQVFTUGQzdVNZakhNWDNMbW1kODA2enpoMi9obDh4SWp0YzBDODNFaGtwRU44Z0hyU0siLCJ1aW4iOiIxMjU0MzE1MTQifQ%3D%3D&data=9oyJixSPcigCQW-saV5eXlcMwV9C6J36XySx-rDHwVwNlofvRmd2ze5sLwFtHTbYbG4nAWIUrI0qftC6aTX9xg&svctype=4&tempid=h5_group_info";
function storeGet(key, fallback) {
try {
if (typeof GM_getValue === "function") {
const v = GM_getValue(key, undefined);
if (v !== undefined) return v;
}
} catch (_) {}
try {
const raw = localStorage.getItem(key);
if (raw == null) return fallback;
return JSON.parse(raw);
} catch (_) {
return fallback;
}
}
function storeSet(key, value) {
try {
if (typeof GM_setValue === "function") GM_setValue(key, value);
} catch (_) {}
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (_) {}
}
function storeRemove(key) {
try {
if (typeof GM_deleteValue === "function") GM_deleteValue(key);
} catch (_) {}
try {
if (typeof GM_setValue === "function") GM_setValue(key, undefined);
} catch (_) {}
try {
localStorage.removeItem(key);
} catch (_) {}
}
function listProgressAccountIds() {
const ids = new Set();
const cur = String(progressAccountId() || "").trim();
if (cur) ids.add(cur);
ids.add("anon");
const saved = String(storeGet(K.learningUserId, "") || "").trim();
if (saved) ids.add(saved);
try {
if (typeof peekLiveLearningUserId === "function") {
const live = peekLiveLearningUserId();
if (live) ids.add(String(live).trim());
}
} catch (_) {}
return [...ids].filter(Boolean);
}
const state = {
enabled: !!storeGet(K.enabled, false),
companyId: "196",
subjects: [],
stages: [],
selectedSid: "",
lessons: [],
platformSections: [],
platformLessons: [],
logs: storeGet(K.logs, []) || [],
current: storeGet(K.current, null),
currentTask: "",
panelRefreshing: false,
bgBusy: false,
cloudToken: String(storeGet(K.cloudToken, "") || "").trim(),
cloudLease: "",
cloudLeaseExp: 0,
cloudTier: String(storeGet(K.cloudToken, "") || "").trim() ? "unknown" : "free",
cloudProExpireAt: 0,
tokenBoundBlocked: false,
freeVideoLimit: 2,
freeUsedVideos: 0,
proBuyUrl: PRO_BUY_URL,
finishTip: FINISH_TIP,
panelNotice: PANEL_NOTICE,
patchingPageButtons: false,
platformLessonsBySid: {},
platformSectionsBySid: {},
platformProgressInflight: {},
};
function loadQueue() {
const q = storeGet(K.queue, []);
return Array.isArray(q) ? q.map(String) : [];
}
function saveQueue(ids) {
storeSet(K.queue, (ids || []).map(String));
}
function loadDoneSet() {
migrateLegacyProgressIfNeeded();
const arr = storeGet(scopedProgKey(K.done), []);
return new Set(Array.isArray(arr) ? arr.map(String) : []);
}
function saveDoneSet(set) {
storeSet(scopedProgKey(K.done), [...set]);
}
function loadLocalProg() {
migrateLegacyProgressIfNeeded();
const o = storeGet(scopedProgKey(K.localProg), {});
return o && typeof o === "object" ? o : {};
}
function saveLocalProg(o) {
storeSet(scopedProgKey(K.localProg), o || {});
}
function clearLocalDone() {
saveDoneSet(new Set());
saveLocalProg({});
storeSet(K.done, []);
storeSet(K.localProg, {});
listProgressAccountIds().forEach((uid) => {
storeSet(K.done + "::" + uid, []);
storeSet(K.localProg + "::" + uid, {});
});
saveQueue([]);
storeSet(K.current, null);
storeSet(K.job, null);
invalidatePlatformAssignmentCache();
try {
storeSet(K.enabled, false);
state.enabled = false;
} catch (_) {}
}
function localDayKey(ts) {
const d = ts ? new Date(ts) : new Date();
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return y + "-" + m + "-" + day;
}
function clearLocalDayStoreKey() {
return scopedProgKey(K.clearLocalDay);
}
function getClearLocalDayRecord() {
const raw = storeGet(clearLocalDayStoreKey(), null);
if (!raw) return null;
if (typeof raw === "string") return { day: raw, at: 0 };
if (raw && typeof raw === "object") {
return { day: String(raw.day || ""), at: Number(raw.at) || 0 };
}
return null;
}
function hasClearedLocalToday() {
const rec = getClearLocalDayRecord();
return !!(rec && rec.day && rec.day === localDayKey());
}
function markClearedLocalToday() {
storeSet(clearLocalDayStoreKey(), { day: localDayKey(), at: Date.now() });
}
function tryClearLocalDoneWithDailyLimit() {
if (hasClearedLocalToday()) {
showPanelDialog({
title: "\u4eca\u65e5\u5df2\u6e05\u7a7a\u8fc7",
message:
"\u6e05\u7a7a\u5df2\u5b66\u6807\u8bb0\u4e00\u65e5\u4ec5\u5141\u8bb8\u4e00\u6b21\u3002\n\u5e73\u53f0\u8fdb\u5ea6\u901a\u5e38\u9694\u65e5\u624d\u4f1a\u66f4\u65b0\uff0c\u8bf7\u9694\u65e5\u67e5\u770b\u5b9e\u65f6\u5e73\u53f0\u8fdb\u5ea6\u540e\uff0c\u518d\u9488\u5bf9\u672a\u6ee1\u8bfe\u7a0b\u70b9\u300c\u6e05\u7a7a\u5df2\u5b66\u6807\u8bb0\u300d\u3002",
okText: "\u77e5\u9053\u4e86",
});
return false;
}
showPanelDialog({
title: "\u6e05\u7a7a\u5df2\u5b66\u6807\u8bb0",
message:
"\u6e05\u7a7a\u5168\u90e8\u300c\u5df2\u5b66\u5f85\u5e73\u53f0\u66f4\u65b0\u300d\u672c\u5730\u6807\u8bb0\uff1f\n\u4e0d\u5f71\u54cd\u5df2\u5199\u5165\u89c6\u9891\u670d/\u5e73\u53f0\u7684\u5b66\u65f6\u3002\n\n\u6ce8\u610f\uff1a\u6bcf\u65e5\u4ec5\u53ef\u6e05\u7a7a 1 \u6b21\uff0c\u8bf7\u8c28\u614e\u64cd\u4f5c\u3002",
cancelText: "\u53d6\u6d88",
okText: "\u786e\u5b9a\u6e05\u7a7a",
danger: true,
onOk: () => {
clearLocalDone();
markClearedLocalToday();
pruneQueueLocked();
renderCourseList();
renderChapterPreview();
_sp();
updatePanel();
log("\u5df2\u6e05\u7a7a\u672c\u5730\u5df2\u5b66\u6807\u8bb0\uff08\u4eca\u65e5\u989d\u5ea6\u5df2\u7528\uff09\uff0c\u53ef\u76f4\u63a5\u52fe\u9009\u672a\u6ee1\u8bfe\u7a0b\u91cd\u5237");
setTimeout(() => {
showPanelDialog({
title: "\u5df2\u6e05\u7a7a",
message:
"\u672c\u5730\u5df2\u5b66\u6807\u8bb0\u5df2\u6e05\u9664\u3002\u8bf7F5\u5237\u65b0\u7f51\u9875\u540e\uff0c\u5e73\u53f0\u672a\u6ee1\u7684\u8bfe\u7a0b\u73b0\u5728\u53ef\u4ee5\u52fe\u9009\uff0c\u70b9\u300c\u5168\u9009\u5fc5\u5b66\u56de\u653e\u300d\u6216\u624b\u52a8\u52fe\u9009\u540e\u5f00\u59cb\u5373\u53ef\u3002",
okText: "\u77e5\u9053\u4e86",
});
}, 0);
},
});
return true;
}
function progressAccountId() {
try {
if (typeof peekLiveLearningUserId === "function") {
const live = peekLiveLearningUserId();
if (live) return live;
}
} catch (_) {}
const saved = String(storeGet(K.learningUserId, "") || "").trim();
if (saved && saved !== "anon") return saved;
return "anon";
}
function scopedProgKey(base) {
return String(base) + "::" + progressAccountId();
}
function migrateLegacyProgressIfNeeded() {
const uid = progressAccountId();
if (!uid || uid === "anon") return;
const flag = K.progMigrated + "::" + uid;
if (storeGet(flag, false)) return;
const skProg = K.localProg + "::" + uid;
const skDone = K.done + "::" + uid;
if (!storeGet(skProg, null)) {
const legacyProg = storeGet(K.localProg, null);
if (legacyProg && typeof legacyProg === "object" && Object.keys(legacyProg).length) {
storeSet(skProg, legacyProg);
}
}
if (!storeGet(skDone, null)) {
const legacyDone = storeGet(K.done, null);
if (Array.isArray(legacyDone) && legacyDone.length) {
storeSet(skDone, legacyDone);
}
}
storeSet(flag, true);
}
function getCloudToken() {
return String(storeGet(K.cloudToken, "") || state.cloudToken || "").trim();
}
function setCloudToken(tok) {
const t = String(tok || "").trim();
state.cloudToken = t;
storeSet(K.cloudToken, t);
}
function getOpenMode() {
void storeGet(K.openMode, "iframe");
return "iframe";
}
let _playPopup = null;
function removePlayIframe() {
document.querySelectorAll("iframe[data-rjpx-play]").forEach((el) => {
try {
el.remove();
} catch (_) {}
});
}
function closePlayPopup() {
try {
if (_playPopup && !_playPopup.closed) _playPopup.close();
} catch (_) {}
_playPopup = null;
}
function removePlayContext() {
removePlayIframe();
closePlayPopup();
}
function openPlayContext(entryUrl, mode) {
removePlayIframe();
closePlayPopup();
const m = String(mode || "iframe").toLowerCase();
if (m === "popup" || m === "tab") {
try {
_playPopup = window.open(
entryUrl,
"rjpx_play_win",
"width=200,height=140,left=40,top=40,menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=no"
);
} catch (_) {
_playPopup = null;
}
if (_playPopup) {
try {
_playPopup.focus();
} catch (_) {}
return "popup";
}
try {
if (typeof GM_openInTab === "function") {
GM_openInTab(entryUrl, { active: true, insert: true, setParent: true });
return "tab";
}
} catch (_) {}
throw new Error("\u65e0\u6cd5\u6253\u5f00\u5b66\u4e60\u5c0f\u7a97\u53e3\uff0c\u8bf7\u5141\u8bb8\u672c\u7ad9\u5f39\u7a97\u540e\u91cd\u8bd5");
}
const iframe = document.createElement("iframe");
iframe.setAttribute("data-rjpx-play", "1");
iframe.setAttribute("title", "rjpx-play");
iframe.style.cssText =
"position:fixed;width:1px;height:1px;left:-100px;top:-100px;opacity:0;pointer-events:none;border:0;";
iframe.src = entryUrl;
document.documentElement.appendChild(iframe);
return "iframe";
}
function escHtml(s) {
return String(s || "")
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """);
}
const LOG_JARGON_RE =
/\b(license|sessionid|session|HTTP|http_|body|tid|albcdn|SDK|sdk|xhr|turbo|chunk|API|confid|userid|iframe|GM_|type\s*=|hits\s*=|otherTid|albcmd|gensee|lease|engine|Bearer|JSON)\b/i;
function isFriendlyLogLine(text) {
const line = String(text || "").trim();
if (!line) return false;
if (LOG_JARGON_RE.test(line)) return false;
return true;
}
function log(text) {
const line = String(text || "").trim();
if (!isFriendlyLogLine(line)) return;
console.log("[\u4eba\u6559\u770b\u8bfe] " + line);
state.logs.unshift(new Date().toLocaleTimeString() + " " + line);
if (state.logs.length > 200) state.logs.length = 200;
storeSet(K.logs, state.logs.slice(0, 80));
renderRunLog();
}
function renderRunLog() {
const box = document.getElementById("rjpx-run-log");
if (!box) return;
if (!state.logs.length) {
box.innerHTML = '
\u6682\u65e0\u65e5\u5fd7
';
return;
}
box.innerHTML = state.logs.map((t) => '' + escHtml(t) + "
").join("");
}
function clearRunLog() {
state.logs = [];
storeSet(K.logs, []);
renderRunLog();
}
function logCourse(action, title) {
const a = String(action || "").trim();
const t = String(title || "").trim();
if (!a) return;
log(t ? a + "\u300c" + t + "\u300d" : a);
}
function sleep(ms) {
return new Promise((r) => setTimeout(r, ms));
}
function parseHms(s) {
const m = String(s || "")
.trim()
.match(/^(\d+):(\d{2}):(\d{2})$/);
if (!m) return 0;
return (+m[1] * 3600 + +m[2] * 60 + +m[3]) * 1000;
}
function formatHms(ms) {
const sec = Math.max(0, Math.floor((Number(ms) || 0) / 1000));
const h = Math.floor(sec / 3600);
const m = Math.floor((sec % 3600) / 60);
const s = sec % 60;
const pad = (n) => String(n).padStart(2, "0");
return pad(h) + ":" + pad(m) + ":" + pad(s);
}
function openUrl(url) {
try {
if (typeof GM_openInTab === "function") {
GM_openInTab(url, { active: true, insert: true, setParent: true });
return;
}
} catch (_) {}
window.open(url, "_blank");
}
function _gm(url, opts) {
opts = opts || {};
return new Promise((resolve, reject) => {
if (typeof GM_xmlhttpRequest !== "function") {
reject(new Error("\u9700\u8981 Tampermonkey GM_xmlhttpRequest"));
return;
}
GM_xmlhttpRequest({
method: opts.method || "GET",
url,
headers: opts.headers || {},
data: opts.data || null,
timeout: opts.timeout || 60000,
anonymous: false,
onload: (res) => resolve(res),
onerror: (e) => reject(e || new Error("network error")),
ontimeout: () => reject(new Error("timeout")),
});
});
}
async function _gt(url, opts) {
const res = await _gm(url, opts || {});
return {
status: res.status || 0,
text: String(res.responseText || ""),
finalUrl: res.finalUrl || url,
headers: String(res.responseHeaders || ""),
};
}
function openProBuyPage() {
const url = String(state.proBuyUrl || PRO_BUY_URL).trim() || PRO_BUY_URL;
try {
if (typeof GM_openInTab === "function") {
GM_openInTab(url, { active: true, insert: true, setParent: true });
return;
}
} catch (_) {}
try {
window.open(url, "_blank");
} catch (_) {}
}
function readCookieValue(name) {
try {
const m = document.cookie.match(new RegExp("(?:^|;\\s*)" + name + "=([^;]*)"));
return m ? decodeURIComponent(m[1]) : "";
} catch (_) {
return "";
}
}
function peekLiveLearningUserId() {
const candidates = [
readCookieValue("peixun__px_uid"),
readCookieValue("peixun_uid"),
readCookieValue("peixun_userid"),
readCookieValue("PEPUSERID"),
];
for (const c of candidates) {
const s = String(c || "").trim();
if (s && /^\d{3,}$/.test(s)) return s;
}
try {
const uname = String(readCookieValue("peixun__px_uname") || "").trim();
const um = uname.match(/(\d{5,})\s*$/);
if (um && um[1]) return um[1];
} catch (_) {}
if (IS_PEP) {
try {
const html = document.documentElement ? document.documentElement.innerHTML : "";
const m =
html.match(/active_user:\s*['"](\d{5,})['"]/) ||
html.match(/partnerid=(\d{5,})/);
if (m && m[1]) return m[1];
} catch (_) {}
}
return "";
}
function invalidateCloudAuthCache(reason) {
state.cloudLease = "";
state.cloudLeaseExp = 0;
storeSet(K.cloudLease, null);
}
function onLearningUserChanged(prevId, nextId) {
invalidateCloudAuthCache();
storeSet(K.userProfile, null);
storeSet(K.learningUserId, nextId);
state.tokenBoundBlocked = false;
state.cloudTier = getCloudToken() ? "unknown" : "free";
state.cloudProExpireAt = 0;
state.freeUsedVideos = 0;
try {
renderCourseList();
renderChapterPreview();
updatePanel();
} catch (_) {}
}
function resolveLearningUserId() {
if (IS_GENSEE) {
const saved = String(storeGet(K.learningUserId, "") || "").trim();
return saved && saved !== "anon" ? saved : "";
}
const live = peekLiveLearningUserId();
const saved = String(storeGet(K.learningUserId, "") || "").trim();
if (live) {
if (saved && saved !== "anon" && saved !== live) {
onLearningUserChanged(saved, live);
log("\u68c0\u6d4b\u5230\u4eba\u6559\u8d26\u53f7\u5207\u6362\uff0c\u5df2\u91cd\u65b0\u6821\u9a8c\u6388\u6743");
} else {
storeSet(K.learningUserId, live);
}
return live;
}
if (saved && saved !== "anon") return saved;
return "";
}
function formatProExpireText(expSec) {
const ex = Number(expSec || 0);
if (!Number.isFinite(ex) || ex <= 0) return "—";
const d = new Date(ex * 1000);
if (Number.isNaN(d.getTime())) return "—";
return d.toLocaleString("zh-CN", { hour12: false });
}
function resolveProExpireSec(data) {
const raw = data && (data.pro_expires_at ?? data.proExpiresAt ?? data.pro_expire_at ?? data.proExpireAt);
const n = Number(raw || 0);
if (Number.isFinite(n) && n > 1e12) return Math.floor(n / 1000);
return Number.isFinite(n) ? Math.floor(n) : 0;
}
function persistLeaseCacheExtra(extra) {
try {
const cur = storeGet(K.cloudLease, null) || {};
storeSet(K.cloudLease, Object.assign({}, typeof cur === "object" ? cur : {}, extra || {}));
} catch (_) {}
}
function parseUserProfileFromHtml(html) {
const text = String(html || "");
const out = { name: "", phone: "", school: "", role: "", uname: "", userid: "" };
const nameM =
text.match(/\u59d3\u540d[\uff1a:]\s*([^<\r\n]+)/) ||
text.match(/container_user_gztb2020b[\s\S]{0,800}?\s*([^<]+?)\s*<\/h4>/i);
if (nameM) out.name = String(nameM[1] || "").replace(/\s+/g, "").trim();
const phoneM = text.match(/\u624b\u673a\u53f7[\uff1a:]\s*([0-9*]{6,20})/);
if (phoneM) out.phone = String(phoneM[1] || "").trim();
let schoolM =
text.match(/id=["']school["'][^>]*\bvalue=["']([^"']*)["']/i) ||
text.match(/\bvalue=["']([^"']*)["'][^>]*id=["']school["']/i) ||
text.match(/name=["']school["'][^>]*\bvalue=["']([^"']*)["']/i);
if (schoolM) out.school = String(schoolM[1] || "").trim();
const roleBlock = text.match(/