更新脚本版本号至2026-06-05.7,优化提示消息文本和逻辑,提升用户体验

This commit is contained in:
竟康 2026-06-05 16:37:01 +08:00
parent 8ae3f92996
commit d5100655ee
1 changed files with 34 additions and 34 deletions

View File

@ -1,7 +1,7 @@
// ==UserScript==
// @name 平顶山学院(青书学堂)自动学习脚本
// @namespace http://tampermonkey.net/
// @version 2026-06-05.5
// @version 2026-06-05.7
// @description 自动进入青书学堂未完成课件,支持视频播放、文本课件停留和运行状态提示
// @author JK
// @license MIT
@ -40,10 +40,10 @@
// 视频默认播放倍速;想改成 1.5 倍、3 倍可以从 Tampermonkey 菜单配置。
videoPlaybackRate: 2,
// toast 默认显示时长单位毫秒4000 表示 4 秒后自动隐藏。
// 提示消息默认显示时长单位毫秒4000 表示 4 秒后自动隐藏。
toastDefaultDuration: 4000,
// 运行心跳提示间隔,单位秒;视频播放中、文本课件等待中都会按这个间隔提醒。
// 运行提醒间隔,单位秒;视频播放中、文本课件等待中都会按这个间隔提醒。
heartbeatSeconds: 10,
// 课程列表最多等待多少秒;超过后认为可能请求失败,触发刷新。
@ -60,7 +60,7 @@
...GM_getValue(CONFIG_STORAGE_KEY, {}),
};
// toast 容器和样式标签的 id通常不用改除非和页面元素冲突。
// 提示消息容器和样式标签的 id通常不用改除非和页面元素冲突。
const TOAST_CONTAINER_ID = "qingshu-auto-toast-container";
const TOAST_STYLE_ID = "qingshu-auto-toast-style";
@ -71,8 +71,8 @@
location.pathname +
location.search;
// 创建右下角 toast 容器和样式。
// 每次通知都会创建一个新的 toast,所以多个通知会一起堆叠显示。
// 创建右下角提示消息容器和样式。
// 每次通知都会创建一个新的提示消息,所以多个通知会一起堆叠显示。
const ensureToastContainer = () => {
let container = document.querySelector("#" + TOAST_CONTAINER_ID);
@ -141,7 +141,7 @@
return container;
};
// 显示一条 toast,同时保留 console 输出。
// 显示一条提示消息,同时保留 console 输出。
// type 可用 info、success、warning、errorduration 是自动隐藏时间。
const notify = (
message,
@ -175,7 +175,7 @@
const saveConfig = (patch) => {
Object.assign(CONFIG, patch);
GM_setValue(CONFIG_STORAGE_KEY, CONFIG);
notify("配置已保存,刷新页面后完全生效", "success", 6000);
notify("设置已保存,刷新页面后生效", "success", 6000);
};
const promptNumberConfig = (label, key, unit, currentValue, toStoredValue) => {
@ -188,7 +188,7 @@
const value = Number(input);
if (!Number.isFinite(value) || value <= 0) {
notify("置失败请输入大于0的数字", "error", 6000);
notify("置失败请输入大于0的数字", "error", 6000);
return;
}
@ -209,9 +209,9 @@
);
});
GM_registerMenuCommand("设置最小学习时间", () => {
GM_registerMenuCommand("设置课件最小学习时间", () => {
promptNumberConfig(
"设置最小学习时间",
"设置课件最小学习时间",
"minStudySeconds",
"分钟",
CONFIG.minStudySeconds / 60,
@ -219,9 +219,9 @@
);
});
GM_registerMenuCommand("设置心跳提示间隔", () => {
GM_registerMenuCommand("设置运行提醒间隔", () => {
promptNumberConfig(
"设置心跳提示间隔",
"设置运行提醒间隔",
"heartbeatSeconds",
"秒",
CONFIG.heartbeatSeconds
@ -237,9 +237,9 @@
);
});
GM_registerMenuCommand("设置课程列表最大刷新次数", () => {
GM_registerMenuCommand("设置课程列表自动刷新次数", () => {
promptNumberConfig(
"设置课程列表最大刷新次数",
"设置课程列表自动刷新次数",
"maxCourseListRefreshCount",
"次",
CONFIG.maxCourseListRefreshCount,
@ -247,9 +247,9 @@
);
});
GM_registerMenuCommand("设置 toast 显示时长", () => {
GM_registerMenuCommand("设置提示消息显示时间", () => {
promptNumberConfig(
"设置 toast 显示时长",
"设置提示消息显示时间",
"toastDefaultDuration",
"秒",
CONFIG.toastDefaultDuration / 1000,
@ -257,15 +257,15 @@
);
});
GM_registerMenuCommand("重置脚本配置", () => {
GM_registerMenuCommand("恢复默认设置", () => {
GM_deleteValue(CONFIG_STORAGE_KEY);
Object.assign(CONFIG, DEFAULT_CONFIG);
notify("配置已重置,刷新页面后完全生效", "success", 6000);
notify("设置已重置,刷新页面后生效", "success", 6000);
});
};
registerConfigMenus();
notify("插件加载完成,等待界面渲染", "info");
notify("脚本已启动,等待页面内容加载", "info");
// 等待某个 DOM 条件成立。
// 页面里的课程列表、视频、文本课件经常是后加载的,所以不用固定延迟,
@ -398,7 +398,7 @@
document.querySelector(TEXT_SELECTOR)
);
// 把秒数格式化成“x分x秒”用于心跳提示
// 把秒数格式化成“x分x秒”用于运行提醒
const formatSeconds = (seconds) => {
if (!Number.isFinite(seconds) || seconds < 0) {
return "未知";
@ -411,10 +411,10 @@
};
const onPageLoad = async () => {
notify("页面加载完成,开始执行脚本", "info");
notify("页面加载完成,脚本开始运行", "info");
if (url.includes("CourseStudy")) {
notify("进入准备阶段,等待课程列表", "info");
notify("进入课程列表页,等待课程列表加载", "info");
// 课程列表是异步渲染的,等到至少有一个课程节点后再开始遍历。
const items = await waitForCondition(
@ -434,7 +434,7 @@
clearCourseStudyRefreshCount();
notify("获取到课程数量:" + items.length, "success");
notify("已加载课程数量:" + items.length, "success");
// 从上到下找到第一个“没学过”或“学习不足配置时长”的课件并进入。
for (const item of items) {
@ -442,7 +442,7 @@
const mark = item.querySelector(".study_being");
const studiedSeconds = mark ? parseStudySeconds(mark.innerText) : 0;
notify(
"点击未完成学习时长的视频" +
"进入未完成学习时长的课件" +
item.querySelector(".title").innerText +
",已学习:" +
studiedSeconds +
@ -454,9 +454,9 @@
return;
}
}
notify("所有视频均已播放完毕,结束脚本", "success", 6000);
notify("所有课件均已达到学习时长,脚本停止", "success", 6000);
} else if (url.includes("CourseShow")) {
notify("进入学习阶段,等待课件加载", "info");
notify("进入课件页面,等待课件加载", "info");
// 等到播放页真正渲染出视频或文本课件,再决定如何处理。
const coursewareElement = await waitForCoursewareElement();
@ -464,9 +464,9 @@
// 文本课件没有 video 标签,不需要播放;停留到配置的学习时长后返回列表页。
if (coursewareElement.matches(TEXT_SELECTOR)) {
notify(
"检测到文本课件,等待" +
"检测到文本课件,将停留" +
formatSeconds(CONFIG.minStudySeconds) +
"后返回准备页面",
"后返回课程列表",
"warning",
6000
);
@ -478,13 +478,13 @@
if (studiedSeconds >= CONFIG.minStudySeconds) {
clearInterval(textInterval);
notify("文本课件等待完成,返回准备页面", "success");
notify("文本课件停留完成,返回课程列表", "success");
returnToStudyPage();
return;
}
notify(
"文本课件学习中,已等待" +
"文本课件学习中,已停留" +
formatSeconds(studiedSeconds) +
",剩余:" +
formatSeconds(CONFIG.minStudySeconds - studiedSeconds),
@ -509,7 +509,7 @@
// 有些播放器会重建 video 元素,短暂找不到时继续等下一轮。
if (!video) {
if (!videoMissingNotified) {
notify("视频元素暂时不可用,继续等待", "warning");
notify("正在等待视频加载", "warning");
videoMissingNotified = true;
}
return;
@ -518,12 +518,12 @@
if (video.paused && video.currentTime === 0) {
video.play();
notify("视频未播放,开始播放视频", "success");
notify("视频已开始播放", "success");
return;
}
if (video.currentTime >= video.duration - 1) {
notify("视频播放结束,返回准备页面", "success");
notify("视频播放结束,返回课程列表", "success");
clearInterval(videoInterval);
returnToStudyPage();
}