// ==UserScript==
// @name 学习通刷课助手-自动静音,防止鼠标移出暂停,章节结束自动跳转下一节
// @namespace http://tampermonkey.net/
// @version 1.0.7
// @description 学习通课程自动挂机,当前脚本支持课程视频播放完成,自动跳转下一小节,章节测试自动跳过,后台播放防止视频暂停。
// @author Sweek
// @match *://*.chaoxing.com/*
// @license GPLv3
// @icon https://www.google.com/s2/favicons?sz=64&domain=csdn.net
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @require https://code.jquery.com/jquery-2.1.4.min.js
// ==/UserScript==
GM_setValue("playbackRate", GM_getValue("playbackRate") || 1);
GM_setValue("volume", GM_getValue("volume") || 0);
// 定义全局变量
let currentTime = null // 当前视频当前播放节点
let duration = null // 当前视频总长度
let progress = null // 当前视频播放进度
let playbackRate = GM_getValue("playbackRate") // 当前视频播放倍速
let volume = GM_getValue("volume") // 当前视频音量
// 课程章节相关数据
let courseName = null // 当前课程名称
let courseId = null // 当前课程id
let chapterInfo = [] // 当前课程所有章节数据
let currentChapterId = null // 当前所在章节id
let currentChapterName = null // 当前所在章节名称
let allChapterName = [] // 所有章节名称
let currentChapterTabArr = [] // 当前页面所有的分栏
let currentChapterTabName = '' // 当前页面激活的分栏名称
let videoProgressId = '' // 定时监听页面内容监听事件
// 获取当前页面的 URL
url = ''
chapterId = ''
let arrayEchelon = []; // 顺序执行梯队,初始化为空数组
const dealEvent = new Event("redeal", { bubbles: false, cancelable: false });
// 页面模板部分
// 页面模板部分
// 页面模板部分
// 页面样式
var popCSs = `
#my-window {
position: fixed;
top: 5px;
left: 20px;
width: 320px;
height: auto;
background-color: rgba(247, 247, 247, 1);
border: 1px solid #fff;
border-radius: 5px;
z-index: 9999;
overflow: hidden;
padding: 0 5px 10px 0;
font-family: fangsong;
font-weight: bold;
}
#my-window .header {
background-color: #4497fa;
color: #fff;
padding: 5px;
font-size: 16px;
font-family: 'fangsong';
font-weight: bold;
border-radius: 5px;
cursor: move;
height: 25px;
width: 320px;
}
#my-window .content {
width: 320px;
height: auto;
}
#my-window .content .content-title {
height: 22px;
width: 300px;
background-color: #dadada;
line-height: 22px;
padding-left: 5px;
font-size: 12px;
font-family: 'fangsong';
font-weight: 600;
boder-radius: 5px;
border-left: 4px solid #2196f3;
border-right: 4px solid #dadada;
margin-top: 5px;
border-radius: 3px;
}
#my-window .content .content-notice {
height: 80px;
width: 300px;
overflow: auto;
border: 1px solid gray;
border-radius: 3px;
padding: 5px;
margin-top: 5px;
font-size: 12px;
font-weight: bold;
}
#my-window .content .content-process {
height: 60px;
width: 300px;
overflow: auto;
border: 1px solid gray;
border-radius: 3px;
padding: 5px;
margin-top: 5px;
}
#my-window .content .content-log {
height: 120px;
width: 300px;
overflow: auto;
border: 1px solid gray;
border-radius: 3px;
padding: 5px;
margin-top: 5px;
}
#my-window .content .content-set {
height: 240px;
width: 300px;
overflow: auto;
border: 1px solid gray;
border-radius: 3px;
padding: 5px;
margin-top: 5px;
}
#my-window .content .content-set .content-set-content {
width: 290px;
display: flex;
justify-content: space-between;
border-top: 1px solid gray;
border-bottom: 1px solid gray;
padding: 5px 5px;
}
#my-window .content .content-set .content-set-content .control {
width: 220px;
height: 20px;
display: flex;
justify-content: space-between;
}
#my-window .content .content-set .content-set-content .control #playbackRateSelect {
width: 220px;
height: 20px;
border-radius: 3px;
border: 1px solid gray;
font-size: 13px;
font-family: fangsong;
font-weight: bold;
cursor: pointer;
}
#my-window .content .content-set .content-set-content .control #volumeSelect {
width: 220px;
height: 20px;
border-radius: 3px;
border: 1px solid gray;
font-size: 13px;
font-family: fangsong;
font-weight: bold;
cursor: pointer;
}
#my-window .content .content-set .content-set-content #email-input {
width: 175px;
height: 20px;
border-radius: 3px;
border: 1px solid gray;
font-size: 13px;
font-family: fangsong;
font-weight: bold;
}
#my-window .content .content-set .content-set-content #save-btn {
color: gray;
width: 40px;
height: 22px;
border-radius: 3px;
border: 1px solid gray;
font-size: 13px;
font-family: fangsong;
font-weight: bold;
cursor: pointer;
}
#save-btn:hover {
background-color: #e3e3e3!important;
}
#my-window .resizer {
position: absolute;
bottom: 0;
right: 0;
width: 20px;
height: 20px;
background-color: #2196f3;
cursor: se-resize;
border-radius: 0px;
z-index: 1;
}
#hide-btn {
height: 25px;
width: auto;
float: right;
margin-right: 10px;
background-color: #fff;
border: 1px solid gray;
border-radius: 5px;
font-size: 12px;
padding: 0 5px;
font-family: 'fangsong';
cursor: pointer;
}
#hide-btn:hover {
background-color: #4497fa;
color: #fff;
}
#hide-notice-btn {
height: 20px;
line-height: 20px;
width: auto;
background-color: #fff;
border: 1px solid gray;
border-radius: 3px;
font-size: 12px;
padding: 0 5px;
font-family: 'fangsong';
cursor: pointer;
float: right;
}
#hide-notice-btn:hover {
background-color: gray;
color: #fff;
}
#hide-process-btn {
height: 20px;
line-height: 20px;
width: auto;
background-color: #fff;
border: 1px solid gray;
border-radius: 3px;
font-size: 12px;
padding: 0 5px;
font-family: 'fangsong';
cursor: pointer;
float: right;
}
#hide-process-btn:hover {
background-color: gray;
color: #fff;
}
#hide-log-btn {
height: 20px;
line-height: 20px;
width: auto;
background-color: #fff;
border: 1px solid gray;
border-radius: 3px;
font-size: 12px;
padding: 0 5px;
font-family: 'fangsong';
cursor: pointer;
float: right;
}
#hide-log-btn:hover {
background-color: gray;
color: #fff;
}
#hide-set-btn {
height: 20px;
line-height: 20px;
width: auto;
background-color: #fff;
border: 1px solid gray;
border-radius: 3px;
font-size: 12px;
padding: 0 5px;
font-family: 'fangsong';
cursor: pointer;
float: right;
}
#hide-set-btn:hover {
background-color: gray;
color: #fff;
}
`
// 页面Html
var popHtml = `
<div class="header">学习通助手[1.0.7]
<button id="hide-btn">显示/隐藏</button>
</div>
<div class="content" id="my-window-content">
<div class="resizer" style="display: none;"></div>
<div class="row" style="border: 1px solid #ccc; padding: 5px;">
<div class="content-title">公告
<button id="hide-notice-btn">显示/隐藏</button>
</div>
<div class="content-notice" id="content-notice">
</div>
<div class="content-title">播放进度<span id="async-time"></span>
<button id="hide-process-btn">显示/隐藏</button>
</div>
<div class="content-process" id="content-process"></div>
<div class="content-title">执行日志
<button id="hide-log-btn">显示/隐藏</button>
</div>
<div class="content-log" id="content-log"></div>
<div class="content-title">配置
<button id="hide-set-btn">显示/隐藏</button>
</div>
<div class="content-set" id="content-set">
<div style="margin-bottom: 5px;font-family: 'fangsong';font-weight: bold;">
<div>
[该邮箱地址用来登录app网站,该邮箱需要和网站注册邮箱保持一致,不可为空,填写完,点击保存,可在网站查看课程进度。]
</div>
<div>[二维码:<a style="color: dodgerblue;cursor: pointer;" id="Qcode">点击查看,微信扫码</a>]</div>
</div>
<div class="content-set-content">
<label for="email-input">[邮箱]</label>
<div class="control">
<input type="email" id="email-input" placeholder="请输入邮箱地址">
<button id="save-btn">保存</button>
</div>
</div>
<div class="content-set-content">
<label for="email-input">[视频倍速]</label>
<div class="control">
<select id="playbackRateSelect">
<option value="1">1x</option>
<option value="2">2x</option>
<option value="3">3x</option>
<option value="4">4x</option>
</select>
</div>
</div>
<div class="content-set-content">
<label for="email-input">[视频音量]</label>
<div class="control">
<select id="volumeSelect">
<option value="0">静音</option>
<option value="0.2">20</option>
<option value="0.4">40</option>
<option value="0.6">60</option>
<option value="0.8">80</option>
<option value="1">100</option>
</select>
</div>
</div>
</div>
</div>
</div>
`
// 初始化添加页面弹窗以及悬浮球
function initPopup() {
// 添加CSS样式
GM_addStyle(popCSs);
// 创建窗口元素
const myWindow = document.createElement("div");
myWindow.id = "my-window";
myWindow.innerHTML = popHtml;
// 获取页面body元素
const body = document.getElementsByTagName("body")[0];
// 添加窗口和悬浮球到页面
body.appendChild(myWindow);
// 绑定隐藏窗口按钮的click事件
const hideBtn = document.querySelector("#hide-btn");
hideBtn.addEventListener("click", hideWindow);
// 绑定隐藏公告按钮的click事件
const hideNoticeBtn = document.querySelector("#hide-notice-btn");
hideNoticeBtn.addEventListener("click", hideNotice);
// 绑定隐藏播放进度按钮的click事件
const hideProcessBtn = document.querySelector("#hide-process-btn");
hideProcessBtn.addEventListener("click", hideProcess);
// 绑定隐藏日志按钮的click事件
const hideLogBtn = document.querySelector("#hide-log-btn");
hideLogBtn.addEventListener("click", hideLog);
// 绑定隐藏日志按钮的click事件
const hideSetBtn = document.querySelector("#hide-set-btn");
hideSetBtn.addEventListener("click", hideSet);
// 获取弹窗内容
var htmlContent = '<div style="width: 800px; height: 400px;display: flex;justify-content: center;align-items: center;"><img style="width: 400px; height: 400px;" src="https://www.sweek.top/api/preview/Qcode.png" alt="微信扫码"><img style="height: 400px;" src="https://www.sweek.top/api/preview/Qrcode.jpg" alt="微信扫码"></div>';
// 点击链接显示弹窗
document.getElementById('Qcode').addEventListener('click', function() {
showCustomPopup(htmlContent, 800, 400);
});
// 获取头部元素
const header = myWindow.querySelector('.header');
// 处理移动事件
let isDragging = false;
let mouseX = 0;
let mouseY = 0;
header.addEventListener('mousedown', function (e) {
e.preventDefault();
isDragging = true;
mouseX = e.clientX;
mouseY = e.clientY;
});
document.addEventListener('mousemove', function (e) {
if (isDragging) {
const deltaX = e.clientX - mouseX;
const deltaY = e.clientY - mouseY;
const newLeft = Math.min(
Math.max(0, myWindow.offsetLeft + deltaX),
window.innerWidth - myWindow.offsetWidth
);
const newTop = Math.min(
Math.max(0, myWindow.offsetTop + deltaY),
window.innerHeight - myWindow.offsetHeight
);
myWindow.style.left = newLeft + 'px';
myWindow.style.top = newTop + 'px';
mouseX = e.clientX;
mouseY = e.clientY;
}
});
document.addEventListener('mouseup', function () {
isDragging = false;
});
document.getElementById('playbackRateSelect').addEventListener('change', function() {
updatePlaybackRate(this.value);
});
document.getElementById("playbackRateSelect").value = playbackRate;
document.getElementById('volumeSelect').addEventListener('change', function() {
updateVolume(this.value);
});
document.getElementById("volumeSelect").value = volume;
}
// 当下拉框的值改变时,更新变量值
function updatePlaybackRate(val) {
playbackRate = val
GM_setValue("playbackRate", playbackRate);
// 刷新页面
location.reload();
}
// 当下拉框的值改变时,更新变量值
function updateVolume(val) {
volume = val
GM_setValue("volume", volume);
// 刷新页面
location.reload();
}
// 隐藏窗口函数
function hideWindow() {
var myWindowContent = document.getElementById("my-window-content");
var showPop = myWindowContent.style.display
if (showPop == '' || showPop == 'block') {
myWindowContent.style.display = "none";
} else {
myWindowContent.style.display = "block";
}
}
// 隐藏公告函数
function hideNotice() {
var contentNotice = document.getElementById("content-notice");
var showPop = contentNotice.style.display
if (showPop == '' || showPop == 'block') {
contentNotice.style.display = "none";
} else {
contentNotice.style.display = "block";
}
}
// 隐藏播放进度函数
function hideProcess() {
var contentProcess = document.getElementById("content-process");
var showPop = contentProcess.style.display
if (showPop == '' || showPop == 'block') {
contentProcess.style.display = "none";
} else {
contentProcess.style.display = "block";
}
}
// 隐藏日志函数
function hideLog() {
var contentLog = document.getElementById("content-log");
var showPop = contentLog.style.display
if (showPop == '' || showPop == 'block') {
contentLog.style.display = "none";
} else {
contentLog.style.display = "block";
}
}
// 隐藏设置函数
function hideSet() {
var contentSet = document.getElementById("content-set");
var showPop = contentSet.style.display
if (showPop == '' || showPop == 'block') {
contentSet.style.display = "none";
} else {
contentSet.style.display = "block";
}
}
function takeEmail() {
// 获取邮箱输入框和保存按钮
const emailInput = document.getElementById("email-input");
const saveBtn = document.getElementById("save-btn");
// 当保存按钮被点击时
saveBtn.addEventListener("click", function() {
// 获取输入的邮箱地址
const email = emailInput.value.trim();
// 检查邮箱地址是否有效
if (!isValidEmail(email)) {
notify('请输入有效的邮箱地址!', 2500)
return;
}
const url = 'https://www.sweek.top/api/checkEmailExists';
const data = {
email
};
if(email) {
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
},
body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
}).then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // 解析 JSON 响应数据
}).then(data => {
if(data.exists){
// 将邮箱地址存储到本地存储中
GM_setValue("savedEmail", email);
// 提示保存成功
notify('保存成功!课程信息将同步到该邮箱账号', 2500)
} else {
notify('邮箱账号尚未注册,请扫码前往留言信箱网站注册', 2500)
}
}).catch(error => {
console.error('Error:', error);
});
}
});
// 页面加载时,尝试从本地存储中获取已保存的邮箱地址并反显到输入框中
const savedEmail = GM_getValue("savedEmail");
if (savedEmail) {
emailInput.value = savedEmail;
}
}
// 邮箱地址验证函数
function isValidEmail(email) {
// 此处可以使用正则表达式等方式进行邮箱地址的验证
// 这里简单地判断邮箱是否包含 '@' 符号
return email.includes("@");
}
// 封装页面组件方法
// 封装页面组件方法
// 封装页面组件方法
// 页面弹窗显示
function showCustomPopup(htmlContent, width, height) {
// 创建弹窗容器
var popupContainer = document.createElement('div');
popupContainer.className = 'custom-popup-container';
// 设置弹窗容器样式
popupContainer.style.position = 'fixed';
popupContainer.style.top = '50px';
popupContainer.style.left = '0';
popupContainer.style.width = '100%';
popupContainer.style.height = 'calc(100% - 50px)';
popupContainer.style.display = 'flex';
popupContainer.style.justifyContent = 'center';
popupContainer.style.alignItems = 'center';
popupContainer.style.zIndex = '999999';
// 创建弹窗内容容器
var popupContent = document.createElement('div');
popupContent.className = 'custom-popup-content';
// 设置弹窗内容样式
popupContent.style.width = width + 'px';
popupContent.style.height = height + 'px';
popupContent.style.backgroundColor = '#fff';
popupContent.style.border = '1px solid #ccc';
popupContent.style.borderRadius = '5px';
popupContent.style.position = 'relative';
// 创建关闭按钮
var closeButton = document.createElement('button');
closeButton.innerHTML = '关闭';
closeButton.style.position = 'absolute';
closeButton.style.top = '5px';
closeButton.style.right = '5px';
closeButton.style.padding = '5px 10px';
closeButton.style.backgroundColor = '#ccc';
closeButton.style.border = 'none';
closeButton.style.borderRadius = '3px';
closeButton.style.cursor = 'pointer';
closeButton.style.fontFamily = 'Arial, sans-serif';
// 关闭按钮点击事件处理
closeButton.addEventListener('click', function() {
popupContainer.remove();
});
// 设置弹窗内容
popupContent.innerHTML = htmlContent;
// 将关闭按钮和弹窗内容添加到弹窗容器中
popupContent.appendChild(closeButton);
popupContainer.appendChild(popupContent);
// 将弹窗容器添加到页面中
document.body.appendChild(popupContainer);
}
// 页面通知提示
function notify(text, time) {
// 创建通知元素
var notification = document.createElement('div');
notification.className = 'notification';
// 设置通知内容
notification.innerHTML = '<div class="notification-content"><h2 style="font-size: 16px;font-weight: bold;color: #307dff;font-family: fangsong;">' + '学习通助手提示' + '</h2><p style="font-family: fangsong;font-size: 13px;font-weight: bold;">' + text + '</p></div>';
// 将通知添加到页面中
document.body.appendChild(notification);
// 设置通知样式
notification.style.position = 'fixed';
notification.style.top = '50px';
notification.style.left = '-400px'; // 从左边弹出
notification.style.transform = 'translateY(-50%)'; // 垂直居中
notification.style.transition = 'left 0.5s ease-in-out'; // 添加过渡效果
notification.style.zIndex = '999999';
notification.style.backgroundColor = '#fff';
notification.style.border = '1px solid #ccc';
notification.style.padding = '10px';
notification.style.borderRadius = '5px';
notification.style.lineHeight = '25px';
// 等待一小段时间后,移动通知到左边
setTimeout(function() {
notification.style.left = '20px'; // 移动到左边
}, 100);
// 设置定时器,在指定时间后移除通知
setTimeout(function() {
// 移动通知到左边以外
notification.style.left = '-400px';
// 等待过渡效果完成后,移除通知元素
setTimeout(function() {
notification.remove();
}, 500);
}, time);
}
// 添加执行日志
function addlog(str, color) {
var _time = new Date().toLocaleTimeString()
var contentLog = window.top.document.querySelector('.content-log');
var newContent = '<p style="color: ' + color + ';">[' + _time + ']' + str + '</p><hr>';
contentLog.innerHTML += newContent;
// 将滚动条滚动到底部
contentLog.scrollTop = contentLog.scrollHeight;
}
/** 监听事件 */
document.addEventListener("redeal", () => {
dealAnsEchelon(arrayEchelon);
});
/** 初始化 */
function initAll() {
// console.log('location.pathname:::+ ', location.pathname)
if(location.pathname == '/mooc-ans/knowledge/cards') {
addlog('正在获取当前页面的任务...');
const arrayAnsAll = document.querySelectorAll(".ans-attach-ct");
addlog(`当前页面任务数量为: ${Array.from(arrayAnsAll).length}`);
const arrayAns = document.querySelectorAll('.ans-attach-ct:not(.ans-job-finished)');
const taskCount = arrayAns.length;
// addlog(`找到的任务数量: ${taskCount}`);
if (arrayAns && taskCount > 0) {
try {
const arrayType = getIframesType(arrayAns);
console.log('任务类型数组:', arrayType);
const arrayDocument = getAllIframesDocument(arrayAns);
console.log('任务文档数组:', arrayDocument);
arrayEchelon = distributeAns(arrayType, arrayDocument);
// addlog(`当前页面待办任务数量为: ${arrayEchelon.length}`);
// 触发处理任务的事件
document.dispatchEvent(dealEvent);
} catch (error) {
console.error("初始化过程中发生错误:", error);
}
} else {
addlog("当前页面没有可处理的任务,直接跳过章节");
// 如果没有任务,
skipChapter();
}
}
}
// 获取任务属性
function getIframesType(arrayAns) {
return Array.from(arrayAns)
.map(ans => {
const jsonStr = ans.querySelector("iframe")?.getAttribute("data");
if (!jsonStr) {
console.warn("未找到 data 属性或 data 属性为空。");
return null;
}
try {
const json = JSON.parse(jsonStr);
// 根据需求获取任务类型
// 课程类型
if(json && json.type) {
return json.type;
}
// 测试类型
if(json && json.worktype) {
return json.worktype;
}
} catch (error) {
console.warn("解析 JSON 失败:", error);
return null;
}
})
.filter(type => type !== null);
}
// 获取任务 document
function getAllIframesDocument(arrayAns) {
return Array.from(arrayAns)
.map(ans => ans.querySelector("iframe")?.contentWindow?.document)
.filter(doc => doc !== undefined && doc !== null);
}
// 按任务属性映射处理函数
const handlerMap = {
".mp4": videoHandler,
".wmv": videoHandler,
".avi": videoHandler,
".mkv": videoHandler,
".flv": videoHandler,
".doc": pptxHandler,
".docx": pptxHandler,
".pptx": pptxHandler,
".pdf": pptxHandler,
".ppt": pptxHandler,
".mp3": audioHandler,
".wav": audioHandler
};
// 按任务属性分配执行函数
function distributeAns(arrayType, arrayDocument) {
return arrayType.map((type, index) => {
const handler = handlerMap[type] || ignoreAns;
return { document: arrayDocument[index], handler };
});
}
// 处理单个任务
function dealSingleAns(singleAns) {
if (singleAns && singleAns.handler && typeof singleAns.handler === 'function') {
singleAns.handler(singleAns.document);
} else {
console.warn("无效的任务或处理函数。");
document.dispatchEvent(dealEvent);
}
}
/** 工具函数 */
/** 工具函数 */
/** 工具函数 */
// 获取当前时间,年月日时分秒
function getCurrentDateTime() {
var now = new Date();
var year = now.getFullYear();
var month = (now.getMonth() + 1).toString().padStart(2, '0'); // 月份从0开始,需要加1,并确保两位数格式
var day = now.getDate().toString().padStart(2, '0'); // 确保两位数格式
var hours = now.getHours().toString().padStart(2, '0'); // 确保两位数格式
var minutes = now.getMinutes().toString().padStart(2, '0'); // 确保两位数格式
var seconds = now.getSeconds().toString().padStart(2, '0'); // 确保两位数格式
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 设置播放进度
function setVideoProcess(val1, val2, val3) {
var _process = window.top.document.querySelector('#content-process');
var _time = getCurrentDateTime()
var newContent = '<p style="color: #000;">[' + _time + ']' + '</p><hr><p>' + '播放进度:' + val1 + '</p><hr><p>' + '视频长度:' + val2 + 's' + '/' +val3 + 's' + '</p>';
_process.innerHTML = newContent;
}
// 获取视频播放进度-定时监听页面内容进行下一步处理
function getVideoProgress() {
// 同步课程进度
if(location.pathname == '/mycourse/studentstudy') {
const emailInput = document.getElementById("email-input");
const email = emailInput.value.trim();
const url = 'https://www.sweek.top/api/checkEmailExists';
const data = {
email
};
if(email) {
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
},
body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
}).then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // 解析 JSON 响应数据
}).then(data => {
if(data.exists){
syncCourseData()
} else {
var _async_time = window.top.document.querySelector('#async-time');
var newContent = `<span style="color: #000;">[同步课程信息失败,邮箱未注册]</span>`;
_async_time.innerHTML = newContent;
}
}).catch(error => {
console.error('Error:', error);
});
}
// 脚本运行过程中如果弹出弹窗,发现后关闭-10s执行一次
const jobFinishTip = document.querySelector(".jobFinishTip");
const nextChapter = document.querySelector(".nextChapter");
if (jobFinishTip) {
const computedStyle = getComputedStyle(jobFinishTip);
if (computedStyle.display !== 'none') {
nextChapter.click()
}
}
}
}
// 获取页面url
function getURLInfo() {
if(location.pathname == '/mycourse/studentstudy') {
url = location.href
// 获取问号后面的部分
var queryString = url.split('?')[1];
// 将查询字符串拆分为参数对
var queryParams = queryString.split('&');
// 创建一个对象来存储参数
var params = {};
// 遍历参数对,将它们存储在对象中
queryParams.forEach(function(queryParam) {
var parts = queryParam.split('=');
var key = decodeURIComponent(parts[0]);
var value = decodeURIComponent(parts[1]);
params[key] = value;
});
chapterId = params['chapterId']
courseId = params['courseId']
GM_setValue("courseId", courseId);
}
}
// 获取当前页面章节小节的所有分栏
function getSubChapter() {
try {
if(location.pathname == '/mycourse/studentstudy') {
var subChapter = window.top.document.querySelector('#prev_tab');
if(subChapter) {
currentChapterTabArr = subChapter.querySelectorAll('li');
currentChapterTabArr.forEach(function(item) {
if(item.className === 'active') {
currentChapterTabName = item.innerText
}
})
}
}
} catch (error) {
console.error('An error occurred:', error);
// location.reload(); // 刷新页面
}
}
// 获取课程所有章节节点数据
function getChapterCodeInfo() {
if(location.pathname === '/mooc-ans/knowledge/cards') {
var chapter = window.top.document.querySelectorAll('.posCatalog_select')
chapterInfo = chapter
allChapterName=[]
chapterInfo.forEach(function(item) {
allChapterName.push({
id: item.id,
title: item.innerText,
active: item.classList.contains('posCatalog_active') ? 1 : 0,
ifTitle: item.classList.contains('firstLayer') ? 1 : 0,
status: item.childNodes[3]?.className == 'icon_Completed prevTips' ? 1 : 0
})
if (item.classList.contains('posCatalog_active')) {
currentChapterId = item.id
GM_setValue("currentChapterId", currentChapterId);
currentChapterName = item.innerText
GM_setValue("currentChapterName", currentChapterName);
}
});
GM_setValue("chapterInfo", JSON.parse(JSON.stringify(allChapterName)));
addlog('当前章节为' + currentChapterName, 'green')
}
}
// 获取公告数据
function getBoard() {
fetch('https://www.sweek.top/api/board')
.then(response => response.json())
.then(data => {
// 在这里处理接收到的数据
var notice = document.querySelector('#content-notice');
notice.innerHTML = data.text
})
.catch(error => {
// 在这里处理错误
console.error('Error:', error);
});
}
// 同步课程数据至数据库
function syncCourseData() {
const url = 'https://www.sweek.top/api/insertOrUpdateCourse';
const email = GM_getValue("savedEmail")
const course_name = GM_getValue("courseName")
const current_chapter_id = GM_getValue("currentChapterId")
const current_chapter_name = GM_getValue("currentChapterName")
const course_id = GM_getValue("courseId")
const process = document.querySelector('#content-process').innerHTML
const chapter_info = GM_getValue("chapterInfo")
var chapter = window.top.document.querySelectorAll('.posCatalog_select')
allChapterName = []
chapter.forEach(function(item) {
allChapterName.push({
id: item.id,
title: item.innerText,
active: item.classList.contains('posCatalog_active') ? 1 : 0,
ifTitle: item.classList.contains('firstLayer') ? 1 : 0,
status: item.childNodes[3]?.className == 'icon_Completed prevTips' ? 1 : 0
})
if (item.classList.contains('posCatalog_active')) {
currentChapterId = item.id
GM_setValue("currentChapterId", currentChapterId);
currentChapterName = item.innerText
GM_setValue("currentChapterName", currentChapterName);
}
});
const data = {
email,
course_id: course_id || '',
course_name: course_name || '测试',
chapter: JSON.stringify(allChapterName),
current_chapter: current_chapter_id + ',' + current_chapter_name,
process
};
// 播放异常刷新页面
// if(process.includes("NaN")) {
// location.reload(); // 刷新页面
// return
// }
if(email && process) {
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 声明请求主体的内容类型为 JSON
},
body: JSON.stringify(data), // 将数据对象转换为 JSON 字符串并作为请求主体
}).then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // 解析 JSON 响应数据
}).then(data => {
var _async_time = window.top.document.querySelector('#async-time');
var _time = getCurrentDateTime()
var newContent = `<span style="color: #000;">[同步时间:${_time}]</span>`;
_async_time.innerHTML = newContent;
}).catch(error => {
console.error('Error:', error);
});
}
}
/** 执行函数 */
// 直接跳过
function skipChapter() {
addlog("跳过章节");
const chapterNext = window.top.document.querySelector("#prevNextFocusNext");
if (chapterNext) {
chapterNext.click();
} else {
// console.warn("未找到 #prevNextFocusNext 元素。");
}
setTimeout(() => {
const tip = document.querySelector(".maskDiv.jobFinishTip.maskFadeOut");
if (tip) {
const tipNextChapter = document.querySelector(".jb_btn.jb_btn_92.fr.fs14.nextChapter");
tipNextChapter?.click();
} else {
// console.warn("未找到完成提示或下一章节按钮。");
}
setTimeout(() => {
initAll();
// 注意:这里不再立即调用 dealAnsEchelon,而是依靠事件触发
}, 5000);
}, 2000);
}
// 忽略任务
function ignoreAns() {
addlog("无法处理, 忽略任务");
setTimeout(() => {
document.dispatchEvent(dealEvent);
}, 1500);
}
// 处理视频
function videoHandler(idocument) {
addlog("处理视频任务中...");
const video = idocument.querySelector("video");
const videoPlayButton = idocument.querySelector(".vjs-big-play-button");
if (!video) {
// console.log("未找到视频元素。");
// document.dispatchEvent(dealEvent);
return;
}
// 播放视频
videoPlayButton?.click();
// 监听视频暂停事件并在暂停时继续播放
const handlePause = () => {
// console.log("视频暂停,自动恢复播放...");
videoPlayButton?.click();
// 如果检测到弹窗,则点击隐藏
const modalDialog = idocument.querySelector(".vjs-modal-dialog-content");
const closeButton = idocument.querySelector(".vjs-done-button");
const ariaHiddenValue = modalDialog.getAttribute("aria-hidden");
if (modalDialog && closeButton && !ariaHiddenValue) {
setTimeout(() => {
closeButton?.click();
// console.log('模块弹窗已隐藏...')
}, 2000);
}
};
// 设置播放倍速(延迟设置,确保视频已加载完成)
video.addEventListener('loadeddata', () => {
// 设置音量
video.volume = volume;
addlog(`已将视频音量调节为${volume*100}%`);
// 设置倍速
video.playbackRate = playbackRate;
addlog(`已将视频倍速调节为${playbackRate}X`);
});
video.addEventListener("pause", handlePause);
// 监听视频播放结束事件,并在结束时触发 dealEvent,并且移除暂停事件监听
video.addEventListener("ended", () => {
document.dispatchEvent(dealEvent);
video.removeEventListener("pause", handlePause);
}, { once: true });
// 同步显示视频进度
video.addEventListener("timeupdate", function() {
let currentTime = video.currentTime; // 当前播放时间(以秒为单位)
let duration = video.duration; // 视频总时长(以秒为单位)
let progress = (currentTime / duration) * 100; // 计算播放进度百分比
setVideoProcess(progress.toFixed(2) + '%', currentTime.toFixed(0), duration.toFixed(0)); // 设置视频播放进度显示
});
// 10秒之后如果还没播放,执行点击播放按钮
setTimeout(() => {
if (video.paused && !video.ended) {
videoPlayButton?.click();
addlog('由于程序出错未自动播放,现重新模拟点击播放按钮...');
}
}, 10000);
}
// 处理 PPT & PDF
function pptxHandler(idocument) {
// notify('等待PPT/PDF加载完成...', 5000)
addlog("处理 PPT/PDF 任务中...");
const iframe = idocument.querySelector("iframe");
if (!iframe) {
// console.warn("未找到嵌入的 iframe 元素。");
document.dispatchEvent(dealEvent);
return;
}
const sDocument = iframe.contentWindow?.document;
if (!sDocument) {
// console.warn("无法获取 PPT/PDF 的文档内容。");
document.dispatchEvent(dealEvent);
return;
}
const finalHeight = sDocument.documentElement.scrollHeight;
let currentHeight = 0;
const scrollStep = 400; // 每次滚动的高度
const scrollInterval = 1000; // 滚动的时间间隔(毫秒)
const timer = setInterval(() => {
if (currentHeight >= finalHeight) {
clearInterval(timer);
document.dispatchEvent(dealEvent);
return;
}
currentHeight += scrollStep;
sDocument.defaultView.scrollTo(0, currentHeight);
}, scrollInterval);
}
// 处理音频
function audioHandler(idocument) {
addlog("处理音频任务中...");
const audio = idocument.querySelector("audio");
const audioPlayButton = idocument.querySelector(".vjs-play-control.vjs-control.vjs-button");
if (!audio) {
console.warn("未找到音频元素。");
document.dispatchEvent(dealEvent);
return;
}
// 播放音频
audioPlayButton?.click();
// 监听音频播放结束事件
audio.addEventListener("ended", () => {
document.dispatchEvent(dealEvent);
}, { once: true });
// 监听音频暂停事件并在暂停时继续播放
audio.addEventListener("pause", () => {
// console.log("音频暂停,自动恢复播放...");
audioPlayButton?.click();
});
}
/** 任务梯队顺序处理 */
function dealAnsEchelon(arrayEchelon) {
const remainingTasks = arrayEchelon.length;
addlog(`待处理任务数量为: ${remainingTasks}`);
if (remainingTasks === 0) {
skipChapter();
return;
}
const nextTask = arrayEchelon.shift();
try {
dealSingleAns(nextTask);
} catch (error) {
console.error("处理任务时发生错误:", error);
// 继续处理下一个任务
dealAnsEchelon(arrayEchelon);
}
}
// 方法执行入口
(function () {
// 进入学习通弹出提示
if(location.pathname == '/base') {
notify('已进入学习通首页,请进入课程,选择需要学习的课程', 5000)
}
// 进入课程弹出提示
if(location.pathname == '/mooc2-ans/mycourse/stu') {
courseName = window.top.document.querySelector('.classDl .colorDeep').getAttribute('title')
GM_setValue("courseName", courseName);
notify('已进入课程:' + courseName + ',请选择需要学习的章节', 5000)
}
// 初始化显示页面弹窗
if(location.pathname == '/mycourse/studentstudy') {
initPopup()
// 获取公告数据
getBoard()
// 邮箱操作
takeEmail()
}
// 获取页面章节节点数据
getChapterCodeInfo()
// 获取当前页面章节小节的所有分栏
getSubChapter()
// 获取页面url信息
getURLInfo()
// 定时打印视频播放进度-定时监听页面内容进行下一步处理
videoProgressId = setInterval(() => {
getVideoProgress()
}, 10000);
/** 执行代码 */
setTimeout(() => {
initAll();
// 不再直接调用 dealAnsEchelon,这将由 initAll 中的事件触发
}, 1500);
})();