// ==UserScript==
// @name 国家中小学智慧教育平台刷课脚本(16倍速,自动答题,自动切换列表中的视频,后台播放,学时不更新的解决方法看下面)
// @namespace http://tampermonkey.net/
// @version 3.3.3
// @license CC BY-NC-SA
// @description 针对2023暑期教师研修更新,16倍速,自动答题,自动切换列表中的视频,后台播放,学时不更新的解决方法看下面
// @author HGGshiwo
// @match https://*.zxx.edu.cn/teacherTraining/courseDetail*
// @match https://basic.smartedu.cn/teacherTraining/courseDetail*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
(function () {
("use strict");
const courses = [
{ course_id: '08e005b6-442f-40d0-bf5d-ac3ed35feaeb', resNo: 1, groupNo: 2 },
{ course_id: '1a11eeee-8d43-4d3b-8818-1b842a0ee966', resNo: 1, groupNo: 1 },
{ course_id: '47678428-4c27-4526-a8cf-15fb65138617', resNo: 1, groupNo: 1 }
]
// function
const changInputValue = (inputDom, newText) => {
if (!inputDom) {
return;
}
let lastValue = inputDom.value;
inputDom.value = newText;
let event = new Event("input", { bubbles: true });
event.simulated = true;
let tracker = inputDom._valueTracker;
if (tracker) {
tracker.setValue(lastValue);
}
inputDom.dispatchEvent(event);
};
function findLastIndex(array, predicate) {
// 先将数组反转
const reversedArray = array.slice().reverse();
// 使用findIndex找到满足条件的元素的索引
const index = reversedArray.findIndex(predicate);
if (index === -1) {
return -1; // 若未找到,则直接返回-1
}
// 计算满足条件的元素在原数组中的索引
const originalIndex = array.length - 1 - index;
return originalIndex;
}
const State = {
LoadPage: "loadPage",
GetActive: "getActive",
SwitchSource: "switchSource",
PlayVideo: "playVideo",
HandlePlayRes: "handlePlayRes",
WaitPlay: "waitPlay",
SwitchActive: "switchActive",
SwitchFirst: "switchFirst",
TaskEnd: "taskEnd",
}
var state = State.LoadPage;
var groups = undefined;
var groupNo = undefined;
var resItems = undefined;
var resNo = undefined;
var videoErr = undefined;
var interval = undefined;
const func_table = {
loadPage: () => {
var video = document.querySelector("video");
var resItems = document.querySelector(".resource-item");
if (!!video && !!resItems) {
return State.GetActive
}
else {
console.log(666, "等待视频加载")
return State.LoadPage
}
},
getActive: () => {
groups = document.querySelector(".fish-collapse.fish-collapse-icon-position-right.tcourse-catalog.undefined").childNodes
//适配chrome版本低于97, firefox版本低于108的用户
groupNo = findLastIndex([...groups], group => {
resItems = [...group.querySelectorAll(".resource-item")]
resNo = resItems.findIndex(resItem => resItem.className.includes("active"))
return resNo !== -1
})
return State.SwitchSource
},
switchSource: () => {
//视频修改为标清 zxj663建议添加
let sped = document.querySelector(
"div.vjs-menu-button.vjs-menu-button-popup.vjs-control.vjs-button.vjs-resolution-button > span"
);
if(!sped) {
return State.SwitchSource
}
if (sped.innerText != "标清") {
document
.querySelector(
"div.vjs-menu-button.vjs-menu-button-popup.vjs-control.vjs-button.vjs-resolution-button > div > ul > li:nth-child(3) > span.vjs-menu-item-text"
)
.click();
}
return State.PlayVideo
},
playVideo: () => {
let icons = resItems[resNo].getElementsByClassName("iconfont");
if (icons[0] && icons[0].className.includes("icon_checkbox_fill")) {
console.log(666, `第${groupNo + 1}组, 第${resNo + 1}个视频已经观看`);
return State.SwitchActive
}
console.log(666, `开始观看: 第${resNo + 1}个视频,第${groupNo + 1}组`);
var video = document.querySelector("video");
if (!!video) {
video.muted = true;
video.play().then(() => {
videoErr = false
}).catch((err) => {
console.log(666, err);
videoErr = true
});
renderMenu()
video.playbackRate = rateMenu[active].value;
video.addEventListener("pause", () => state = State.PlayVideo, false)
video.addEventListener("ended", () => state = State.SwitchActive, false)
return State.HandlePlayRes
}
else {//是pdf,直接跳下一个
return State.SwitchActive
}
},
handlePlayRes: () => {
//处理播放的结果
return videoErr === undefined ? State.HandlePlayRes : videoErr ? State.PlayVideo : State.WaitPlay
},
waitPlay: () => { return state },
switchActive: () => {
//从列表中查找当前的课程
const index = courses.findIndex(course => window.location.href.includes(course.course_id))
if (index !== -1 && (courses[index].groupNo < groupNo || (courses[index].groupNo == groupNo && courses[index].resNo <= resNo))) {
if (index + 1 < courses.length) {
console.log(666, "进入下一个学习页面");
window.open(`https://basic.smartedu.cn/teacherTraining/courseDetail?courseId=${courses[index + 1].course_id}`, "_self");
}
else {
return State.TaskEnd
}
}
//如果没看完当前组,则观看当前组的下一个视频
if (resNo + 1 != resItems.length) {
resNo += 1
resItems[resNo].click();
console.log(666, `点击当前组的下一个视频`);
return State.SwitchSource;
}
//如果看完了当前组,没看完当前页面,则看下一个页面
if (groupNo + 1 != groups.length) {
console.log(666, `点击下一组的第一个视频`);
groupNo += 1
let header = groups[groupNo].querySelector(".fish-collapse-header")
if (!!header) {
header.click()
}
return State.SwitchFirst
}
//如果都看完了
return State.TaskEnd
},
switchFirst: () => {
resItems = groups[groupNo].getElementsByClassName("resource-item");
resNo = 0
resItems[resNo].click();
return State.SwitchSource
},
taskEnd: () => {
if(!!interval) {
clearInterval(interval)
}
window.alert("都看完了")
return State.TaskEnd;
}
}
const setVideoHandler = () => {
interval = setInterval(() => {
try {
var popup = false;
[".nqti-option", ".index-module_footer_wewZ2 .fish-btn", ".fish-modal-confirm-btns .fish-btn"].forEach(selector => {
let dom = document.querySelector(selector)
if (!!dom) {
popup = true
dom.click()
}
})
//增加填空题支持
var inputForm = document.querySelector(".index-module_box_blt8G");
if (!!inputForm) {
popup = true
changInputValue(inputForm.getElementsByTagName("input")[1], " ");
}
if (!popup) {
state = func_table[state]()
console.log(666, `${state}已经完成!`)
}
else {
console.log(666, "处理弹窗")
}
}
catch (err) {
console.log(666, `${state}: ${err}`)
}
}, 1000)
}
//修改播放速度
const changeRate = (rate, index) => {
localStorage.setItem("_active", `${index}`)
active = index
document.querySelector(".vjs-playback-rate-value").innerHTML = rateMenu[index].title
document.getElementsByTagName("video")[0].playbackRate = rate
return false
}
//修改速度菜单
const renderMenu = () => {
document.querySelector(".vjs-playback-rate .vjs-menu-content").innerHTML =
rateMenu.map((rate, index) =>
`<li class="vjs-menu-item" tabindex="-1" role="menuitemradio" aria-disabled="false" aria-checked="${index == active}">
<span class="vjs-menu-item-text">${rate.title}</span>
<span class="vjs-control-text" aria-live="polite"></span>
</li>`
).join(" ")
const doms = document.querySelectorAll(".vjs-playback-rate .vjs-menu-content .vjs-menu-item")
rateMenu.forEach((rate, index) => {
doms[index].addEventListener("click", () => changeRate(rate.value, index), false)
})
//显示速度控制菜单
const rateButtons = document.getElementsByClassName("vjs-playback-rate vjs-menu-button vjs-menu-button-popup vjs-control vjs-button vjs-hidden")
if (rateButtons.length > 0) {
rateButtons[0].classList.remove("vjs-hidden")
document.querySelector(".vjs-playback-rate-value").innerHTML = rateMenu[active].title
}
}
//获取速度
let activeStr = localStorage.getItem("_active")
const rateMenu = [{ title: "1x", value: 1 }, { title: "1.5x", value: 1.5 }, { title: "2x", value: 2 }, { title: "2.5x", value: 2.5 }, { title: "3x", value: 3 }]
let active = activeStr === null ? 0 : parseInt(activeStr)
//下面开始运行脚本
console.log(666, "开始执行脚本")
setVideoHandler();
})();