// ==UserScript==
// @name 国家智慧教育公共服务平台 | 高等教育教师专业发展 | 2025年暑期教师研修 | 自动挂机
// @version 2025.07.31
// @description 自动识别课程并挂机播放视频,完成指定学时自动跳转下一门课程。
// @author FlowPeakFish
// @match https://core.teacher.vocational.smartedu.cn/p/course/*
// @icon https://teacher.vocational.smartedu.cn/favicon.ico
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// @namespace https://greasyfork.org/users/1497801
// ==/UserScript==
// ==============================
// 🔧 课程名称与链接配置
// ==============================
const courseTitles = [
"大力弘扬教育家精神",
"数字素养提升",
"科学素养提升",
"心理健康教育能力提升",
"教学科研能力提升"
];
const courseLinks = [
"https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006809341534883840?itemId=1003784879729774592&type=1&segId=1003784832051576832&projectId=1003784624690925568&orgId=608196190709395456&originP=1",
"https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006809341547466752?itemId=1003784976377577472&type=1&segId=1003784928350146560&projectId=1003784624690925568&orgId=608196190709395456&originP=1",
"https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006809341560049664?itemId=1003785080355917824&type=1&segId=1003785029070618624&projectId=1003784624690925568&orgId=608196190709395456&originP=1",
"https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006809341572632576?itemId=1003785177923817472&type=1&segId=1003785131789123584&projectId=1003784624690925568&orgId=608196190709395456&originP=1",
"https://core.teacher.vocational.smartedu.cn/p/course/vocational/v_1006809341585215488?itemId=1003785287292944384&type=1&segId=1003785243122728960&projectId=1003784624690925568&orgId=608196190709395456&originP=1",
"https://teacher.higher.smartedu.cn/h/subject/summer2025/",
];
// 每门课程所需学时(单位:学时,1 学时 = 45 分钟)
const courseRequiredPeriods = [2, 2, 1, 2, 3];
// ==============================
// 🚀 脚本入口:识别课程并启动定时器
// ==============================
(function () {
'use strict';
const currentURL = window.location.href;
// 只在课程播放页执行
if (currentURL.includes("/p/course/vocational/v")) {
const courseTitle = document.querySelector("h1")?.textContent?.trim();
if (courseTitle) {
const index = courseTitles.findIndex(t => courseTitle.includes(t));
if (index !== -1) {
GM_setValue("下标", index);
createLogBox();
addLog(`✅ 识别课程标题:${courseTitle}(第 ${index+1} 个课程)`);
setInterval(mainLoop, 5000);
} else {
createLogBox();
addLog(`⚠️ 未匹配课程标题:${courseTitle}`);
}
} else {
createLogBox();
addLog(`⚠️ 页面未找到课程标题(h1 标签)`);
}
}
})();
// ==============================
// 🎬 主执行循环函数
// ==============================
function mainLoop() {
switchToNextUnfinishedVideo();
ensureVideoIsPlaying();
checkIfStudyTimeComplete();
clickConfirmIfPopupVisible();
}
// ==============================
// 🎯 切换下一个未播放完的视频
// ==============================
function switchToNextUnfinishedVideo() {
const current = document.querySelector('.video-title.clearfix.on .four')?.textContent;
if (current === '100%') {
const all = document.getElementsByClassName('video-title clearfix');
for (let i = 1; i < all.length; i++) {
const four = all[i].querySelector('.four')?.textContent;
if (four !== '100%') {
all[i].click();
addLog(`➡️ 切换至未完成视频:第 ${i + 1} 个`);
break;
}
}
}
}
// ==============================
// ⏱️ 学时统计与自动跳转下一门课
// ==============================
function checkIfStudyTimeComplete() {
const index = GM_getValue("下标");
const requiredPeriods = courseRequiredPeriods[index]; // 学时
const videoDivs = document.getElementsByClassName("video-title");
let totalSeconds = 0;
for (let videoDiv of videoDivs) {
const percent = videoDiv.querySelector(".four")?.textContent?.trim();
const time = videoDiv.querySelector(".three")?.textContent?.trim();
if (percent === "100%" && time) {
const [hh, mm, ss] = time.replace(/[()]/g, "").split(":").map(Number);
totalSeconds += hh * 3600 + mm * 60 + ss;
}
}
const completedPeriods = totalSeconds / 2700; // ✅ 1 学时 = 2700 秒 = 45 分钟
addLog(`⏳ 当前已完成 ${(completedPeriods).toFixed(2)} 学时 / 目标 ${requiredPeriods} 学时`);
if (completedPeriods >= requiredPeriods) {
const nextIndex = index + 1;
if (courseLinks[nextIndex]) {
GM_setValue("下标", nextIndex);
addLog(`✅ 学时达标,准备跳转下一课程:下标 ${nextIndex}`);
setTimeout(() => {
window.location.href = courseLinks[nextIndex];
}, 500);
} else {
addLog("🎉 所有课程已完成!");
}
}
}
// ==============================
// 📺 强制播放视频(静音避免浏览器限制)
// ==============================
function ensureVideoIsPlaying() {
const video = document.querySelector("#video-Player > video");
if (!video) {
addLog("⚠️ 未找到视频元素");
return;
}
// 如果没有静音,自动设置为静音
if (!video.muted) {
video.muted = true;
addLog("🔇 自动设置为静音");
}
// 如果播放速率为 1,则改为 2
if (video.playbackRate === 1) {
video.playbackRate = 2;
addLog("⏩ 播放速率设置为 2 倍速");
}
if (!video.paused) {
addLog("📡 正在挂机中...");
return;
}
video.play().then(() => {
addLog("🎬 视频恢复播放");
}).catch(err => {
addLog("⚠️ 视频播放失败:" + err.message);
});
}
// ==============================
// ✅ 弹窗检测与“确定”按钮点击处理
// ==============================
function clickConfirmIfPopupVisible() {
const confirmBtn = document.querySelector(".layui-layer-btn .layui-layer-btn0");
if (confirmBtn) {
confirmBtn.click();
addLog("🧩 检测到弹窗,已自动点击“确定”按钮");
}
}
// ==============================
// 🪵 日志输出框与记录函数
// ==============================
function createLogBox() {
const box = document.createElement('div');
box.id = 'logBox';
Object.assign(box.style, {
position: 'fixed',
bottom: '20px',
left: '20px',
width: '420px',
height: '260px',
backgroundColor: 'rgba(0, 0, 0, 0.85)',
color: '#00FF00',
border: '2px solid #00FF00',
borderRadius: '10px',
boxShadow: '0 0 10px #00FF00',
overflowY: 'auto',
padding: '12px',
fontFamily: 'monospace',
fontSize: '14px',
zIndex: '99999',
lineHeight: '1.5',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
});
box.innerHTML = `<div style="font-weight:bold;font-size:16px;margin-bottom:8px;color:#00FFFF;
border-bottom:1px solid #00FF00;padding-bottom:4px">📋 智慧教育挂机日志</div>`;
const style = document.createElement('style');
style.textContent = `
#logBox::-webkit-scrollbar {
width: 8px;
}
#logBox::-webkit-scrollbar-thumb {
background: #00ff00;
border-radius: 4px;
box-shadow: inset 0 0 6px rgba(0,255,0,0.5);
}
#logBox::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.3);
}
`;
document.head.appendChild(style);
document.body.appendChild(box);
}
function addLog(message) {
const log = document.getElementById('logBox');
if (!log) return;
const time = new Date().toLocaleTimeString('zh-CN', { hour12: false });
const line = document.createElement('div');
line.textContent = `[${time}] ${message}`;
log.appendChild(line);
log.scrollTop = log.scrollHeight;
console.log(`[${time}] ${message}`);
}