// ==UserScript== // @name 青书学堂网课自动学习助手 // @namespace https://degree.qingshuxuetang.com // @version 1.0.4 // @description 支持视频自动播放、文档自动跳过、课程自动跳转下一节,并提供课程列表进度面板显示学习时长与完成状态 // @author eivy // @match *https://degree.qingshuxuetang.com/xayd/Student/Course/CourseShow* // @match *https://degree.qingshuxuetang.com/xayd/Student/Course/CourseStudy* // @grant GM_addStyle // @grant GM.setValue // @grant GM.getValue // @grant GM_addElement // @license MIT // @run-at document-start // @downloadURL https://update.greasyfork.icu/scripts/568720/%E9%9D%92%E4%B9%A6%E5%AD%A6%E5%A0%82%E7%BD%91%E8%AF%BE%E8%87%AA%E5%8A%A8%E5%AD%A6%E4%B9%A0%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/568720/%E9%9D%92%E4%B9%A6%E5%AD%A6%E5%A0%82%E7%BD%91%E8%AF%BE%E8%87%AA%E5%8A%A8%E5%AD%A6%E4%B9%A0%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== //整个函数起始位 (function () { "use strict"; // 默认播放速度 //青书学堂是根据课程播放时长计算完成度,对视频播放速度有要求,所以这里设置为1.提高视频播放速度对课程进度没有影响 const speed = 1; const isListPage = window.location.href.includes( "xayd/Student/Course/CourseStudy", ); const isDetailPage = window.location.href.includes( "xayd/Student/Course/CourseShow", ); console.log("脚本加载成功....."); //当前 页面 为列表页时执行该部分内容 if (isListPage) { let url1Promise = new Promise((resolve) => { window.resolveUrl1 = resolve; // 暴露resolve方法,供请求完成时调用 }); let url2Promise = new Promise((resolve) => { window.resolveUrl2 = resolve; // 暴露resolve方法 }); console.log("油猴:开始重写XHR"); const originalXHR = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url, async) { // 匹配目标XHR请求 if (url.includes("GetCoursewareTree")) { console.log("油猴:匹配到目标XHR →", url); // 监听请求加载完成 this.addEventListener( "load", function () { if (this.status >= 200 && this.status < 300) { const data = JSON.parse(this.responseText); window.resolveUrl1(data); // 完成第一个Promise } else { window.resolveUrl1({}); // 失败也resolve,避免Promise.all卡死 } }, 200, ); } // 匹配目标XHR请求 if (url.includes("GetStudyRecordAndScore")) { console.log("油猴:匹配到目标XHR →", url); // 监听请求加载完成 this.addEventListener( "load", function () { if (this.status >= 200 && this.status < 300) { const data = JSON.parse(this.responseText); window.resolveUrl2(data); // 完成第一个Promise } else { window.resolveUrl2({}); // 失败也resolve,避免Promise.all卡死 } }, 100, ); } // 执行原生open方法 originalXHR.call(this, method, url, async); }; Promise.all([url1Promise, url2Promise]).then( ([class_list, class_time_record_list]) => { initListPage(class_list, class_time_record_list); }, ); setTimeout(() => { // 若超时,强制resolve空数据 window.resolveUrl1(null); window.resolveUrl2(null); }, 100000); } function initDelatilsPage() { if (!isDetailPage) { return; } createPEelement(); const class_id = getUrlParameters("courseId"); const currentClassNumber = getUrlParameters("nodeId"); const classItemsCache = GM.getValue("classid_" + class_id); if (!classItemsCache) { return; } classItemsCache .then((data) => { const currentIndex = data.findIndex( (item) => item.id === currentClassNumber, ); if (currentIndex === -1 || currentIndex + 1 >= classItemsCache.length) { alert("没有下一节课程了"); return; } //下一节课程信息 const nextInfo = data[currentIndex + 1]; detailInit(nextInfo); }) .catch((error) => { console.log(error); }); } function detailInit(nextInfo) { if (!nextInfo) { return; } const video = document.querySelector("#vjs_video_3_html5_api"); const doc = document.querySelector("[id^='easyXDM_']"); if (doc || video) { if (doc) { let count = 5; let countdownTimer = setInterval(() => { if (count <= 0) { clearInterval(countdownTimer); } count--; const p = document.getElementById("p-learn-title"); if (p) { p.innerText = " 正在运行中...:文档将在 " + count + " 秒后跳过"; } }, 1000); console.log("当前页面内容为文档"); const timer = setTimeout(() => { CoursewareNodesManager.onMenuClick(nextInfo.id); clearTimeout(timer); }, 5000); } else { console.log("当前页面内容为视频"); autoPlayMedia(video, nextInfo); } } else { const timer = setTimeout(() => { detailInit(nextInfo); clearTimeout(timer); }, 2000); } } //当页面为课程列表页时加载课程列表显示在页面的右边 function loadClassList() { if (!isListPage) { return; } GM_addStyle(` .class-name { width: 100px; text-align: center; overflow: hidden; /* 隐藏溢出内容 */ text-overflow: ellipsis; /* 显示省略号 */ white-space: nowrap; /* 文本不换行 */ } .p-lesson-list{ position: relative; margin-left: 20px; display: inline-block; } .btn{ background-color: #22bb93; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; transition: background-color 0.3s ease; outline: none; display: inline-block; text-align: center; } .btn:hover { background-color: #66b1ff; } .btn:active { background-color: #3a8ee6; } .finish-style{ background: #b3eeff !important; } /* 容器样式 */ .container-dialog { max-width: 500px; background: #fff; border-radius: 3px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); position: absolute; top: 20%; right: 50px; } /* 表格基础样式 - 增强外边框 */ .table-main { position: relative; width: 100%; border-collapse: collapse; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; border: 2px solid #000; border-radius: 4px; overflow: hidden; } /* 表头样式 */ .table-head th { height: 30px; background: linear-gradient(135deg, #00c922 0%, #00c922 100%); color: #fff; padding: 0; text-align: center; font-weight: 300; font-size: 14px; letter-spacing: 0.5px; border: 1px solid #5a67d8; } /* 表格单元格样式 */ .table-body td { padding: 0; border: 1px solid #dcdfe6; color: #333; font-size: 14px; } th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #e0e0e0; overflow: hidden; text-overflow: ellipsis; /* 内容过长时省略 */ white-space: nowrap; } /* 关键:表格主体容器,设置高度和滚动 */ .table-body-wrapper { max-height: 300px; /* 控制滚动区域高度,可根据需求调整 */ overflow-y: auto; /* 纵向超出时显示滚动条 */ overflow-x: hidden; /* 横向隐藏,避免出现双滚动条 */ } /* 滚动条样式美化(可选,适配Chrome/Safari) */ .table-body-wrapper::-webkit-scrollbar { width: 6px; /* 滚动条宽度 */ } .table-body-wrapper::-webkit-scrollbar-thumb { background-color: #ccc; border-radius: 3px; } .table-body-wrapper::-webkit-scrollbar-track { background-color: #f9f9f9; } /* 斑马纹效果 */ .table-body tr:nth-child(even) { background-color: #f9f9f9; } /* 悬停效果 */ .table-body tr:hover { background-color: #f0f7ff; transition: background-color 0.2s ease; } /* 圆角处理 */ .table-head th:first-child { border-top-left-radius: 3px; } /* 表格基础样式:取消默认间距,保证列对齐 */ table { border-collapse: collapse; /* 合并边框 */ table-layout: fixed; /* 固定列宽,避免内容撑开列 */ } .table-head th:last-child { border-top-right-radius: 3px; }`); const tableHtml = `
课程编号 课程名称 课程编码 学习时长 课程状态 操作
`; document.body?.insertAdjacentHTML("beforeend", tableHtml); } //递归获取课程列表 function getClassList(data, result = []) { if (!data) return result; // 只有没有子节点时,才添加到结果 if (!data.nodes || !Array.isArray(data.nodes) || data.nodes.length === 0) { result.push(data); return result; } // 有子节点则递归遍历子节点 for (let childNode of data.nodes) { getClassList(childNode, result); } return result; } //获取课程列表 function loadClassListData(classListData, class_time_record_list) { if (!isListPage) { return; } //获取课程id const courseId = getUrlParameters("courseId"); //课程列表 const classItems = getClassList(classListData.data); //获取课程播放时间 const class_time_record_list_data = class_time_record_list.data?.studyRecordList || []; classItems.forEach((element) => { const temp = class_time_record_list_data.find( (item) => item.nodeId === element.id, ); if (temp) { element.play_time = temp.accumulativeTime; element.isFinish = true; } else { element.play_time = 0; element.isFinish = false; } }); GM.setValue("classid_" + courseId, classItems); //渲染列表页面 let classItemsHtml = ""; for (let i = 0; i < classItems.length; i++) { const element = classItems[i]; classItemsHtml += ` ${courseId} ${element.name} ${element.id} ${Math.floor(element.play_time / 60) .toString() .padStart( 2, "0", )}:${(element.play_time % 60).toString().padStart(2, "0")} ${element.isFinish ? "完成" : "未学习"} `; } document.querySelector(".table-body").innerHTML = classItemsHtml; } //列表页加载,该函数由Promise调用 function initListPage(classJson, class_time_record_list) { if (isListPage) { console.log("课程列表页"); loadClassList(); loadClassListData(classJson, class_time_record_list); //创建跳转按钮 createBtn(); } } //详情页网课内容加载 window.addEventListener("load", function () { if (isDetailPage) { console.log("课程详情页"); initDelatilsPage(); } }); //获取当钱url 课程节点信息 function getUrlParameters(paramKey) { const params = new URLSearchParams(window.location.search); return params.get(paramKey); } //状态显示 function createBtn() { document.querySelectorAll("#btn-td").forEach((td) => { const customBtn = document.createElement("button"); customBtn.id = "btn-study"; customBtn.className = "btn-style"; customBtn.textContent = "开始学习"; customBtn.addEventListener("click", function () { const class_id = this.parentNode.parentNode.children[2].textContent; // 事件触发后的逻辑(示例) CoursewareNodesManager.onMenuClick(class_id); }); td.appendChild(customBtn); }); } // 详情页视频自动播放 // 自动播放视频 function autoPlayMedia(video, nextInfo) { if (video) { // 确保视频静音(浏览器自动播放策略要求) video.muted = true; // 设置倍速 video.playbackRate = speed; if (video.paused) { video.play(); } } //播放结束自动播放下一节 video.addEventListener("ended", function () { CoursewareNodesManager.onMenuClick(nextInfo.id); }); video.addEventListener("pause", function () { video.muted = true; video.play(); }); } //详情页插件运行状态标签 function createPEelement() { const p = document.createElement("p"); p.style.color = "red"; p.id = "p-learn-title"; p.className = "learn-title lesson-list"; p.innerText = " 正在运行中...."; document.querySelector(".player-header").appendChild(p); } })();