// ==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); }); })();