// ==UserScript==
// @name 2026年暑期教师研修|国家智慧教育公共服务平台|国家职教与高教智慧教育平台|自动刷视频自动播放(职教/高教)
// @namespace local.codex.smartedu
// @version 2026.07.28.2
// @description 适配2026年暑期教师研修职教/高教专题。
// @author local
// @match https://www.smartedu.cn/jiaoshi/training/595a8948-d059-4972-bc30-bf10685f0061*
// @match https://www.smartedu.cn/jiaoshi/courseIndex*
// @match https://www.smartedu.cn/jiaoshi/courseDetail*
// @match https://teacher.ykt.eduyun.cn/teacherTraining/courseIndex*
// @match https://teacher.ykt.eduyun.cn/teacherTraining/courseDetail*
// @run-at document-idle
// @grant none
// @license MIT
// ==/UserScript==
(function () {
"use strict";
const TRAINING_ID = "595a8948-d059-4972-bc30-bf10685f0061";
const TRAINING_URL = `https://www.smartedu.cn/jiaoshi/training/${TRAINING_ID}`;
const COURSE_BASE_URL = "https://www.smartedu.cn/jiaoshi/courseIndex";
const COURSE_CONTEXT = {
tag: "2026年“暑期教师研修”专题(职业教育、高等教育)",
channelId: "",
libraryId: "bb042e69-9a11-49a1-af22-0c3fab2e92b9",
breadcrumb: "2026年“暑期教师研修”专题(职业教育、高等教育)",
};
// 职教、高教使用同一专题课程清单,无需再选择方向。
const COURSES = [
{
id: "2e001e74-34e1-4b0f-bda9-5350799a91fc",
name: "大力弘扬教育家精神",
recognizedHours: 2,
totalSeconds: 160749,
},
{
id: "058fa244-f310-41e8-a73b-2853030db536",
name: "综合育人能力提升",
recognizedHours: 4,
totalSeconds: 82814,
},
{
id: "b9141681-d108-45d9-8891-d99fe7b3289b",
name: "职教“双师”能力提升",
recognizedHours: 4,
totalSeconds: 49131,
},
{
id: "cd7e43d3-aedb-48f9-8b5a-bdc22f4ffb81",
name: "高教教学科研能力提升",
recognizedHours: 4,
totalSeconds: 88850,
},
];
const STORAGE_KEY = "smartedu-2026-training-settings";
const DEFAULT_SETTINGS = {
enabled: false,
muted: true,
playbackRate: 1,
autoNextCourse: true,
lastEndedResourceText: "",
};
let settings = loadSettings();
let lastActionAt = 0;
let lastStatus = "等待页面加载";
let boundVideo = null;
let tickTimer = null;
let waitingForProgressSave = false;
function loadSettings() {
try {
return {
...DEFAULT_SETTINGS,
...JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}"),
};
} catch (error) {
console.warn("[研修助手] 配置读取失败,已使用默认配置。", error);
return { ...DEFAULT_SETTINGS };
}
}
function saveSettings(patch = {}) {
settings = { ...settings, ...patch };
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
renderPanel();
}
function getCoursePlan() {
return COURSES;
}
function getCurrentCourseId() {
return new URL(location.href).searchParams.get("courseId");
}
function getCurrentCourse() {
const id = getCurrentCourseId();
return getCoursePlan().find((course) => course.id === id) || null;
}
function getCourseUrl(course) {
const url = new URL(COURSE_BASE_URL);
url.searchParams.set("courseId", course.id);
Object.entries(COURSE_CONTEXT).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
return url.href;
}
function getRequiredProgress(course) {
if (!course) return 100;
return Math.ceil((course.recognizedHours * 3600 * 100) / course.totalSeconds);
}
function readCourseProgress() {
const progressText = document.querySelector(
'[class*="CourseIndex-module_progress-text_"]'
);
const value = Number.parseFloat(progressText?.textContent || "");
if (Number.isFinite(value)) return value;
const courseButton = Array.from(document.querySelectorAll("a")).find((item) =>
["开始学习", "继续学习", "复习回顾"].includes(item.textContent.trim())
);
return courseButton?.textContent.trim() === "复习回顾" ? 100 : null;
}
function hasReachedRequiredHours() {
const course = getCurrentCourse();
const progress = readCourseProgress();
if (!course || progress === null) return false;
const required = getRequiredProgress(course);
if (progress < required) return false;
setStatus(
`课程进度 ${progress}%,已达到本课程约 ${course.recognizedHours} 学时要求`
);
return true;
}
function isTrainingHome() {
return location.pathname.includes(`/jiaoshi/training/${TRAINING_ID}`);
}
function isCoursePage() {
return /\/(jiaoshi|teacherTraining)\/course(Index|Detail)/.test(location.pathname);
}
function isOnCooldown(ms = 2500) {
return Date.now() - lastActionAt < ms;
}
function markAction() {
lastActionAt = Date.now();
}
function setStatus(text) {
if (lastStatus === text) return;
lastStatus = text;
console.log(`[研修助手] ${text}`);
renderPanel();
}
function ensurePanel() {
if (document.getElementById("smartedu-2026-helper")) return;
const panel = document.createElement("section");
panel.id = "smartedu-2026-helper";
panel.innerHTML = `
2026 研修助手
`;
const style = document.createElement("style");
style.textContent = `
#smartedu-2026-helper {
position: fixed;
right: 18px;
bottom: 18px;
z-index: 2147483647;
width: 260px;
box-sizing: border-box;
padding: 14px;
border: 1px solid rgba(30, 98, 236, .28);
border-radius: 12px;
background: rgba(255, 255, 255, .97);
color: #1f2329;
box-shadow: 0 8px 30px rgba(0, 0, 0, .18);
font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI",
"Microsoft YaHei", sans-serif;
}
#smartedu-2026-helper .s26-title {
margin-bottom: 10px;
color: #1e62ec;
font-size: 16px;
font-weight: 700;
}
#smartedu-2026-helper .s26-row,
#smartedu-2026-helper .s26-check {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin: 7px 0;
}
#smartedu-2026-helper select {
min-width: 112px;
padding: 4px 6px;
border: 1px solid #d9d9d9;
border-radius: 6px;
background: #fff;
}
#smartedu-2026-helper .s26-check {
justify-content: flex-start;
}
#smartedu-2026-helper .s26-status {
min-height: 40px;
margin: 10px 0;
padding: 8px;
border-radius: 7px;
background: #f4f7ff;
color: #4e5969;
word-break: break-word;
}
#smartedu-2026-helper .s26-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
#smartedu-2026-helper button {
padding: 7px 8px;
border: 0;
border-radius: 7px;
background: #1e62ec;
color: #fff;
cursor: pointer;
}
#smartedu-2026-helper button[data-action="next"] {
background: #5b6b83;
}
`;
document.documentElement.append(style, panel);
panel.querySelector('[data-action="rate"]').addEventListener("change", (event) => {
saveSettings({ playbackRate: Number(event.target.value) });
configureVideo(document.querySelector("video"));
});
panel.querySelector('[data-action="muted"]').addEventListener("change", (event) => {
saveSettings({ muted: event.target.checked });
configureVideo(document.querySelector("video"));
});
panel.querySelector('[data-action="auto-next"]').addEventListener("change", (event) => {
saveSettings({ autoNextCourse: event.target.checked });
});
panel.querySelector('[data-action="toggle"]').addEventListener("click", () => {
const enabled = !settings.enabled;
saveSettings({ enabled });
setStatus(enabled ? "已启动,正在寻找未完成视频" : "已暂停");
if (enabled) runTick(true);
});
panel.querySelector('[data-action="next"]').addEventListener("click", () => {
markAction();
if (!openNextUnfinishedVideo({ skipActive: true })) {
moveToNextCourse();
}
});
}
function renderPanel() {
const panel = document.getElementById("smartedu-2026-helper");
if (!panel) return;
panel.querySelector('[data-action="rate"]').value = String(settings.playbackRate);
panel.querySelector('[data-action="muted"]').checked = settings.muted;
panel.querySelector('[data-action="auto-next"]').checked = settings.autoNextCourse;
panel.querySelector('[data-action="toggle"]').textContent = settings.enabled ? "暂停" : "开始";
const course = getCurrentCourse();
const prefix = course ? `${course.name}:` : "";
panel.querySelector('[data-role="status"]').textContent = `${prefix}${lastStatus}`;
}
function expandCatalogs() {
document
.querySelectorAll('.fish-collapse-header[aria-expanded="false"]')
.forEach((header) => header.click());
}
function isVideoResource(item) {
const image = item.querySelector("img");
if (!image) return true;
return /video/i.test(image.src);
}
function isFinishedResource(item) {
return Boolean(
item.querySelector(
'.icon_checkbox_fill[title="已学完"], .iconfont.icon_checkbox_fill'
)
);
}
function getResourceText(item) {
return (item?.textContent || "").replace(/\s+/g, " ").trim();
}
function getVideoResources() {
expandCatalogs();
return Array.from(
document.querySelectorAll(".resource-item.resource-item-train")
).filter(isVideoResource);
}
function openNextUnfinishedVideo({ skipActive = false } = {}) {
const items = getVideoResources();
if (!items.length) return false;
waitingForProgressSave = false;
const activeIndex = items.findIndex((item) =>
item.classList.contains("resource-item-active")
);
const lastEndedIndex = settings.lastEndedResourceText
? items.findIndex(
(item) => getResourceText(item) === settings.lastEndedResourceText
)
: -1;
const referenceIndex = activeIndex >= 0 ? activeIndex : lastEndedIndex;
const isCandidate = (item) =>
!isFinishedResource(item) &&
getResourceText(item) !== settings.lastEndedResourceText &&
(!skipActive || !item.classList.contains("resource-item-active"));
// 页面重新打开时,优先恢复平台标记为“进行中”的资源;没有才找首个未开始资源。
let target = !skipActive
? items.find(
(item) =>
isCandidate(item) &&
item.querySelector(
'.icon_processing_fill[title="进行中"], .iconfont.icon_processing_fill'
)
)
: null;
if (!target && referenceIndex >= 0) {
target =
items.slice(referenceIndex + 1).find(isCandidate) ||
items.slice(0, referenceIndex).find(isCandidate);
} else if (!target) {
target = items.find(isCandidate);
}
if (!target) {
const endedItem = lastEndedIndex >= 0 ? items[lastEndedIndex] : null;
if (endedItem && !isFinishedResource(endedItem)) {
waitingForProgressSave = true;
setStatus("等待平台把刚播放的视频更新为“已学完”");
}
return false;
}
const title = getResourceText(target);
markAction();
target.click();
setStatus(`正在打开:${title || "下一视频"}`);
return true;
}
function closeKnownPlaybackTip() {
const buttons = Array.from(document.querySelectorAll("button"));
const button = buttons.find((item) =>
["知道了", "我知道了"].includes(item.textContent.trim())
);
if (button) button.click();
}
function configureVideo(video) {
if (!video) return false;
video.muted = Boolean(settings.muted);
video.playbackRate = Number(settings.playbackRate) || 1;
if (boundVideo !== video) {
boundVideo = video;
video.addEventListener("ended", onVideoEnded);
video.addEventListener("play", () => setStatus("视频正在播放"));
video.addEventListener("waiting", () => setStatus("视频缓冲中"));
video.addEventListener("error", () => setStatus("播放器报错,请刷新页面后重试"));
}
if (video.paused && !video.ended && settings.enabled) {
video.play().catch(() => {
setStatus("浏览器阻止了自动播放,请手动点一次播放");
});
}
return true;
}
function onVideoEnded() {
setStatus("本视频播放完成,等待平台保存进度");
const activeItem = document.querySelector(
".resource-item.resource-item-train.resource-item-active"
);
const activeText = getResourceText(activeItem);
if (activeText) saveSettings({ lastEndedResourceText: activeText });
window.setTimeout(() => {
if (!settings.enabled) return;
if (openNextUnfinishedVideo({ skipActive: true })) return;
if (waitingForProgressSave) return;
const course = getCurrentCourse();
if (course) location.href = getCourseUrl(course);
}, 5000);
}
function moveToNextCourse() {
if (!settings.autoNextCourse) {
setStatus("当前课程暂无未完成视频");
return false;
}
const plan = getCoursePlan();
const currentId = getCurrentCourseId();
const index = plan.findIndex((course) => course.id === currentId);
if (index >= 0 && index + 1 < plan.length) {
const nextCourse = plan[index + 1];
setStatus(`切换到:${nextCourse.name}`);
markAction();
location.href = getCourseUrl(nextCourse);
return true;
}
saveSettings({ enabled: false });
setStatus("课程列表已处理完,请回专题页核对认定学时");
return false;
}
function startPlan() {
const plan = getCoursePlan();
const current = getCurrentCourse();
const target = current || plan[0];
markAction();
location.href = getCourseUrl(target);
}
function runTick(force = false) {
ensurePanel();
renderPanel();
if (!settings.enabled) return;
if (!force && isOnCooldown()) return;
closeKnownPlaybackTip();
const video = document.querySelector("video");
if (configureVideo(video)) return;
if (isTrainingHome()) {
setStatus("正在检查第一门尚未达到认定学时的课程");
startPlan();
return;
}
if (!isCoursePage()) {
setStatus("请打开2026年暑期教师研修专题或课程页");
return;
}
if (hasReachedRequiredHours()) {
moveToNextCourse();
return;
}
const loginPrompt = Array.from(document.querySelectorAll("button, a")).some(
(item) => item.textContent.trim() === "登录"
);
if (loginPrompt && !document.querySelector(".resource-item.resource-item-train")) {
setStatus("请先登录并完成专题报名");
return;
}
if (openNextUnfinishedVideo()) return;
if (waitingForProgressSave) return;
const courseButton = Array.from(document.querySelectorAll("a")).find((item) =>
["开始学习", "继续学习"].includes(item.textContent.trim())
);
if (courseButton) {
markAction();
courseButton.click();
setStatus("正在进入课程播放器");
return;
}
setStatus("等待课程目录或播放器加载");
}
ensurePanel();
renderPanel();
tickTimer = window.setInterval(runTick, 3000);
runTick(true);
// 平台或浏览器因页面失焦而暂停时,不尝试规避检测;回到前台后自动重新尝试播放。
document.addEventListener("visibilitychange", () => {
if (!document.hidden && settings.enabled) {
setStatus("页面已恢复前台,正在继续播放");
runTick(true);
}
});
window.addEventListener("focus", () => {
if (settings.enabled) runTick(true);
});
window.addEventListener("beforeunload", () => {
if (tickTimer) window.clearInterval(tickTimer);
});
})();