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