// ==UserScript==
// @name 英华学堂全系列刷课助手|自动刷课|考试自动答题
// @namespace https://github.com/iFulling/cdcasSK
// @version 4.2
// @description 英华学堂全系列刷课助手(原为成都文理学院刷课助手)🚀目前支持平台包括:【粟湾科技、海旗科技、御瑞在线学堂、如仁科技、厚荣科技】等等。😀目前已具有功能包括:视频自动播放、自动识别填充验证码、考试自动答题等功能。如有bug请留言。🐧QQ交流群:878643471
// @author iFulling
// @match *://*.yuruixxkj.com/*
// @match *://*.zjxkeji.com/*
// @match *://mooc.cdcas.com/*
// @match *://*.cdcas.com/*
// @match *://*.haiqikeji.com/*
// @match *://*/*
// @icon data:image/png;base64,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
// @connect ark.cn-beijing.volces.com
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_notification
// @grant GM_getResourceURL
// @require https://registry.npmmirror.com/onnxruntime-web/1.17.0/files/dist/ort.min.js
// @license MIT
// @run-at document-idle
// @noframes
// ==/UserScript==
const IMG_WECHAT = "data:image/jpeg;base64,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";
const IMG_ALIPAY = "data:image/jpeg;base64,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yyyyyyyyy++++++3gw888888888888888+++++qAQw8I0wIc4gQwwwIV+++++qAA0Eo8gw4EsU0QgAV+++++qAIsY0sgAQc8M4c8IV+++++qAIIEEAsAoUMYY0woV+++++qAEIskcUsskM4gUgoV+++++qAAYUs8E0UUsUAEMAV+++++qAM4gE4gC0EgkgoogV+++++qAEEgUsAtC3kYos4IV+++++qAEwsEw8TuU0g0wooV+++++qAQssM0YQ4gcU4o0IV+++++qAAcc80Q0YYswMAAAV+++++qAY40wocg400QUA0AV+++++qAQ80wsEQIEw4okgIV+++++qAEcQ4A00UQkoowYgV+++++qAYQwoE44cAcMAgYIV+++++qAIAAIAAAEMAAAMAIV+++++qA4EkMMg8cYIMUwcIV+++++vAAAAAAAAAAAAAAAAV2+++++LMMMMMMMMMMMMMMMc++++++++l98k714o1+w0+++++++++++8kO9dXeaO4Ke+++++++++++++++++++++++++++++//xAAhEQADAAICAgMBAQAAAAAAAAAAARExUSFAEEEgMGFQcf/aAAgBAwEBPxD5NpZP2P260j2NTrAtkNLBiRTqMbqG/wB+JKL+LXsr2V7K9leyvZXsr2V7K9leyvZXsr2V7K9leyvZXsr2V7K9le/hPjPEZHon2xyLCiP8HMK/zwr1TBnMRjwjMAk7gaejFs4KwZcH4P0N1/FsmU8G8PAnxW/CjkKDkU4FTNEjRSURgy2T0GrhDXLj5J1wS4G7yXwN8hP0LOKDbahZdo3YkexNoTDZu+QbvcSrgkeiLRGiNEaI0RojRGiNEaI0RojRGiNCEqvlg65g65g65g65g65g65g65g+p/aYOuYOuYOuYOuYOuYOuYOuYOuYOuYuwILLLLLLLLLLLLLLLG+X0pUhYk2QjLJSPxHkn2ZnoomXkeC8ZF6Lx43gfe//EACIRAAICAgIDAAMBAAAAAAAAAAABETEgYSFAEDBBUFFxof/aAAgBAgEBPxDJM6NJp6w74Sl/U+BKjGdQ6kXUQlDQkUvA3Ln8LH9Go1Go1Go1Go1Go1Go1Go1Go1Go1Go1Go1eZSIFkqiBRKvxBfTcJp0THhuPCadeiXA5lLP7OKDSn6P8DjmEcUOJypORyi8ZR9CaFDCm4QVtPkXHmR/rIkI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";
const CONFIG = {
VERSION: "4.2",
DEFAULT: {
URLS: "cdcas.yuruixxkj.com|cdcas.rurenkj.com|cdcas.suwankj.com|chengxikej.com|haiqikeji.com",
TIMEOUT_MINUTES: 40
},
THRESHOLDS: {
VIDEO_STUCK_CHECK_INTERVAL: 30000,
VIDEO_STUCK_MIN_DIFF: 0.5,
VIDEO_LOAD_TIMEOUT: 60,
CAPTCHA_CHECK_INTERVAL: 1000,
RELOAD_DELAY: 5000
}
};
const API = {
AI_CHAT: "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
};
const SELECTORS = {
VIDEO: "video, .video-player",
CAPTCHA_LAYER: ".layui-layer-content",
CAPTCHA_IMG: ".layui-layer-content img",
CAPTCHA_INPUT: ".layui-layer-content input",
PLAY_BUTTON: ".layui-layer-btn0",
COURSE_LINKS: 'a[target="_self"], .section-item',
TOPIC_HEAD: ".topic-head",
TOPIC_TAB_PREFIX: "#topic-tab-",
EXAM_MAIN: ".courseexamcon-main",
EXAM_INTRO_LIST: ".courseexamcon-intro ul li",
ERROR_TEXT: "body, html"
};
class Logger {
static _uiManager = null;
static setUIManager(ui) {
this._uiManager = ui;
}
static _log(level, msg) {
const time = new Date().toLocaleTimeString();
const formatted = `[${time}] [${level}] ${msg}`;
if (this._uiManager) {
this._uiManager.addLog(formatted, level.toLowerCase());
}
}
static info(msg) {
this._log('INFO', msg);
}
static error(msg) {
this._log('ERROR', msg);
}
static warn(msg) {
this._log('WARN', msg);
}
}
class Utils {
static async pause(min, max) {
if (max === undefined) {
return new Promise(resolve => setTimeout(resolve, min * 1000));
}
const delay = Math.floor(Math.random() * (max - min + 1)) + min;
return new Promise(resolve => setTimeout(resolve, delay * 1000));
}
static getBase64FromImage(img) {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
return canvas.toDataURL("image/png").split("base64,")[1];
}
}
class ConfigManager {
static get(key, defaultValue) {
return GM_getValue(key, defaultValue);
}
static set(key, value) {
GM_setValue(key, value);
}
static get urls() {
const val = this.get("刷课配置.urls", CONFIG.DEFAULT.URLS);
return val.split("|").map(u => u.trim()).filter(u => u);
}
static set urls(value) {
this.set("刷课配置.urls", value);
}
static get timeoutMinutes() {
return this.get("刷课配置.timeout_minutes", CONFIG.DEFAULT.TIMEOUT_MINUTES);
}
static set timeoutMinutes(value) {
this.set("刷课配置.timeout_minutes", value);
}
static get endpointId() {
return this.get("搜题配置.endpoint_id", "");
}
static set endpointId(value) {
this.set("搜题配置.endpoint_id", value);
}
static get apiKey() {
return this.get("搜题配置.apikey", "");
}
static set apiKey(value) {
this.set("搜题配置.apikey", value);
}
static get autoLoginCaptcha() {
return this.get("刷课配置.auto_login_captcha", false);
}
static set autoLoginCaptcha(value) {
this.set("刷课配置.auto_login_captcha", value);
}
static get autoFillAccount() {
return this.get("刷课配置.auto_fill_account", false);
}
static set autoFillAccount(value) {
this.set("刷课配置.auto_fill_account", value);
}
static get loginAccount() {
return this.get("刷课配置.login_account", "");
}
static set loginAccount(value) {
this.set("刷课配置.login_account", value);
}
static get loginPassword() {
return this.get("刷课配置.login_password", "");
}
static set loginPassword(value) {
this.set("刷课配置.login_password", value);
}
static get autoImportModels() {
return this.get("刷课配置.auto_import_models", true);
}
static set autoImportModels(value) {
this.set("刷课配置.auto_import_models", value);
}
static get videoStage() {
return this.get("刷课配置.video_stage", 1);
}
static set videoStage(value) {
this.set("刷课配置.video_stage", value);
}
}
class UIManager {
constructor() {
this.panel = null;
this.logPopup = null;
this.logContent = null;
this._isDragging = false;
this._dragOffset = { x: 0, y: 0 };
}
init() {
this._registerMenus();
this._createStatusPanel();
this._createLogPopup();
this._injectStyles();
Logger.setUIManager(this);
Logger.info("UI初始化完成");
}
_registerMenus() {
GM_registerMenuCommand("显示/隐藏日志", () => this.toggleLogPopup());
}
_createStatusPanel() {
const div = document.createElement("div");
div.id = "sk-status-panel";
div.className = "sk-panel";
div.textContent = "刷课助手准备就绪";
document.body.appendChild(div);
this.panel = div;
}
_createLogPopup() {
const popup = document.createElement("div");
popup.id = "sk-log-popup";
popup.className = "sk-log-popup";
popup.innerHTML = `
${(window.location.pathname.includes("/login")) ? '' : ''}
${window.location.pathname === "/user/exam" ? '' : ''}
常见问题与提示
1. 关于平台:网站界面要和 成都文理学院在线学堂 差不多,相差太大就不适配。如学习通、智慧树等平台就无法使用。
2. 没有视频:如果视频加载不出来,可以试试关闭代理。如果还是没有视频,就切换网络。如果还是没有,那就是网课的问题。
3. 多端运行:本脚本在电脑、手机、平板都能运行,但是需要先安装油猴或脚本猫才能运行。如果不起作用,可以看看 脚本不生效。
4. 版本冲突:请确保已删除或禁用旧版本的刷课脚本。
5. 保持前台:如果发现学时没变,播放视频时将浏览器置于前台运行。
6. 补刷不跳转:浏览器默认拦截弹出式窗口,需要浏览器设置里允许。
7. 验证码识别:验证码识别需要下载机器视觉模型,每个网站需要下载一次。如果下载比较慢,可以试试科学上网。如果清除了浏览器缓存就需要重新下载。
考试搜题说明
1. AI 提示:答案由 AI 生成,不能保证 100% 正确。分数高低与作者无关,建议搜完后人工复核。
2. 配置教程:
视频教程 |
获取搜题接入点ID和API Key
交流与支持
更多视频教程及问题反馈,欢迎加入脚本交流群:
点击加入 QQ 群
🥣 请作者点一份拼好饭
二维码是自愿捐赠!
插件完全免费,但是依然需要一台服务器来处理数据。
如果您觉得它对您有所帮助,欢迎打赏支持后续开发维护!
微信
支付宝
英华学堂全系列刷课助手(原为成都文理学院刷课助手) 使用说明
1. 您使用本插件,即表示您同意本使用条款;如果您不同意,请勿使用本插件。
2. 此脚本仅用于学习研究,您必须在下载后24小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3. 请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4. 本人对此脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害(如封禁、限制等)。
5. 若有信息功能侵犯到您的权益,请及时联系作者。
6. 任何以任何方式查看此脚本的人或直接或间接使用此脚本的使用者都应仔细阅读此条款。
7. 本人保留随时更改或补充此条款的权利,一旦您使用或复制或修改了此脚本,即视为您已接受此免责条款。
`;
document.body.appendChild(popup);
this.logPopup = popup;
this.logContent = popup.querySelector(".sk-log-content");
const icon = document.createElement("div");
icon.id = "sk-min-icon";
icon.className = "sk-min-icon hidden";
icon.innerHTML = "刷课
助手";
icon.title = "打开刷课助手";
document.body.appendChild(icon);
this.minIcon = icon;
const header = popup.querySelector(".sk-log-header");
header.addEventListener("mousedown", (e) => this._onDragStart(e));
document.addEventListener("mousemove", (e) => this._onDragMove(e));
document.addEventListener("mouseup", () => this._onDragEnd());
popup.querySelector(".sk-log-close").addEventListener("click", () => this.toggleLogPopup());
popup.querySelector(".sk-log-clear").addEventListener("click", () => this.clearLog());
const recaptchaBtn = popup.querySelector(".sk-recaptcha");
if (recaptchaBtn) {
recaptchaBtn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("sk:recaptcha_login"));
});
}
const examStartBtn = popup.querySelector(".sk-exam-start");
const examStopBtn = popup.querySelector(".sk-exam-stop");
if (examStartBtn) {
examStartBtn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("sk:exam_start"));
});
}
if (examStopBtn) {
examStopBtn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("sk:exam_stop"));
});
}
const checkModels = async () => {
try {
const engine = new LocalOcrEngine(this);
const sn = popup.querySelector("#status-onnx");
const sj = popup.querySelector("#status-json");
const sw = popup.querySelector("#status-wasm");
if (sn && sj && sw) {
const onnx = await engine._getFromDB("common.onnx");
const json = await engine._getFromDB("charsets.json");
const wasm = await engine._getFromDB("ort-wasm.wasm");
sn.textContent = onnx ? "已就绪" : "未导入";
sn.style.color = onnx ? "#4ec9b0" : "#f48771";
sj.textContent = json ? "已就绪" : "未导入";
sj.style.color = json ? "#4ec9b0" : "#f48771";
sw.textContent = wasm ? "已就绪" : "未导入";
sw.style.color = wasm ? "#4ec9b0" : "#f48771";
}
const autoFillTip = popup.querySelector("#cfg-auto-fill-tip");
const autoFillFields = popup.querySelector("#cfg-auto-fill-fields");
const autoFillFieldsPwd = popup.querySelector("#cfg-auto-fill-fields-pwd");
const syncVisibility = () => {
const show = popup.querySelector("#cfg-auto-fill-account")?.checked;
if (autoFillTip) autoFillTip.style.display = show ? "block" : "none";
if (autoFillFields) autoFillFields.style.display = show ? "block" : "none";
if (autoFillFieldsPwd) autoFillFieldsPwd.style.display = show ? "block" : "none";
};
syncVisibility();
} catch (e) { }
};
const autoImportVisualModels = async (force = false) => {
if (!force && !ConfigManager.autoImportModels) {
const engine = new LocalOcrEngine(this);
const [onnx, json, wasm] = await Promise.all([
engine._getFromDB("common.onnx"),
engine._getFromDB("charsets.json"),
engine._getFromDB("ort-wasm.wasm")
]);
if (onnx && json && wasm) return;
Logger.warn("自动导入已关闭,检测到模型缺失,请手动导入");
return;
}
try {
const engine = new LocalOcrEngine(this);
const [onnx, json, wasm] = await Promise.all([
engine._getFromDB("common.onnx"),
engine._getFromDB("charsets.json"),
engine._getFromDB("ort-wasm.wasm")
]);
const missing = [];
if (!onnx) missing.push({ key: "common.onnx", type: "arraybuffer", name: "视觉特征模型(ONNX)" });
if (!json) missing.push({ key: "charsets.json", type: "json", name: "字符集(JSON)" });
if (!wasm) missing.push({ key: "ort-wasm.wasm", type: "arraybuffer", name: "推理引擎(WASM)" });
if (!missing.length) {
Logger.info("视觉模型已存在,无需自动导入");
await checkModels();
return;
}
if (!ConfigManager.autoImportModels) {
Logger.info("自动导入视觉模型已关闭,但当前页面仍需要模型,请手动在配置面板中导入");
await checkModels();
return;
}
this.notify("准备导入", "正在自动导入视觉模型...");
for (const item of missing) {
const urls = item.type === "json" ? engine.charsetsUrls : (item.key === "ort-wasm.wasm" ? [
"https://registry.npmmirror.com/onnxruntime-web/1.17.0/files/dist/ort-wasm.wasm",
"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.17.0/dist/ort-wasm.wasm",
"https://unpkg.com/onnxruntime-web@1.17.0/dist/ort-wasm.wasm"
] : engine.modelUrls);
const data = await engine.fetchWithFallback(urls, item.type, item.key);
if (!data) throw new Error(`${item.name} 下载失败`);
Logger.info(`自动导入成功:${item.name}`);
}
this.notify("导入成功", "视觉模型自动导入完成!");
} catch (err) {
Logger.warn(`自动导入视觉模型失败:${err.message}`);
this.notify("自动导入失败", err.message);
} finally {
await checkModels();
}
};
setTimeout(async () => {
await checkModels();
await autoImportVisualModels();
}, 500);
const bindImport = (btnId, fileId, key, isJson, name) => {
const btn = popup.querySelector(btnId);
const fileInput = popup.querySelector(fileId);
if (btn && fileInput) {
btn.addEventListener("click", () => fileInput.click());
fileInput.addEventListener("change", async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
this.notify("准备导入", `正在读取 ${name} 文件...`);
let data;
if (isJson) {
const text = await file.text();
data = JSON.parse(text);
} else {
data = await file.arrayBuffer();
}
const engine = new LocalOcrEngine(this);
const success = await engine._saveToDB(key, data);
if (success) {
this.notify("导入成功", `${name} 导入完成!将会在下次触发验证码时生效。`);
Logger.info(`已通过本地文件导入 ${name}`);
await checkModels();
} else {
this.notify("导入失败", "写入本地缓存失败!");
}
} catch (err) {
this.notify("导入失败", "读取文件或格式错误:" + err.message);
Logger.error(`导入 ${name} 异常: ` + err.message);
}
fileInput.value = '';
});
}
};
bindImport("#btn-import-onnx", "#file-import-onnx", "common.onnx", false, "视觉特征模型(ONNX)");
bindImport("#btn-import-json", "#file-import-json", "charsets.json", true, "字符集(JSON)");
bindImport("#btn-import-wasm", "#file-import-wasm", "ort-wasm.wasm", false, "推理引擎(WASM)");
const autoImportSwitch = popup.querySelector("#cfg-auto-import");
if (autoImportSwitch) {
autoImportSwitch.addEventListener("change", () => {
ConfigManager.autoImportModels = autoImportSwitch.checked;
if (autoImportSwitch.checked) {
autoImportVisualModels(true);
} else {
this.notify("已关闭", "自动导入视觉模型已关闭,后续不会自动补全缺失模型");
Logger.info("自动导入视觉模型已关闭");
}
});
}
const autoFillSwitch = popup.querySelector("#cfg-auto-fill-account");
const autoFillTip = popup.querySelector("#cfg-auto-fill-tip");
const autoFillFields = popup.querySelector("#cfg-auto-fill-fields");
const autoFillFieldsPwd = popup.querySelector("#cfg-auto-fill-fields-pwd");
const updateAutoFillVisibility = () => {
const show = !!autoFillSwitch?.checked;
if (autoFillTip) autoFillTip.style.display = show ? "block" : "none";
if (autoFillFields) autoFillFields.style.display = show ? "block" : "none";
if (autoFillFieldsPwd) autoFillFieldsPwd.style.display = show ? "block" : "none";
};
if (autoFillSwitch) {
autoFillSwitch.addEventListener("change", updateAutoFillVisibility);
updateAutoFillVisibility();
}
icon.addEventListener("click", () => {
this.toggleLogPopup();
if (!this.logPopup.classList.contains("hidden")) checkModels();
});
popup.querySelectorAll(".sk-tab-btn").forEach(btn => {
btn.addEventListener("click", (e) => {
const tab = e.target.dataset.tab;
popup.querySelectorAll(".sk-tab-btn").forEach(b => b.classList.remove("active"));
e.target.classList.add("active");
popup.querySelectorAll(".sk-tab-content").forEach(c => c.classList.remove("active"));
popup.querySelector(`#sk-tab-${tab}`).classList.add("active");
});
});
popup.querySelector("#cfg-save").addEventListener("click", () => {
ConfigManager.timeoutMinutes = parseInt(popup.querySelector("#cfg-timeout").value) || 0;
ConfigManager.endpointId = popup.querySelector("#cfg-endpoint").value;
ConfigManager.apiKey = popup.querySelector("#cfg-apikey").value;
ConfigManager.autoLoginCaptcha = popup.querySelector("#cfg-auto-login").checked;
ConfigManager.autoImportModels = popup.querySelector("#cfg-auto-import").checked;
ConfigManager.autoFillAccount = popup.querySelector("#cfg-auto-fill-account").checked;
ConfigManager.loginAccount = popup.querySelector("#cfg-login-account")?.value || "";
ConfigManager.loginPassword = popup.querySelector("#cfg-login-password")?.value || "";
const rawUrls = popup.querySelector("#cfg-urls").value;
ConfigManager.urls = rawUrls.split('\n').map(u => u.trim()).filter(u => u).join('|');
this.notify("成功", "配置已保存,刷新页面生效");
setTimeout(() => location.reload(), 1000);
});
}
updateToolbar() {
const toolbar = this.logPopup?.querySelector(".sk-log-toolbar");
if (!toolbar) return;
const pathname = window.location.pathname;
const href = window.location.href;
toolbar.innerHTML = `
${(href.includes("/login")) ? '' : ''}
${pathname === "/user/exam" ? '' : ''}
`;
const clearBtn = toolbar.querySelector(".sk-log-clear");
if (clearBtn) clearBtn.onclick = () => this.clearLog();
const recaptchaBtn = toolbar.querySelector(".sk-recaptcha");
if (recaptchaBtn) {
recaptchaBtn.onclick = () => document.dispatchEvent(new CustomEvent("sk:recaptcha_login"));
}
const examStartBtn = toolbar.querySelector(".sk-exam-start");
const examStopBtn = toolbar.querySelector(".sk-exam-stop");
if (examStartBtn) {
examStartBtn.onclick = () => document.dispatchEvent(new CustomEvent("sk:exam_start"));
}
if (examStopBtn) {
examStopBtn.onclick = () => document.dispatchEvent(new CustomEvent("sk:exam_stop"));
}
}
_onDragStart(e) {
if (e.target.tagName === 'BUTTON') return;
this._isDragging = true;
const rect = this.logPopup.getBoundingClientRect();
this._dragOffset = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
this.logPopup.style.cursor = "grabbing";
}
_onDragMove(e) {
if (!this._isDragging) return;
const x = e.clientX - this._dragOffset.x;
const y = e.clientY - this._dragOffset.y;
this.logPopup.style.left = `${Math.max(0, x)}px`;
this.logPopup.style.top = `${Math.max(0, y)}px`;
this.logPopup.style.right = "auto";
this.logPopup.style.bottom = "auto";
}
_onDragEnd() {
this._isDragging = false;
if (this.logPopup) {
this.logPopup.style.cursor = "default";
}
}
_injectStyles() {
GM_addStyle(`
.sk-panel {
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 10px 15px;
border-radius: 8px;
z-index: 999999;
font-size: 14px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
transition: all 0.3s;
max-width: 300px;
pointer-events: none;
}
.sk-log-popup {
position: fixed;
top: 20px;
left: 20px;
width: 420px;
max-height: 500px;
background: #1e1e1e;
border: 1px solid #3c3c3c;
border-radius: 8px;
z-index: 999998;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
display: flex;
flex-direction: column;
overflow: hidden;
}
.sk-log-popup.hidden {
display: none;
}
.sk-log-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: #2d2d2d;
border-bottom: 1px solid #3c3c3c;
cursor: grab;
user-select: none;
}
.sk-log-title {
color: #fff;
font-weight: bold;
}
.sk-log-actions {
display: flex;
gap: 8px;
}
.sk-log-btn {
background: #3c3c3c;
color: #fff;
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
}
.sk-log-btn.active {
background: #0e639c;
}
.sk-log-btn.primary {
background: #0e639c;
width: 100%;
padding: 8px;
margin-top: 10px;
}
.sk-log-btn:hover {
opacity: 0.8;
}
.sk-tab-content {
display: none;
flex: 1;
overflow-y: auto;
padding: 0;
max-height: 420px;
}
.sk-tab-content.active {
display: flex;
flex-direction: column;
}
.sk-log-toolbar {
padding: 4px 8px;
border-bottom: 1px solid #2d2d2d;
background: #252526;
}
.sk-log-content {
flex: 1;
overflow-y: auto;
padding: 8px 12px;
min-height: 150px;
}
.sk-config-item {
padding: 8px 12px;
border-bottom: 1px solid #2d2d2d;
}
.sk-config-item label {
display: block;
color: #ccc;
margin-bottom: 4px;
}
.sk-config-item input, .sk-config-item textarea {
width: 100%;
background: #2d2d2d;
border: 1px solid #3c3c3c;
color: #fff;
padding: 4px;
border-radius: 4px;
box-sizing: border-box;
}
.sk-config-actions {
padding: 12px;
}
.sk-log-item {
padding: 4px 0;
border-bottom: 1px solid #2d2d2d;
word-break: break-all;
}
.sk-log-item.info { color: #9cdcfe; }
.sk-log-item.warn { color: #dcdcaa; }
.sk-log-item.error { color: #f48771; }
.sk-min-icon {
position: fixed;
top: 20px;
left: 20px;
width: 40px;
height: 40px;
background: #1e1e1e;
border: 1px solid #3c3c3c;
border-radius: 50%;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 999998;
font-size: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
transition: transform 0.2s;
}
.sk-min-icon:hover {
transform: scale(1.1);
background: #2d2d2d;
}
/* 滚动条美化 */
.sk-tab-content::-webkit-scrollbar,
.sk-log-content::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.sk-tab-content::-webkit-scrollbar-track,
.sk-log-content::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.sk-tab-content::-webkit-scrollbar-thumb,
.sk-log-content::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
.sk-tab-content::-webkit-scrollbar-thumb:hover,
.sk-log-content::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.sk-min-icon.hidden {
display: none;
}
/* Switch 开关样式 */
.sk-switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
.sk-switch input {
opacity: 0;
width: 0;
height: 0;
}
.sk-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 20px;
}
.sk-slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .sk-slider {
background-color: #0e639c;
}
input:checked + .sk-slider:before {
transform: translateX(20px);
}
`);
}
addLog(msg, level = 'info') {
if (!this.logContent) return;
const item = document.createElement("div");
item.className = `sk-log-item ${level}`;
item.textContent = msg;
this.logContent.appendChild(item);
this.logContent.scrollTop = this.logContent.scrollHeight;
}
clearLog() {
if (this.logContent) {
this.logContent.innerHTML = '';
}
}
toggleLogPopup() {
if (this.logPopup) {
const isHidden = this.logPopup.classList.toggle("hidden");
if (this.minIcon) {
this.minIcon.classList.toggle("hidden", !isHidden);
}
}
}
updateStatus(text) {
if (this.panel) {
this.panel.textContent = text;
this.panel.style.display = "block";
}
}
notify(title, text) {
GM_notification({ title, text, timeout: 3000 });
}
}
class VideoPlayer {
constructor(ui) {
this.ui = ui;
this.video = null;
this.stuckTimer = null;
this.lastTime = 0;
this.stuckCount = 0;
}
async start() {
this.video = document.querySelector(SELECTORS.VIDEO);
if (!this.video) return false;
this._setupVideo();
this._startStuckCheck();
try {
await this.video.play();
this.ui.updateStatus("视频播放中...");
Logger.info("视频开始播放");
} catch (e) {
Logger.error("播放失败: " + e.message);
}
return true;
}
_setupVideo() {
this.video.muted = true;
this.video.playbackRate = 1.0;
this.video.onended = () => this._onEnded();
}
_onEnded() {
Logger.info("当前视频播放结束");
this.ui.updateStatus("视频播放结束,准备切换...");
this._stopStuckCheck();
document.dispatchEvent(new CustomEvent("sk:video_ended"));
}
_startStuckCheck() {
this._stopStuckCheck();
this.lastTime = this.video.currentTime;
this.stuckCount = 0;
this.stuckTimer = setInterval(() => {
if (!this.video) return;
const currentTime = this.video.currentTime;
const diff = Math.abs(currentTime - this.lastTime);
if (diff < CONFIG.THRESHOLDS.VIDEO_STUCK_MIN_DIFF && !this.video.paused) {
this.stuckCount++;
this.ui.updateStatus(`视频卡顿检测 (${this.stuckCount})`);
if (this.stuckCount > 3) {
this.ui.notify("警告", "视频卡死,尝试刷新页面");
setTimeout(() => location.reload(), 2000);
}
} else {
this.stuckCount = 0;
}
this.lastTime = currentTime;
}, CONFIG.THRESHOLDS.VIDEO_STUCK_CHECK_INTERVAL);
}
_stopStuckCheck() {
if (this.stuckTimer) {
clearInterval(this.stuckTimer);
this.stuckTimer = null;
}
}
}
class LocalOcrEngine {
constructor(ui) {
this.ui = ui;
this.session = null;
this.charsets = [];
this.initialized = false;
this.dbName = "iFulling_OCR_DB";
this.storeName = "models";
const GH_USER = "iFulling";
const GH_REPO = "cdcasSK";
const GH_BRANCH = "main";
const GH_DIR = "onnx";
const cdnPrefixes = [
`https://raw.githubusercontent.com/${GH_USER}/${GH_REPO}/${GH_BRANCH}/${GH_DIR}/`,
`https://cdn.jsdelivr.net/gh/${GH_USER}/${GH_REPO}@${GH_BRANCH}/${GH_DIR}/`,
`https://gh-proxy.org/https://raw.githubusercontent.com/${GH_USER}/${GH_REPO}/${GH_BRANCH}/${GH_DIR}/`,
`https://ghfast.top/https://raw.githubusercontent.com/${GH_USER}/${GH_REPO}/${GH_BRANCH}/${GH_DIR}/`,
`https://ghproxy.com/https://raw.githubusercontent.com/${GH_USER}/${GH_REPO}/${GH_BRANCH}/${GH_DIR}/`,
`https://raw.kkgithub.com/${GH_USER}/${GH_REPO}/${GH_BRANCH}/${GH_DIR}/`
];
this.modelUrls = cdnPrefixes.map(prefix => prefix + "common.onnx");
this.charsetsUrls = cdnPrefixes.map(prefix => prefix + "charsets.json");
}
_openDB() {
return new Promise((resolve) => {
const request = indexedDB.open(this.dbName, 1);
request.onerror = () => resolve(null);
request.onsuccess = (e) => resolve(e.target.result);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
});
}
async _getFromDB(key) {
try {
const db = await this._openDB();
if (!db) return null;
return new Promise(resolve => {
const transaction = db.transaction([this.storeName], "readonly");
const store = transaction.objectStore(this.storeName);
const req = store.get(key);
req.onsuccess = () => resolve(req.result);
req.onerror = () => resolve(null);
});
} catch (e) { return null; }
}
async _saveToDB(key, data) {
try {
const db = await this._openDB();
if (!db) return false;
return new Promise(resolve => {
const transaction = db.transaction([this.storeName], "readwrite");
const store = transaction.objectStore(this.storeName);
const req = store.put(data, key);
req.onsuccess = () => resolve(true);
req.onerror = () => resolve(false);
});
} catch (e) { return false; }
}
async fetchWithFallback(urls, responseType = 'arraybuffer', cacheKey = null) {
if (cacheKey) {
const cached = await this._getFromDB(cacheKey);
if (cached) {
Logger.info(`读取视觉模型数据:${cacheKey}`);
if (this.ui) this.ui.updateStatus(`[缓存] 命中视觉模型:${cacheKey}`);
return cached;
}
}
for (const url of urls) {
try {
Logger.warn(`正在尝试请求视觉识别模型:${url}`);
const response = await fetch(url);
if (!response.ok) continue;
Logger.info(`视觉识别模型请求成功,下载中......`);
if (responseType === 'json') {
const data = await response.json();
if (cacheKey) await this._saveToDB(cacheKey, data);
return data;
}
const contentLength = response.headers.get('content-length');
const total = contentLength ? parseInt(contentLength, 10) : 0;
let loaded = 0;
const chunks = [];
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
if (this.ui) {
if (total) {
const percent = ((loaded / total) * 100).toFixed(1);
this.ui.updateStatus(`视觉识别模型下载进度: ${percent}% (已下载 ${(loaded / 1024 / 1024).toFixed(1)}MB)`);
} else {
this.ui.updateStatus(`正在下载视觉识别模型: ${(loaded / 1024 / 1024).toFixed(1)}MB`);
}
}
}
const arrayBuffer = new Uint8Array(loaded);
let offset = 0;
for (let c of chunks) {
arrayBuffer.set(c, offset);
offset += c.length;
}
if (cacheKey) await this._saveToDB(cacheKey, arrayBuffer.buffer);
return arrayBuffer.buffer;
} catch (e) {
Logger.warn(`从 ${url} 获取失败,尝试下一个...`);
}
}
throw new Error("所有 CDN 下载失败,请检查网络!");
}
async init() {
if (this.initialized) return;
Logger.info("正在加载视觉识别模型与引擎...");
if (this.ui) this.ui.updateStatus("开始加载视觉识别模型...");
try {
ort.env.wasm.numThreads = 1;
let wasmBuffer = await this._getFromDB("ort-wasm.wasm");
if (wasmBuffer) {
Logger.info("从本地缓存读取 WASM 引擎...");
} else {
if (!ConfigManager.autoImportModels) {
Logger.warn("本地没有 WASM 引擎,且自动导入已关闭,无法继续加载");
if (this.ui) this.ui.updateStatus("缺少 WASM 引擎,请手动导入");
return;
}
Logger.warn("未找到本地 WASM 引擎,正在通过网络下载并缓存 (只需一次)...");
if (this.ui) this.ui.updateStatus("正在下载 WASM 引擎...");
const wasmFallbackUrls = [
"https://registry.npmmirror.com/onnxruntime-web/1.17.0/files/dist/ort-wasm.wasm",
"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.17.0/dist/ort-wasm.wasm",
"https://unpkg.com/onnxruntime-web@1.17.0/dist/ort-wasm.wasm"
];
wasmBuffer = await this.fetchWithFallback(wasmFallbackUrls, 'arraybuffer', 'ort-wasm.wasm');
Logger.info("WASM 引擎下载并缓存成功!");
}
if (wasmBuffer) {
const wasmBlob = new Blob([wasmBuffer], { type: 'application/wasm' });
const wasmUrl = URL.createObjectURL(wasmBlob);
ort.env.wasm.wasmPaths = {
'ort-wasm.wasm': wasmUrl,
'ort-wasm-simd.wasm': wasmUrl,
'ort-wasm-threaded.wasm': wasmUrl,
'ort-wasm-simd-threaded.wasm': wasmUrl
};
} else {
throw new Error("WASM 引擎获取失败,所有 CDN 均不可用");
}
let modelBuffer = await this._getFromDB("common.onnx");
if (!modelBuffer) {
if (!ConfigManager.autoImportModels) {
Logger.warn("本地没有视觉模型,且自动导入已关闭,无法继续加载");
if (this.ui) this.ui.updateStatus("缺少视觉模型,请手动导入");
return;
}
modelBuffer = await this.fetchWithFallback(this.modelUrls, 'arraybuffer', 'common.onnx');
}
let charsetsData = await this._getFromDB("charsets.json");
if (!charsetsData) {
if (!ConfigManager.autoImportModels) {
Logger.warn("本地没有字符集,且自动导入已关闭,无法继续加载");
if (this.ui) this.ui.updateStatus("缺少字符集,请手动导入");
return;
}
charsetsData = await this.fetchWithFallback(this.charsetsUrls, 'json', 'charsets.json');
}
this.charsets = charsetsData;
if (this.ui) this.ui.updateStatus("正在构建推理模型...");
this.session = await ort.InferenceSession.create(modelBuffer, { executionProviders: ['wasm'] });
this.initialized = true;
Logger.info("视觉识别模型完全加载完毕!");
if (this.ui) this.ui.updateStatus("视觉识别核心加载完毕可用!");
} catch (e) {
Logger.error("视觉识别模型初始化失败:" + e.message);
if (this.ui) this.ui.updateStatus("视觉识别模型失败!");
}
}
preprocess(imgElement) {
const canvas = document.createElement("canvas");
const width = imgElement.naturalWidth || imgElement.width;
const height = imgElement.naturalHeight || imgElement.height;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, width, height);
ctx.drawImage(imgElement, 0, 0);
const imageData = ctx.getImageData(0, 0, width, height);
const gray = new Float32Array(width * height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3] / 255;
const bg = 255 * (1 - a);
gray[i / 4] = (0.2126 * (r * a + bg) + 0.7152 * (g * a + bg) + 0.0722 * (b * a + bg)) / 255;
}
const targetHeight = 64;
const targetWidth = Math.floor(width * (targetHeight / height));
const resized = new Float32Array(targetWidth * targetHeight);
for (let y = 0; y < targetHeight; y++) {
for (let x = 0; x < targetWidth; x++) {
const srcX = x * (width / targetWidth);
const srcY = y * (height / targetHeight);
const x1 = Math.floor(srcX), x2 = Math.min(x1 + 1, width - 1);
const y1 = Math.floor(srcY), y2 = Math.min(y1 + 1, height - 1);
const dx = srcX - x1, dy = srcY - y1;
const val = gray[y1 * width + x1] * (1 - dx) * (1 - dy) +
gray[y1 * width + x2] * dx * (1 - dy) +
gray[y2 * width + x1] * (1 - dx) * dy +
gray[y2 * width + x2] * dx * dy;
resized[y * targetWidth + x] = val;
}
}
return { tensorData: resized, width: targetWidth };
}
async recognize(imgElement) {
if (!this.initialized) await this.init();
if (!this.session) return null;
try {
const { tensorData, width } = this.preprocess(imgElement);
const tensor = new ort.Tensor("float32", tensorData, [1, 1, 64, width]);
const results = await this.session.run({ input1: tensor });
const output = results.output.data;
const text = [];
let lastIndex = -1;
for (let i = 0; i < output.length; i++) {
const idx = Math.round(Number(output[i]));
if (idx === lastIndex) continue;
lastIndex = idx;
if (idx <= 0 || idx >= this.charsets.length) continue;
text.push(this.charsets[idx]);
}
return text.join("");
} catch (e) {
Logger.error("识别出错: " + e.message);
return null;
}
}
}
class CaptchaHandler {
constructor(ui) {
this.ui = ui;
this.observer = null;
this.isRecognizing = false;
this.isLoginRecognizing = false;
this.ocrEngine = new LocalOcrEngine(ui);
}
startObserver() {
if (this.observer) return;
this.observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType !== 1) continue;
const layer = node.matches(SELECTORS.CAPTCHA_LAYER) ? node : node.querySelector(SELECTORS.CAPTCHA_LAYER);
if (layer && layer.querySelector("img") && layer.querySelector("input")) {
Logger.info("MutationObserver 检测到验证码弹窗");
this.checkAndSolve();
}
}
}
});
this.observer.observe(document.body, { childList: true, subtree: true });
Logger.info("已启动 MutationObserver 监听验证码");
}
async checkAndSolve() {
if (this.isRecognizing) return false;
const layer = document.querySelector(SELECTORS.CAPTCHA_LAYER);
if (!layer || layer.offsetParent === null || !layer.querySelector("img") || !layer.querySelector("input")) {
return false;
}
this.isRecognizing = true;
try {
this.ui.updateStatus("检测到验证码,准备识别...");
await Utils.pause(2);
const imgs = document.querySelectorAll(SELECTORS.CAPTCHA_IMG);
let img = null;
if (imgs.length > 0) {
img = imgs[0].style.opacity === '0' && imgs.length > 1 ? imgs[1] : imgs[0];
}
if (!img) {
this.isRecognizing = false;
return false;
}
const code = await this.ocrEngine.recognize(img);
if (code) {
this.ui.updateStatus(`识别结果: ${code},正在提交...`);
Logger.info(`验证码识别结果: ${code}`);
const inputs = document.querySelectorAll(SELECTORS.CAPTCHA_INPUT);
let input = null;
if (inputs.length > 0) {
input = inputs[0].style.display === 'none' && inputs.length > 1 ? inputs[1] : inputs[0];
}
if (input) {
input.dispatchEvent(new MouseEvent('mousedown'));
input.value = code;
input.dispatchEvent(new Event('input'));
await Utils.pause(1);
const btn = document.querySelector(SELECTORS.PLAY_BUTTON);
if (btn) {
btn.click();
this.ui.updateStatus("验证码已提交,等待系统确认...");
for (let i = 0; i < 8; i++) {
await Utils.pause(0.5);
const checkLayer = document.querySelector(SELECTORS.CAPTCHA_LAYER);
if (!checkLayer || checkLayer.offsetParent === null) break;
}
}
this.ui.updateStatus("视频播放中...");
this.isRecognizing = false;
return true;
}
}
this.isRecognizing = false;
return false;
} catch (e) {
this.isRecognizing = false;
return false;
}
}
async checkAndSolveLogin() {
if (!ConfigManager.autoLoginCaptcha || this.isLoginRecognizing) return;
this.isLoginRecognizing = true;
this.ui.updateStatus("正在等待登录验证码加载...");
const getElements = () => {
let img = document.querySelector("#codeImg") || document.querySelector(".captcha-image img");
let input = document.querySelector("#code") || document.querySelector('input[placeholder="请输入验证码"]');
return { img, input };
};
let { img, input } = getElements();
let attempts = 0;
while ((!img || !input) && attempts < 20) {
await Utils.pause(0.5);
({ img, input } = getElements());
attempts++;
}
if (img && input) {
this.ui.updateStatus("检测到登录验证码,准备识别...");
await Utils.pause(1);
const code = await this.ocrEngine.recognize(img);
const currentHref = window.location.href;
if (!currentHref.includes("/login")) {
this.isLoginRecognizing = false;
return;
}
if (code) {
this.ui.updateStatus(`登录验证码识别结果: ${code}`);
Logger.info(`登录验证码识别结果: ${code}`);
input.value = code;
input.dispatchEvent(new Event('input'));
}
} else {
this.ui.updateStatus("未检测到登录验证码");
}
this.isLoginRecognizing = false;
}
async solveLoginCaptcha() {
Logger.info("手动触发重新识别登录验证码");
const img = document.querySelector("#codeImg") || document.querySelector(".captcha-image img");
const input = document.querySelector("#code") || document.querySelector('input[placeholder="请输入验证码"]');
if (!img || !input) {
this.ui.updateStatus("未检测到登录验证码元素");
return;
}
img.click();
await Utils.pause(1.5);
this.ui.updateStatus("正在重新识别验证码...");
const code = await this.ocrEngine.recognize(img);
if (code) {
this.ui.updateStatus(`登录验证码识别结果: ${code}`);
Logger.info(`登录验证码识别结果: ${code}`);
input.value = code;
input.dispatchEvent(new Event('input'));
} else {
this.ui.updateStatus("验证码识别失败");
Logger.warn("手动重新识别验证码失败");
}
}
}
class ExamSolver {
constructor(ui) {
this.ui = ui;
this.running = false;
}
async start() {
if (this.running) return;
this.running = true;
this.ui.updateStatus("开始搜题...");
Logger.info("开始搜题...");
await this._solveLoop();
}
stop() {
this.running = false;
this.ui.updateStatus("搜题已停止");
Logger.info("检测到手动停止,等待本题搜索完毕");
}
async _solveLoop() {
const introList = document.querySelectorAll(SELECTORS.EXAM_INTRO_LIST);
let currentIndex = this._getCurrentIndex();
for (; currentIndex < introList.length; currentIndex++) {
if (!this.running) break;
const tab = document.querySelector(SELECTORS.TOPIC_TAB_PREFIX + currentIndex);
if (!tab) continue;
this.ui.updateStatus(`正在搜索第 ${currentIndex + 1} 题...`);
Logger.info(`正在搜索第 ${currentIndex + 1} 题...`);
await Utils.pause(1);
const type = parseInt(tab.querySelector(SELECTORS.EXAM_MAIN)?.dataset.type);
if ([1, 2, 3].includes(type)) {
await this._handleQuestion(tab, type, currentIndex);
} else {
this.ui.updateStatus("未知题型,跳过...");
}
await Utils.pause(3);
this._nextQuestion(tab, currentIndex);
}
this.running = false;
this.ui.updateStatus("搜题结束");
Logger.info("搜题结束");
}
_getCurrentIndex() {
const onHead = document.querySelector(SELECTORS.TOPIC_HEAD + ".on");
return onHead ? parseInt(onHead.textContent) - 1 : 0;
}
async _handleQuestion(tab, type, index) {
if (type === 2) {
tab.querySelectorAll("label input").forEach(input => input.checked = false);
}
try {
const questionEl = tab.querySelector(SELECTORS.EXAM_MAIN);
const question = questionEl.innerText.replace(/\n\.\n/g, ".");
const answer = await this._getAnswer(question);
if (answer) {
const cleanAnswer = (answer.match(/[A-Za-z]/g) || []).join("").toUpperCase();
this.ui.updateStatus(`第 ${index + 1} 题答案: ${cleanAnswer}`);
Logger.info(`第 ${index + 1} 题答案: ${cleanAnswer}`);
const answerList = cleanAnswer.split("");
tab.querySelectorAll("label").forEach(label => {
if (answerList.some(item => label.innerText.includes(item))) {
const input = label.querySelector("input");
if (input) input.checked = true;
}
});
}
} catch (e) {
Logger.error("搜题出错: " + e.message);
this.ui.updateStatus("搜题出错,跳过...");
}
}
_getAnswer(question) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "POST",
url: API.AI_CHAT,
headers: {
"Authorization": "Bearer " + ConfigManager.apiKey,
"Content-Type": "application/json"
},
data: JSON.stringify({
"model": ConfigManager.endpointId,
"messages": [
{
"role": "user",
"content": "只说选项号,不要说理由。\n" + question
}
]
}),
onload: (res) => {
if (res.status === 200) {
try {
const json = JSON.parse(res.responseText);
resolve(json.choices[0].message.content);
} catch (e) {
reject("解析失败");
}
} else {
reject("API错误: " + res.status);
}
},
onerror: () => reject("网络错误")
});
});
}
_nextQuestion(tab, index) {
const saveBtn = tab.querySelector("input[value='保存修改']");
if (saveBtn && saveBtn.offsetParent !== null) {
saveBtn.click();
const nextLink = document.querySelectorAll(SELECTORS.EXAM_INTRO_LIST)[index + 1]?.querySelector("a");
if (nextLink) nextLink.click();
} else {
const nextBtn = tab.querySelector("input[value='继续下一题']");
if (nextBtn) nextBtn.click();
}
}
}
class App {
constructor() {
this.ui = new UIManager();
this.videoPlayer = new VideoPlayer(this.ui);
this.captchaHandler = new CaptchaHandler(this.ui);
this.examSolver = new ExamSolver(this.ui);
this.checkTimer = null;
this.isVideoMode = false;
}
init() {
if (!this._checkFirstUse()) return;
this._checkCourseChange();
this.ui.init();
this._initRouteListener();
Logger.info(`安装过老版本的需要把老版本删除或者禁用。`);
Logger.info(`如果一直放同一个视频,就把页面停留最大时间设置成0`);
Logger.info(`刷课助手 v${CONFIG.VERSION} 启动`);
this._setupPageTimeout();
this._initVisibilityListener();
this.captchaHandler.startObserver();
this._checkUrl();
}
_checkCourseChange() {
const href = window.location.href;
const match = href.match(/courseId=([^&]+)/) || href.match(/id=([^&]+)/) || href.match(/course\/(\d+)/);
if (match) {
const currentCourseId = match[1];
const lastCourseId = GM_getValue("sk_last_course_id");
if (lastCourseId && lastCourseId !== currentCourseId) {
Logger.info("检测到切换课程,重置刷课状态与黑名单记录");
ConfigManager.videoStage = 1;
GM_setValue("sk_blocked_chapters", []);
}
GM_setValue("sk_last_course_id", currentCourseId);
}
}
_initVisibilityListener() {
document.addEventListener("visibilitychange", () => {
if (!this.isVideoMode) return;
if (document.hidden) {
Logger.warn("检测到页面被隐藏 (最小化或切换标签)");
Logger.warn("页面隐藏可能无法增加学时,若学时会增加可忽略");
} else {
Logger.info("页面恢复可见");
}
});
window.addEventListener("blur", () => {
if (!this.isVideoMode) return;
Logger.warn("检测到窗口失去焦点 (切屏)");
});
window.addEventListener("focus", () => {
if (!this.isVideoMode) return;
Logger.info("窗口重新获得焦点");
});
}
_checkFirstUse() {
const isFirstUse = GM_getValue("firstUse", true);
if (isFirstUse) {
const mzsm = prompt("英华学堂全系列刷课助手|自动刷课|考试自动答题(原为成都文理学院刷课助手)\n若要使用,请阅读并同意以下免责条款。\n\n" +
"1. 此脚本仅用于学习研究,您必须在下载后24小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。\n" +
"2. 请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。\n" +
"3. 本人对此脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害(如封禁、限制等)。\n" +
"4. 若有信息功能侵犯到您的权益,请及时联系作者。\n" +
"5. 任何以任何方式查看此脚本的人或直接或间接使用此脚本的使用者都应仔细阅读此条款。\n" +
"6. 本人保留随时更改或补充此条款的权利,一旦您使用或复制或修改了此脚本,即视为您已接受此免责条款。\n\n" +
"若您同意以上内容,请输入“我已阅读并同意以上内容” 然后开始使用。", "");
if (mzsm === "我已阅读并同意以上内容") {
GM_setValue("firstUse", false);
return true;
} else {
alert("免责条款未同意,脚本停止运行。\n若不想使用,请自行禁用脚本,以免每个页面都弹出该提示。");
return false;
}
}
return true;
}
async _handleErrorPage() {
const errorMsg = document.querySelector("#error-main .name")?.innerText || document.querySelector(".name")?.innerText || "";
if (/未到|未开放|尚未开放|锁定|未开启/.test(errorMsg)) {
const currentChapter = GM_getValue("sk_current_brushing_chapter");
if (currentChapter) {
Logger.warn(`检测到错误页提示: ${errorMsg},已将 [${currentChapter}] 加入临时黑名单`);
const blocked = GM_getValue("sk_blocked_chapters", []);
if (!blocked.includes(currentChapter)) {
blocked.push(currentChapter);
GM_setValue("sk_blocked_chapters", blocked);
}
}
this.ui.updateStatus("检测到章节锁定,正在返回...");
await Utils.pause(2);
this._navigateToStudyRecord();
} else {
Logger.warn(`进入未知错误页: ${errorMsg},5秒后尝试返回记录页检查进度...`);
this.ui.updateStatus("遇到错误,尝试返回...");
await Utils.pause(5);
this._navigateToStudyRecord();
}
}
_navigateToStudyRecord() {
const href = window.location.href;
let courseId = "";
const match = href.match(/courseId=([^&]+)/) || href.match(/id=([^&]+)/) || href.match(/course\/(\d+)/);
if (match) {
courseId = match[1];
}
if (!courseId) {
const input = document.querySelector('input[name="courseId"]');
if (input) courseId = input.value;
}
if (!courseId) {
const backLink = document.querySelector("#back")?.href || "";
const backMatch = backLink.match(/courseId=([^&]+)/) || backLink.match(/id=([^&]+)/);
if (backMatch) courseId = backMatch[1];
}
if (!courseId && typeof data !== 'undefined' && data.back) {
const dataMatch = data.back.match(/courseId=([^&]+)/) || data.back.match(/id=([^&]+)/);
if (dataMatch) courseId = dataMatch[1];
}
if (!courseId) {
courseId = GM_getValue("sk_last_course_id");
if (courseId) Logger.info("URL 丢失课程号,使用缓存记录的 ID: " + courseId);
}
if (courseId) {
ConfigManager.videoStage = 2;
GM_setValue("sk_video_stage", 2);
const targetPath = href.includes("/student/") ? "/student/course-study-record" : "/user/study_record";
location.href = `${targetPath}?id=${courseId}&courseId=${courseId}`;
} else {
Logger.error("无法获取课程号,尝试返回首页");
location.href = href.includes("/student/") ? "/student/home" : "/user/index";
}
}
_checkUrl() {
const href = window.location.href;
const pathname = window.location.pathname;
const savedStage = GM_getValue("sk_video_stage");
if (savedStage) {
ConfigManager.videoStage = savedStage;
}
if (document.getElementById("error-main") || document.querySelector(".errormain")) {
this._handleErrorPage();
return;
}
if (href.includes("/node") || pathname === "/student/course-study") {
this._startVideoMode();
} else if (pathname === "/user/exam") {
this._startExamMode();
} else if (href.includes("/login")) {
this._autoFillLoginFields();
this.captchaHandler.checkAndSolveLogin();
document.addEventListener("sk:recaptcha_login", () => {
this.captchaHandler.solveLoginCaptcha();
});
} else if (href.includes("/user/study_record") || href.includes("course-study-record")) {
this._checkStudyRecord();
} else if (pathname === "/student/home") {
this.ui.updateStatus("已进入个人首页");
this.ui.clearLog();
Logger.info("成功检测到路由跳转:已进入个人首页");
} else {
this.ui.updateStatus("请进入课程或考试页面");
}
}
_autoFillLoginFields() {
if (!ConfigManager.autoFillAccount) return;
if (this._loginFillTimer) return;
const href = window.location.href;
const isHaiqi = href.includes("haiqikeji");
const fill = (selector, value) => {
const el = document.querySelector(selector);
if (!el || !value) return false;
el.focus();
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set;
if (setter) {
setter.call(el, value);
} else {
el.value = value;
}
el.dispatchEvent(new InputEvent("input", {
bubbles: true,
composed: true,
data: value,
inputType: "insertText"
}));
el.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
el.dispatchEvent(new Event("blur", { bubbles: true, composed: true }));
return true;
};
const tryFill = () => {
if (!ConfigManager.autoFillAccount) return true;
if (isHaiqi) {
const inputs = document.querySelectorAll('input.el-input__inner');
const userInput = Array.from(inputs).find(el => el.type === 'text' || (el.placeholder || '').includes('学号'));
const passInput = Array.from(inputs).find(el => el.type === 'password' || (el.placeholder || '').includes('密码'));
const ok1 = userInput && ConfigManager.loginAccount ? (fill('input.el-input__inner[type="text"]', ConfigManager.loginAccount) || fill('input[placeholder="请输入学号"]', ConfigManager.loginAccount)) : false;
const ok2 = passInput && ConfigManager.loginPassword ? (fill('input.el-input__inner[type="password"]', ConfigManager.loginPassword) || fill('input[placeholder="请输入密码"]', ConfigManager.loginPassword)) : false;
return !!(ok1 && ok2);
}
const ok1 = !!fill('#username', ConfigManager.loginAccount);
const ok2 = !!fill('#password', ConfigManager.loginPassword);
return ok1 && ok2;
};
let attempts = 0;
const maxAttempts = 40;
const interval = setInterval(() => {
attempts++;
if (tryFill() || attempts >= maxAttempts) {
clearInterval(interval);
this._loginFillTimer = null;
}
}, 250);
this._loginFillTimer = interval;
tryFill();
}
_initRouteListener() {
const self = this;
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function () {
originalPushState.apply(this, arguments);
self._handleRouteChange();
};
history.replaceState = function () {
originalReplaceState.apply(this, arguments);
self._handleRouteChange();
};
window.addEventListener("popstate", () => this._handleRouteChange());
}
_handleRouteChange() {
this.ui.updateToolbar();
this._checkUrl();
}
_getPageInfo() {
const totalEl = document.querySelector(".pagebar-bbs .total") || document.querySelector(".total");
if (!totalEl) return null;
const text = totalEl.innerText;
const match = text.match(/页次:\s*(\d+)\s*\/\s*(\d+)/) || text.match(/(\d+)\s*\/\s*(\d+)/);
if (match) {
return { current: parseInt(match[1]), total: parseInt(match[2]) };
}
return null;
}
async _checkStudyRecord() {
if (ConfigManager.videoStage !== 2) return;
GM_deleteValue("sk_current_brushing_chapter");
this.ui.updateStatus("正在检查学习进度...");
Logger.info("核对学习进度");
const videoTab = Array.from(document.querySelectorAll(".tab")).find(t => t.innerText.includes("视频记录"));
if (videoTab && !videoTab.classList.contains("active")) {
Logger.info("正在切换至视频记录标签...");
videoTab.click();
await Utils.pause(2);
}
await Utils.pause(1);
let progressEl = null;
for (let i = 0; i < 5; i++) {
progressEl = document.querySelector(".score-table td") || document.querySelector(".progress-text");
if (progressEl) break;
await Utils.pause(1);
}
if (progressEl && progressEl.innerText.includes("100%")) {
Logger.info("学习进度已达 100%,刷课完成!");
this.ui.notify("完成", "学习进度 100%,所有课程已完成!");
this.ui.updateStatus("刷课完成");
ConfigManager.videoStage = 1;
GM_setValue("sk_video_stage", 1);
this.isVideoMode = false;
return;
}
await Utils.pause(1);
let rows = [];
for (let i = 0; i < 5; i++) {
rows = document.querySelectorAll("table#list tbody tr, .video-table tbody tr");
if (rows.length > 0) break;
Logger.info(`正在等待记录列表加载... (${i + 1}/5)`);
await Utils.pause(1);
}
let foundUnfinished = false;
for (const row of rows) {
const statusEl = row.querySelector("td:last-child span, .col-status span, td:nth-last-child(2) span");
const status = statusEl?.innerText;
const blockedChapters = GM_getValue("sk_blocked_chapters", []);
if (status && !status.includes("已学") && !status.includes("已完成") && !/未开放|尚未开放|锁定|未开启/.test(status)) {
const link = row.querySelector("td:first-child a");
const chapterTitle = (row.querySelector("td:first-child") || row.querySelector(".video-link"))?.innerText?.trim();
if (blockedChapters.includes(chapterTitle)) {
Logger.warn(`章节 ${chapterTitle} 在黑名单中(未开放),跳过...`);
continue;
}
if (link) {
Logger.info(`发现未学课程: ${chapterTitle},准备跳转...`);
this.ui.updateStatus(`补刷: ${chapterTitle}`);
foundUnfinished = true;
GM_setValue("sk_current_brushing_chapter", chapterTitle);
GM_setValue("sk_video_stage", 1);
await Utils.pause(1);
link.click();
return;
} else if (chapterTitle) {
Logger.info(`发现未学课程: ${chapterTitle},准备回跳学习页...`);
this.ui.updateStatus(`准备补刷: ${chapterTitle}`);
GM_setValue("sk_target_chapter", chapterTitle);
GM_setValue("sk_current_brushing_chapter", chapterTitle);
GM_setValue("sk_video_stage", 1);
foundUnfinished = true;
await Utils.pause(2);
this._navigateToStudyPage();
return;
}
}
}
if (!foundUnfinished) {
const nextBtn = document.querySelector(".page-btn.next, .btn-next");
const pageInfo = this._getPageInfo();
const hasNextPage = pageInfo ? (pageInfo.current < pageInfo.total) : (nextBtn && !nextBtn.classList.contains("disabled") && !nextBtn.hasAttribute("disabled"));
if (hasNextPage && nextBtn) {
Logger.info("当前页已完成,进入下一页...");
this.ui.updateStatus("翻页检查中...");
await Utils.pause(1);
nextBtn.click();
setTimeout(() => this._checkStudyRecord(), 3000);
} else {
Logger.info("所有页面检查完毕,未发现可刷课程或章节已锁定。");
this.ui.notify("完成", "学习进度核对结束");
this.ui.updateStatus("检查结束");
ConfigManager.videoStage = 1;
GM_setValue("sk_video_stage", 1);
this.isVideoMode = false;
}
}
}
_navigateToStudyPage() {
const href = window.location.href;
let courseId = "";
const match = href.match(/id=([^&]+)/) || href.match(/courseId=([^&]+)/);
if (match) courseId = match[1];
if (courseId) {
const targetPath = href.includes("/student/") ? "/student/course-study" : "/node/index";
location.href = `${targetPath}?id=${courseId}&courseId=${courseId}`;
} else {
Logger.error("无法返回学习页,ID 丢失");
location.href = "/student/home";
}
}
async _startVideoMode() {
this.isVideoMode = true;
this.ui.updateStatus("进入视频刷课模式");
Logger.info("进入视频刷课模式");
GM_registerMenuCommand("开始刷课", () => {
ConfigManager.videoStage = 1;
this._videoLoop();
});
GM_registerMenuCommand("停止刷课", () => this._stopVideoLoop());
document.addEventListener("sk:video_ended", () => {
if (ConfigManager.videoStage === 2) {
const links = document.querySelectorAll(SELECTORS.COURSE_LINKS);
const currentIndex = this._getCurrentVideoIndex(links);
const targetChapter = GM_getValue("sk_current_brushing_chapter");
if (currentIndex !== -1 && currentIndex < links.length - 1) {
const nextItem = links[currentIndex + 1];
const nextTitle = nextItem.querySelector(".section-title")?.innerText?.trim() || nextItem.innerText?.trim();
if (nextTitle) {
Logger.info(`侧边栏发现后续视频: ${nextTitle},继续播放...`);
this._playNextVideo();
return;
}
}
this._playNextVideo();
} else {
this._playNextVideo();
}
});
await Utils.pause(3);
this._videoLoop();
}
async _returnToStudyRecord() {
this.ui.updateStatus("本节补刷完成,返回记录页...");
Logger.info("本节补刷完成,返回记录页继续检查");
await Utils.pause(2);
this._navigateToStudyRecord();
}
_startExamMode() {
this.ui.updateStatus("进入考试搜题模式");
Logger.warn("答案由 AI 生成,不能保证 100% 正确。分数高低与作者无关,建议搜完后人工复核。");
Logger.info("进入考试搜题模式,点击【开始搜题】");
GM_registerMenuCommand("开始搜题", () => this.examSolver.start());
GM_registerMenuCommand("停止搜题", () => this.examSolver.stop());
document.addEventListener("sk:exam_start", () => this.examSolver.start());
document.addEventListener("sk:exam_stop", () => this.examSolver.stop());
}
async _videoLoop() {
if (this.checkTimer) clearInterval(this.checkTimer);
const targetChapter = GM_getValue("sk_target_chapter");
if (targetChapter) {
this.ui.updateStatus(`自动定位章节: ${targetChapter}`);
Logger.info(`正在尝试自动跳转至补刷章节: ${targetChapter}`);
const links = Array.from(document.querySelectorAll(SELECTORS.COURSE_LINKS));
const activeIndex = links.findIndex(link => link.classList?.contains("active") || link.querySelector?.(".active"));
if (activeIndex !== -1) {
const activeItem = links[activeIndex];
const activeTitle = activeItem.querySelector(".section-title")?.innerText?.trim() || activeItem.innerText?.trim();
if (activeTitle) {
GM_deleteValue("sk_target_chapter");
GM_setValue("sk_current_brushing_chapter", targetChapter);
Logger.info(`使用 active 定位当前章节: ${activeTitle}`);
}
}
for (const link of links) {
const title = link.querySelector(".section-title")?.innerText?.trim() || link.innerText?.trim();
if (title && (targetChapter.includes(title) || title.includes(targetChapter))) {
GM_deleteValue("sk_target_chapter");
GM_setValue("sk_current_brushing_chapter", targetChapter);
Logger.info(`成功定位,开始补刷: ${title}`);
link.click();
break;
}
}
}
this.checkTimer = setInterval(async () => {
if (this.captchaHandler.isRecognizing) return;
const layer = document.querySelector(SELECTORS.CAPTCHA_LAYER);
const isRealCaptcha = layer && layer.querySelector("img") && layer.querySelector("input");
if (isRealCaptcha && layer.offsetParent !== null) {
await this.captchaHandler.checkAndSolve();
return;
}
if (layer && !isRealCaptcha && layer.offsetParent !== null) {
const content = layer.innerText || "";
if (/未开放|尚未开放|锁定|请先学习|未开启/.test(content)) {
const currentChapter = targetChapter || GM_getValue("sk_current_brushing_chapter");
if (currentChapter) {
Logger.warn(`检测到章节锁定弹窗: ${content},已将 [${currentChapter}] 加入临时黑名单`);
const blocked = GM_getValue("sk_blocked_chapters", []);
if (!blocked.includes(currentChapter)) {
blocked.push(currentChapter);
GM_setValue("sk_blocked_chapters", blocked);
}
}
const confirmBtn = layer.parentNode.querySelector(".layui-layer-btn0");
if (confirmBtn) confirmBtn.click();
await Utils.pause(1);
this._returnToStudyRecord();
return;
}
const confirmBtn = layer.parentNode.querySelector(".layui-layer-btn0");
if (confirmBtn) {
Logger.info("拦截到系统提示弹窗,尝试自动关闭/确认...");
confirmBtn.click();
await Utils.pause(1);
}
return;
}
const video = document.querySelector(SELECTORS.VIDEO);
if (video) {
if (video.paused) {
await this.videoPlayer.start();
} else {
if (this.ui.panel && this.ui.panel.textContent.includes("验证码")) {
this.ui.updateStatus("视频播放中...");
}
}
} else {
const isExamPage = !document.querySelector(SELECTORS.VIDEO) && !document.querySelector(".detmain-tabs .ico1") && (
Array.from(document.querySelectorAll("button span, .el-button, .detmain-tabs .item span")).some(el =>
/考试|作业|练习|测验|资料/.test(el.innerText)
) || !!document.querySelector(".ico3, .ico4, .external-link-container")
);
if (isExamPage && !this._examSkipping) {
this._examSkipping = true;
Logger.warn("检测到当前章节为考试/作业/非视频内容,准备跳过...");
this.ui.updateStatus("自动跳过非视频章节...");
await Utils.pause(3);
this._playNextVideo();
setTimeout(() => { this._examSkipping = false; }, 5000);
}
}
}, CONFIG.THRESHOLDS.CAPTCHA_CHECK_INTERVAL);
}
_stopVideoLoop() {
if (this.checkTimer) {
clearInterval(this.checkTimer);
this.checkTimer = null;
}
this.ui.updateStatus("刷课已暂停");
}
_getCurrentVideoIndex(links) {
const items = Array.from(links || []);
const href = window.location.href;
const isHaiqi = href.includes("haiqikeji");
if (isHaiqi) {
const activeIndex = items.findIndex(link => link.classList?.contains("active") || link.querySelector?.(".active"));
if (activeIndex !== -1) return activeIndex;
const activeChapter = document.querySelector(".section-item.active");
if (activeChapter) {
const idx = items.findIndex(link => link === activeChapter || link.contains?.(activeChapter) || activeChapter.contains?.(link));
if (idx !== -1) return idx;
}
const currentTitle = document.querySelector("h1")?.innerText?.trim();
if (currentTitle) {
for (let i = 0; i < items.length; i++) {
const link = items[i];
const sectionTitle = link.querySelector(".section-title")?.innerText?.trim() || link.title?.trim() || link.innerText?.trim();
if (!sectionTitle) continue;
if (currentTitle === sectionTitle) return i;
if (currentTitle.includes(sectionTitle) || sectionTitle.includes(currentTitle)) return i;
}
}
return -1;
}
const currentUrl = new URL(window.location.href);
for (let i = 0; i < items.length; i++) {
const link = items[i];
const activeLink = link.querySelector("a.on") || (link.matches && link.matches("a.on"));
if (activeLink) return i;
try {
const linkUrl = new URL(link.href, window.location.origin);
const linkNodeId = linkUrl.searchParams.get("nodeId");
const currentNodeId = currentUrl.searchParams.get("nodeId");
if (linkNodeId && currentNodeId && linkNodeId === currentNodeId) return i;
} catch (e) { }
}
const currentTitle = document.querySelector("h1")?.innerText?.trim();
if (currentTitle) {
for (let i = 0; i < items.length; i++) {
const link = items[i];
const sectionTitle = link.querySelector(".section-title")?.innerText?.trim() || link.title?.trim() || link.innerText?.trim();
if (!sectionTitle) continue;
if (currentTitle === sectionTitle) return i;
if (currentTitle.includes(sectionTitle) || sectionTitle.includes(currentTitle)) return i;
}
}
return -1;
}
async _playNextVideo() {
this._stopVideoLoop();
const sidebar = document.querySelector(".detmain-navlist, .course-sidebar, .section-list, .detmain-navs");
const links = sidebar ? sidebar.querySelectorAll(SELECTORS.COURSE_LINKS) : document.querySelectorAll(SELECTORS.COURSE_LINKS);
let currentIndex = this._getCurrentVideoIndex(links);
if (currentIndex !== -1 && currentIndex < links.length - 1) {
this.ui.updateStatus("准备播放下一个视频...");
await Utils.pause(3);
const nextItem = links[currentIndex + 1];
if (nextItem.classList.contains('section-item') || nextItem.parentElement.classList.contains('item')) {
const header = nextItem.querySelector('.section-header') || nextItem;
header.click();
} else {
nextItem.click();
}
setTimeout(() => this._videoLoop(), 3000);
} else {
const globalNext = Array.from(document.querySelectorAll("a, button, span")).find(el => {
const text = el.innerText;
const isNextText = /下一章|下一节|下一页|Next Chapter|Next Section/i.test(text);
const isBBS = el.closest(".pagebar-bbs, .discuss-list, .bbs-list, .pagebar");
const isRecord = text.includes("记录") || text.includes("首页") || text.includes("尾页");
return isNextText && el.offsetParent !== null && !isBBS && !isRecord;
});
if (globalNext) {
Logger.info(`侧边栏已到头,发现跳转按钮: ${globalNext.innerText},尝试进入下一章...`);
this.ui.updateStatus("跳转下一章...");
globalNext.click();
setTimeout(() => this._videoLoop(), 5000);
return;
}
this.ui.notify("完成", "当前页面所有视频已播放完毕");
this.ui.updateStatus("准备核对总进度...");
Logger.info("当前播放列表已结束,开始回跳记录页核对进度");
ConfigManager.videoStage = 2;
await Utils.pause(2);
this._navigateToStudyRecord();
}
}
_setupPageTimeout() {
const minutes = ConfigManager.timeoutMinutes;
if (minutes > 0) {
setTimeout(() => {
if (this.isVideoMode) {
this.ui.notify("超时", "页面停留超时,即将刷新");
setTimeout(() => location.reload(), 5000);
}
}, minutes * 60 * 1000);
}
}
}
function matchUrl() {
let iconLink = document.querySelector("link[rel='shortcut icon']");
if (iconLink && /yuruixxkj|yinghua|canghui|gyxy|ruren|zjxkeji|yuncanjykeji|haiqikeji/.test(iconLink.getAttribute("href"))) {
return true;
}
const urls = ConfigManager.urls;
const currentUrl = window.location.hostname;
for (let i = 0; i < urls.length; i++) {
if (currentUrl.includes(urls[i].trim())) {
return true;
}
}
return false;
}
function quick502Check() {
const errorText = document.body.innerText || document.documentElement.innerText || "";
const title = document.title || "";
if (/Internal Server Error|Service Unavailable|Bad Gateway/i.test(errorText + title)) {
setTimeout(() => {
location.reload();
}, 5000);
return true;
}
return false;
}
(function () {
'use strict';
if (!matchUrl()) return;
if (quick502Check()) return;
const app = new App();
window.addEventListener("load", () => app.init());
})();
// (function(s) {
// const v = document.querySelector('video');
// if(!v) return console.log('未发现视频');
// const hijack = (prop, validator) => {
// const desc = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, prop);
// Object.defineProperty(v, prop, {
// set: function(val) {
// if (validator(val, this[prop])) return; // 拦截非法设置
// desc.set.call(this, val);
// },
// get: function() { return desc.get.call(this); },
// configurable: true
// });
// };
// hijack('playbackRate', (val) => val !== s);
// hijack('currentTime', (val, cur) => val < cur - 1);
// ['ratechange', 'seeking', 'seeked', 'timeupdate'].forEach(name => {
// v.addEventListener(name, e => e.stopImmediatePropagation(), true);
// });
// v.playbackRate = s;
// v.play();
// })(16);