// ==UserScript==
// @name zhyDaDa华理超星网课助手
// @namespace http://zhydada.github.io/
// @version 1.6
// @description [个人向] 刷超星尔雅的网课, 现在支持pdf和视频的处理, 以及提供设置面板
// @author zhyDaDa
// @match *://mooc.s.ecust.edu.cn/*
// @match *://mooc1.chaoxing.com/*
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @icon data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAkACQAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAAgACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9yL68aMTFZP3i7toZ9q55xk9hWLovi+6vtRjhurdrdZQQrbzywGcc+wP5Vf1U/Jc/Vv5msK/t5JfIaFo98Mqy/McA4zx+tfP0uG8HTcqNZt8+0m2nHfVWaT6bpnPnGbPBYulS05Grt/Mv634vubDUHhtbdrlYwN7bjwxGcfkQfxrb0+4kkWBnb5m27grZXPfB71ytlbyRtcNKybppmk+Vs4zjj8MVv6VJu+yjPRl/mK58Zw3RqSp0cJpyfFO7bloul7Xv2svuDJ82ljcZVpK3IldP5ianGzpdemW/ma53Wb99PtlePaxZ9vzDpwT/AErsZ4CzyK0bFSTxtrI8ReFTrFnHHCogZJA5JjPIwRj9R+VZ4HiinVxyWNhy0722bSXn1fyX+YZ1k8sbiIV7rlirNdWYujag+oWztJtVlfaNo68A+/rXRaT9+25/jX+Yqv4d8KnR7OSOdVuGeQuCIzwMAY/T9a1rW18uWMLCyhWGPl6V0YjijCU8ZNYenJwvZNKy6d9fvDJcnlgq9Su2uWSskt1sf//Z
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
/**
* 1.0 视频基本操作
* 1.1 寻找猎物
* 1.2 确保在看到绿色勾勾出现再跳
* 1.3 自动判断视频有没有放过, 只挑没放过的视频看
* 1.4 克服浏览器禁止自动播放的限制, 尽力剔除bug
* 1.5 可以完成ppt和pdf的任务了
* 1.6 提供设置面板(保存设置有待完善)
*/
//#region /*======================div:函数定义==========================*/
window.zhy_settings = {
skipGreen: 1,
playbackRate: '1',
};
try {
let settings = JSON.parse(localStorage.getItem('settings'));
zhy_settings.skipGreen = settings.skipGreen && 1;
zhy_settings.playbackRate = settings.playbackRate || '1';
} catch { console.log("读入设置失败"); }
window.zhyDaDa = {};
/**
* 醒目的控制台输出
* @param {"string"} log 要打印到控制台的话
* @param {"报错"|"警告"|"启动"|"提示"|"幽灵白"} color 字体颜色 可选项为["报错"|"警告"|"启动"|"提示"|"幽灵白"] 默认绿色
* @param {"int"} fontSize 字体大小, 默认24
*/
zhyDaDa.sendLog = (log, color, fontSize) => {
switch (color) {
case "报错":
color = "red";
break;
case "警告":
color = "#F2AB26";
break;
case "启动":
color = "#A162F7";
break;
case "提示":
color = "#35D4C7";
break;
case "幽灵白":
color = "ghostwhite";
break;
default:
color = color || "#43bb88";
break;
}
fontSize = fontSize || 24;
console.log('%c' + log, 'color: ' + color + ';font-size: ' + fontSize + 'px;font-weight: bold;'); //text-decoration: underline;
}
zhyDaDa.getBaseDocument = () => {
var currentWindow = window;
while (currentWindow !== currentWindow.parent) {
currentWindow = currentWindow.parent;
}
return currentWindow.document;
}
/**
* 模拟cmd中的sleep函数
* @param {"number"} d deltaTime 即要等待的时间差 照旧以毫秒为单位
*/
zhyDaDa.sleep = (d) => {
for (let t = Date.now(); Date.now() - t <= d;);
}
//#endregion
/*======================div:全局效果==========================*/
//#region 防止多次启动
if (window === window.top) zhyDaDa.sendLog("\n###################\n##zhyDaDa 网课助手##\n###################", "启动", 32);
else return;
//#endregion
zhyDaDa.findPray = () => {
try {
let redTags = $(".orange01", zhyDaDa.getBaseDocument()).get();
let blankTags = $(".noJob", zhyDaDa.getBaseDocument()).get();
let Tags = redTags.concat(blankTags);
// let len = Tags.length;
// let ran = (Math.random() * len + 1) >> 0;
if (!zhy_settings.skipGreen) {
zhyDaDa.turnToNextPage();
window.setTimeout(() => { zhyDaDa.main(); }, 5000);
} else if (Tags.length > 0) {
let pray = Tags[0];
pray.parentNode.firstElementChild.click();
window.setTimeout(() => { zhyDaDa.main(); }, 5000);
}
} catch { console.log("找不到猎物了"); }
}
zhyDaDa.turnToNextPage = () => {
let focus = $(".currents", zhyDaDa.getBaseDocument()).get()[0];
let neighbour = focus.parentNode.nextElementSibling;
neighbour.firstElementChild.querySelector('a').click();
}
zhyDaDa.getIframes = () => {
let aaa = $("iframe", zhyDaDa.getBaseDocument()).get();
if (aaa[0].id == "iframe") {
aaa = $("iframe", aaa[0].contentWindow.document).get();
}
return aaa;
}
zhyDaDa.classifyTasks = (taskIframes) => {
let tasks = []; //[$iframe,"catagory"]
tasks = taskIframes.map((iframe) => {
let className = iframe.className;
let catagory;
if (className.indexOf("video") > 0) catagory = "video";
if (className.indexOf("pdf") > 0) catagory = "pdf";
if (className.indexOf("ppt") > 0) catagory = "ppt";
return [iframe, catagory];
});
return tasks;
}
zhyDaDa.dealTasks = (tasksList) => {
return new Promise((resolve, reject) => {
if (tasksList.length < 1) {
resolve();
} else {
let iframeNode = tasksList[0][0];
let iframeCata = tasksList[0][1];
switch (iframeCata) {
case "video":
zhyDaDa.dealVideo(iframeNode).then(() => {
tasksList.shift();
zhyDaDa.dealTasks(tasksList).then(resolve);
});
break;
case "pdf":
tasksList.shift();
zhyDaDa.dealTasks(tasksList).then(resolve);
zhyDaDa.dealPdf(iframeNode).then(() => {
tasksList.shift();
zhyDaDa.dealTasks(tasksList).then(resolve);
});
break;
case "ppt":
tasksList.shift();
zhyDaDa.dealTasks(tasksList).then(resolve);
zhyDaDa.dealPpt(iframeNode).then(() => {
tasksList.shift();
zhyDaDa.dealTasks(tasksList).then(resolve);
});
break;
default:
reject("No such catagory");
break;
}
}
})
}
//#region div: Video
zhyDaDa.dealVideo = (iframeNode) => {
return new Promise((resolve, reject) => {
let iconNode = iframeNode.parentNode;
if (iconNode.classList.length > 1 && zhy_settings.skipGreen) resolve();
else {
let videoNode = iframeNode.contentWindow.document.querySelector("video");
zhyDaDa.watchVideo(videoNode).then(resolve);
}
})
}
zhyDaDa.watchVideo = (video) => {
return new Promise((resolve, reject) => {
video.addEventListener('ended', onEnded); // 添加 ended 事件处理程序
video.volume = 0;
console.log('zhy_settings.playbackRate: ', zhy_settings.playbackRate);
video.playbackRate = Number(zhy_settings.playbackRate);
video.pause = () => { return true }
video.play(); // 开始播放视频
function onEnded() {
video.removeEventListener('ended', onEnded); // 删除 ended 事件处理程序
resolve(); // 视频播放完成,设置 Promise 状态为 fulfilled
}
});
}
//#endregion
//#region div: Pdf
zhyDaDa.dealPdf = (iframeNode) => {
return new Promise((resolve, reject) => {
let iconNode = iframeNode.parentNode;
if (iconNode.classList.length > 1 && zhy_settings.skipGreen) resolve();
else {
let btn = iframeNode.contentWindow.document.querySelector(".mkeRbtn");
let num = iframeNode.contentWindow.document.querySelector(".all").innerText;
num = Number(num);
for (let i = 0; i < num + 2; i++) {
btn.click();
}
}
})
}
//#endregion
//#region div: Ppt
zhyDaDa.dealPpt = (iframeNode) => {
return new Promise((resolve, reject) => {
let iconNode = iframeNode.parentNode;
if (iconNode.classList.length > 1 && zhy_settings.skipGreen) resolve();
else {
let btn = iframeNode.contentWindow.document.querySelector(".nextBtn");
let num = iframeNode.contentWindow.document.querySelector(".all").innerText;
num = Number(num);
for (let i = 0; i < num + 2; i++) {
btn.click();
}
}
})
}
//#endregion
zhyDaDa.main = () => {
let classifiedTasksList = zhyDaDa.classifyTasks(zhyDaDa.getIframes());
console.log(classifiedTasksList);
zhyDaDa.dealTasks(classifiedTasksList).then(() => { window.setTimeout(zhyDaDa.findPray, 3000); });
}
// 插入控制面板浮窗
zhyDaDa.insertPanel = () => {
let _style = `
<style>
#zhy_settings_panel {
position: fixed;
top: 50px;
left: 50px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
font-size: 12px;
z-index: 99999;
}
#zhy_settings_panel h3 {
margin-top: 0;
margin-bottom: 10px;
}
#drag-handle {
width: 100%;
height: 18px;
cursor: move;
background: grey;
border-radius: 10px;
margin-bottom: 10px;
}
#zhy_settings_panel .zhy_settings_item {
margin-bottom: 10px;
}
#zhy_settings_panel label {
display: inline-block;
width: 120px;
}
#playbackRateContainer {
display: grid;
}
#playbackRateContainer input[type="radio"] {
display: none;
}
#playbackRateContainer label {
flex: 1;
text-align: center;
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
}
#playbackRateContainer input[type="radio"]:checked + label {
background-color: #ccc;
}
</style>
`;
let _html = `
<div id="zhy_settings_panel">
<div id="drag-handle"></div>
<h3>Settings</h3>
<div class="zhy_settings_item">
<label for="skipGreenToggle">Skip Green:</label>
<input type="checkbox" id="skipGreenToggle" checked>
</div>
<div class="zhy_settings_item">
<label for="startWork">启动刷课:</label>
<button id="startWork">启动</button>
</div>
<div class="zhy_settings_item">
<label>Playback Rate:</label>
<div id="playbackRateContainer">
<input type="radio" id="rate1" name="playbackRate" value="1" checked>
<label for="rate1">1</label>
<input type="radio" id="rate1.25" name="playbackRate" value="1.25">
<label for="rate1.25">1.25</label>
<input type="radio" id="rate1.5" name="playbackRate" value="1.5">
<label for="rate1.5">1.5</label>
<input type="radio" id="rate2" name="playbackRate" value="2">
<label for="rate2">2</label>
</div>
</div>
</div>
`;
document.documentElement.insertAdjacentHTML('beforeend', _style + _html);
// 获取设置面板元素
var panel = document.getElementById("zhy_settings_panel");
// 获取浮窗标题栏元素
let panelHeader = document.getElementById("drag-handle");
// 定义变量记录鼠标按下时的坐标和面板的初始位置
let startX, startY, panelX, panelY;
// 给标题栏添加鼠标按下事件监听器
panelHeader.addEventListener("mousedown", function(e) {
// 记录鼠标按下时的坐标和面板的初始位置
startX = e.clientX;
startY = e.clientY;
panelX = panel.offsetLeft;
panelY = panel.offsetTop;
// 给 document 添加鼠标移动和松开事件监听器
document.addEventListener("mousemove", movePanel);
document.addEventListener("mouseup", stopPanel);
});
// 移动浮窗的函数
function movePanel(e) {
// 计算鼠标移动的距离
var deltaX = e.clientX - startX;
var deltaY = e.clientY - startY;
// 更新面板的位置
panel.style.left = panelX + deltaX + "px";
panel.style.top = panelY + deltaY + "px";
}
// 停止移动浮窗的函数
function stopPanel() {
// 移除鼠标移动和松开事件监听器
document.removeEventListener("mousemove", movePanel);
document.removeEventListener("mouseup", stopPanel);
}
// 获取设置面板中的元素
let skipGreenToggle = document.getElementById("skipGreenToggle");
let startWork = document.getElementById("startWork");
let playbackRateRadios = document.getElementsByName("playbackRate");
// 定义回调函数
function settingsChanged(ratio) {
zhy_settings.skipGreen = document.getElementById("skipGreenToggle").checked ? 1 : 0;
zhy_settings.playbackRate = ratio || zhy_settings.playbackRate;
window.localStorage.setItem('zhy_settings', JSON.stringify({ skipGreen: zhy_settings.skipGreen, playbackRate: zhy_settings.playbackRate }));
}
// 给skipGreenToggle添加change事件监听器,当状态发生改变时调用回调函数
skipGreenToggle.addEventListener("change", () => { settingsChanged(); });
// 给startWork添加click事件监听器,当点击时调用回调函数
startWork.addEventListener("click", () => { zhyDaDa.main(); });
// 给playbackRateRadios中的每个单选按钮添加click事件监听器,当点击时调用回调函数
playbackRateRadios.forEach(function(radio) {
radio.addEventListener("click", (event) => { settingsChanged(event.target.value); });
});
}
// $("#mainid", zhyDaDa.getBaseDocument()).get()[0].onchange = () => { zhyDaDa.main() };
zhyDaDa.insertPanel();
window.setTimeout(() => { zhyDaDa.main() }, 5000);
})();