// ==UserScript== // @name 学习通刷课助手-自动静音,防止鼠标移出暂停,章节结束自动跳转下一节 // @namespace http://tampermonkey.net/ // @version 2.0.2 // @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); GM_setValue("courseImg", GM_getValue("courseImg") || ''); GM_setValue("testType", GM_getValue("testType") || 'save'); GM_setValue("ifTest", GM_getValue("ifTest") || 'skip'); GM_setValue("taskInterval", GM_getValue("taskInterval") || '6000'); GM_setValue("testInterval", GM_getValue("testInterval") || '4000'); GM_setValue("ifReview", GM_getValue("ifReview") || '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 courseImg = GM_getValue("courseImg") // 当前课程封面 let chapterInfo = [] // 当前课程所有章节数据 let currentChapterId = null // 当前所在章节id let currentChapterName = null // 当前所在章节名称 let allChapterName = [] // 所有章节名称 let currentChapterTabArr = [] // 当前页面所有的分栏 let currentChapterTabName = '' // 当前页面激活的分栏名称 let videoProgressId = '' // 定时监听页面内容监听事件 // 任务间隔 let taskInterval = GM_getValue("taskInterval") // 是否复习模式 let ifReview = GM_getValue("ifReview") // 章节测试数据 let testDom = null let ifTest = GM_getValue("ifTest") let testType = GM_getValue("testType") let testTasks = [] let testBtnDocument = null let testInterval = GM_getValue("testInterval") // 获取当前页面的 URL url = '' chapterId = '' let arrayEchelon = []; // 顺序执行梯队,初始化为空数组 const dealEvent = new Event("redeal", { bubbles: false, cancelable: false }); let testArrayEchelon = []; // 顺序执行梯队,初始化为空数组 const testDealEvent = new Event("testRedeal", { bubbles: false, cancelable: false }); // AI请求接口Url const fetch_url ='https://www.sweek.top/model/sse/data?text=' // 页面模板部分 // 页面模板部分 // 页面模板部分 // 页面样式 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-y: auto; overflow-x: hidden; 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: 190px; height: 20px; display: flex; justify-content: space-between; } #my-window .content .content-set .content-set-content .control #ifReviewSelect { width: 190px; 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 #playbackRateSelect { width: 190px; 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: 190px; 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 #ifTestSelect { width: 190px; 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 #testTypeSelect { width: 190px; 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 #taskIntervalSelect { width: 190px; 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 #testIntervalSelect { width: 190px; 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: 145px; 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 = `
' + text + '
[' + _time + ']' + str + '
[' + _time + ']' + '
' + '播放进度:' + val1 + '
' + '视频长度:' + val2 + 's' + '/' +val3 + 's' + '
'; _process.innerHTML = newContent; } else if(type == 'audio') { var _process = window.top.document.querySelector('#content-process'); var _time = getCurrentDateTime() var newContent = '[' + _time + ']' + '
' + '播放进度:' + val1 + '
' + '音频长度:' + val2 + 's' + '/' +val3 + 's' + '
'; _process.innerHTML = newContent; } else if(type == 'pdf') { var _process = window.top.document.querySelector('#content-process'); var _time = getCurrentDateTime() var newContent = '[' + _time + ']' + '
' + '浏览进度:' + val1 + '
' + 'pdf高度:' + val2 + 'px' + '/' +val3 + 'px' + '
'; _process.innerHTML = newContent; } else if(type == 'test') { var _process = window.top.document.querySelector('#content-process'); var _time = getCurrentDateTime() var newContent = '[' + _time + ']' + '[' + '答题进度:' + val1 + ']' + '[' + val2 + '/' + val3 + ']' + '
'; testTasks.forEach((item,index) => { const testItem = item.status ? `[${index + 1}]${item.answer_text}
` : `[${index + 1}]${item.answer_text || '暂无答案'}
` newContent += testItem }) _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 = `[同步课程信息失败,邮箱未注册]`; _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); }); } // 同步课程数据至数据库 async function syncCourseData() { const url = 'https://www.sweek.top/api/insertOrUpdateCourse'; const email = GM_getValue("savedEmail"); const course_name = GM_getValue("courseName") || '测试'; const course_id = GM_getValue("courseId") || ''; const course_img = GM_getValue("courseImg") || ''; const process = document.querySelector('#content-process').innerHTML; const chapter_info = GM_getValue("chapterInfo"); const chapters = [...window.top.document.querySelectorAll('.posCatalog_select')]; const allChapterName = chapters.map(item => { const isActive = item.classList.contains('posCatalog_active'); const isFirstLayer = item.classList.contains('firstLayer'); const isCompleted = item.childNodes[3]?.className === 'icon_Completed prevTips'; if (isActive) { GM_setValue("currentChapterId", item.id); GM_setValue("currentChapterName", item.innerText); } return { id: item.id, title: item.innerText, active: isActive ? 1 : 0, ifTitle: isFirstLayer ? 1 : 0, status: isCompleted ? 1 : 0, }; }); const data = { email, course_id, course_name, course_img, chapter: JSON.stringify(allChapterName), current_chapter: `${GM_getValue("currentChapterId") || ''},${GM_getValue("currentChapterName") || ''}`, process, }; // 播放异常刷新页面 // if(process.includes("NaN")) { // location.reload(); // 刷新页面 // return; // } if (email && process) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error('Network response was not ok'); } const responseData = await response.json(); const _async_time = window.top.document.querySelector('#async-time'); const _time = getCurrentDateTime(); _async_time.innerHTML = `[同步时间:${_time}]`; } catch (error) { console.error('Error:', error.message); } } } // 存储章节测试题目至数据库 async function insertTest(arr) { const url = 'https://www.sweek.top/api/insertTest'; const data = { testList: arr } if (arr.length > 0) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error('Network response was not ok'); } console.log('题目同步成功!:::+ ', arr) } catch (error) { console.error('Error:', error.message); } } } // 存储章节测试题目至数据库 async function insertTestModel(arr) { const url = 'https://www.sweek.top/api/insertTestModel'; const data = { testList: arr } if (arr.length > 0) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error('Network response was not ok'); } console.log('题目同步成功!:::+ ', arr) } catch (error) { console.error('Error:', error.message); } } } // 方法执行入口 (function () { // 进入学习通弹出提示 if(location.pathname == '/base') { notify('已进入学习通首页,请进入课程,选择需要学习的课程', 5000) } // 进入课程弹出提示 if (location.pathname == '/mooc2-ans/mycourse/stu') { let courseName = window.top.document.querySelector('.classDl .colorDeep')?.getAttribute('title'); let courseImg = window.top.document.querySelector('.classDl').getElementsByTagName("img")[0]?.getAttribute('src'); if (courseName && courseImg) { GM_setValue("courseName", courseName); GM_setValue("courseImg", courseImg); notify('已进入课程:' + courseName + ',请选择需要学习的章节', 5000); } else { console.error('课程信息未能获取'); } } // 初始化显示页面弹窗 if(location.pathname == '/mycourse/studentstudy') { initPopup() // 获取公告数据 getBoard() // 邮箱操作 takeEmail() } // 获取页面章节节点数据 getChapterCodeInfo() // 获取当前页面章节小节的所有分栏 getSubChapter() // 获取页面url信息 getURLInfo() // 定时打印视频播放进度-定时监听页面内容进行下一步处理 videoProgressId = setInterval(() => { getVideoProgress() }, 10000); // 页面加载完成之后触发这个方法 window.onload = function() { // 5秒后执行initAll setTimeout(() => { initAll(); }, 5000); }; })();