您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
高教在线课程自动挂机,当前脚本支持课程视频播放完成,自动跳转下一小节,章节测试自动跳过,后台播放防止视频暂停。
// ==UserScript== // @name 【高教在线刷课助手】|| 自动执行,移除防止暂停,自动跳转下一节 // @namespace http://tampermonkey.net/ // @version 0.0.1 // @description 高教在线课程自动挂机,当前脚本支持课程视频播放完成,自动跳转下一小节,章节测试自动跳过,后台播放防止视频暂停。 // @author Sweek // @match *://*.cqooc.com/* // @license GPLv3 // @icon https://www.sweek.top/api/preview/avatar.jpg // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @require https://code.jquery.com/jquery-2.1.4.min.js // @grant unsafeWindow // @grant GM_getResourceText // ==/UserScript== /* 窗口初始化 */ /* 窗口初始化 */ /* 窗口初始化 */ function initPopup() { // 创建 CSS 样式 const popCSs = ` #my-window { position: fixed; top: 10px; left: 20px; width: 400px; height: 600px; background: rgb(255, 255, 255); color: white; font-size: 14px; border-radius: 5px; z-index: 9999; display: flex; flex-direction: column; user-select: none; font-family: 'fangsong'; border: 1px solid rgb(71, 158, 130); overflow: hidden; } #window-header { height: 50px; padding: 0 10px; background: rgb(71, 158, 130); cursor: grab; display: flex; justify-content: space-between; align-items: center; border-radius: 5px 5px 0 0; .window-header-title { font-size: 16px; font-weight: bold; font-family: 'fangsong'; } } #restore-btn { cursor: pointer; font-size: 16px; background: none; border: none; color: #444; height:20px; width:30px; text-align: center; line-height: 20px; background: #fff; border-radius: 5px; padding: 0 5px; } #restore-btn:hover { background: rgb(71, 158, 130); border: 1px solid #fff; color: #fff; } .tab { display: inline-block; padding: 5px 10px; cursor: pointer; background: #888; text-align: center; font-weight: bold; } .tab.active { background: #444; } #window-content { flex: 1; padding: 8px; overflow: auto; } #my-window.minimized { height: 50px; overflow: hidden; } #tab-container { display: block; width: 100%; background: #ddd; display: grid; grid-template-columns: repeat(2, 1fr); padding: 6px 8px; } #window-content { display: block; background: #fff; } #my-window.minimized #window-content, #my-window.minimized #tab-container { display: none; } #taskCountContent { background: #fff; border: 1px solid #ccc; border-radius: 5px; height: 100%; overflow-y: auto; color: #333; line-height: 20px; padding: 5px; } #taskLogsContent { background: #222; border: 1px solid #ccc; border-radius: 2px; height: 100%; overflow-y: auto; color: #fff; line-height: 20px; padding: 5px; } `; // 添加 CSS 样式 const style = document.createElement("style"); style.innerHTML = popCSs; document.head.appendChild(style); // 创建窗口元素 const popHtml = ` <div id="window-header"> <span class="window-header-title">Sweek高教在线刷课助手[0.0.1]</span> <div id="restore-btn">⏷</div> </div> <div id="tab-container"> <span class="tab active" data-tab="taskCount">页面任务</span> <span class="tab" data-tab="taskLogs">执行日志</span> </div> <div id="window-content"> <div id="taskCountContent">当前任务数: <span id="taskCount">0</span></div> <div id="taskLogsContent" style="display: none;"></div> </div> `; const myWindow = document.createElement("div"); myWindow.id = "my-window"; myWindow.innerHTML = popHtml; document.body.appendChild(myWindow); // 绑定最小化按钮事件 document.getElementById("restore-btn").addEventListener("click", () => { myWindow.classList.toggle("minimized"); }); // 处理拖动窗口 let isDragging = false; let offsetX = 0; let offsetY = 0; document.getElementById("window-header").addEventListener("mousedown", (e) => { isDragging = true; offsetX = e.clientX - myWindow.offsetLeft; offsetY = e.clientY - myWindow.offsetTop; }); document.addEventListener("mousemove", (e) => { if (isDragging) { let x = e.clientX - offsetX; let y = e.clientY - offsetY; // 限制窗口不能拖出屏幕 let maxX = window.innerWidth - myWindow.offsetWidth; let maxY = window.innerHeight - myWindow.offsetHeight; x = Math.max(0, Math.min(x, maxX)); y = Math.max(0, Math.min(y, maxY)); myWindow.style.left = `${x}px`; myWindow.style.top = `${y}px`; } }); document.addEventListener("mouseup", () => { isDragging = false; }); // 绑定 Tab 切换事件 document.querySelectorAll(".tab").forEach(tab => { tab.addEventListener("click", () => { document.querySelectorAll(".tab").forEach(el => el.classList.remove("active")); tab.classList.add("active"); document.getElementById("taskCountContent").style.display = "none"; document.getElementById("taskLogsContent").style.display = "none"; if (tab.dataset.tab === "taskCount") { document.getElementById("taskCountContent").style.display = "block"; } else { document.getElementById("taskLogsContent").style.display = "block"; } }); }); } function destroyPopup() { const myWindow = document.getElementById("my-window"); if (myWindow) { myWindow.remove(); // 移除窗口 } const styleTags = document.querySelectorAll("style"); styleTags.forEach(style => { if (style.innerHTML.includes("#my-window")) { style.remove(); // 移除添加的 CSS 样式 } }); document.removeEventListener("mousemove", moveHandler); document.removeEventListener("mouseup", upHandler); // console.log("窗口已销毁"); } /* 课程处理方法 */ /* 课程处理方法 */ /* 课程处理方法 */ let taskQueue = []; // 任务队列 const testDealEvent = new Event("testRedeal", { bubbles: false, cancelable: false }); // 处理视频 function handleVideo(Dom) { return new Promise((resolve) => { const playButton = Dom.querySelector(".dplayer-mobile-play"); // 获取播放按钮 const video = Dom.querySelector("video"); // 获取视频元素 if (!video) { addLog("未找到视频元素"); return resolve(); } let playAttempted = false; // 是否尝试过播放 // 监听视频播放时的进度 video.addEventListener("timeupdate", () => { const currentTime = video.currentTime; // 当前播放时间 const duration = video.duration; // 视频总时长 const playbackRate = video.playbackRate; // 播放倍速 // 生成显示进度的内容 const val1 = currentTime; // 当前播放时间 const val2 = duration; // 视频总时长 setProgress(val1, val2, playbackRate, 'video'); // 设置显示播放进度 }); function preventPause() { video.onpause = () => { // addLog("视频暂停,1秒后重新播放"); setTimeout(() => { if (video.paused) { video.play().catch((error) => { addLog(`重新播放失败: ${error}`); }); } }, 500); // 0.5秒后重新播放 }; } function checkPlayback() { setTimeout(() => { if (!video.paused) { addLog("视频已成功播放"); return; } addLog("播放按钮点击无效,尝试直接调用 video.play()"); video.muted = true; // 静音播放,防止浏览器限制 video.play().catch((error) => { addLog(`直接播放失败: ${error}`); }); }, 500); // 延迟检查,确保点击后有时间触发播放 } if (playButton) { addLog("找到播放按钮,尝试点击播放"); playButton.click(); playAttempted = true; checkPlayback(); // 延迟检查播放情况 } if (!playAttempted) { addLog("未找到播放按钮,尝试直接调用 video.play()"); video.muted = true; video.play().catch((error) => { addLog(`直接播放失败: ${error}`); }); } // 阻止视频暂停 preventPause(); // 监听视频播放完成事件 video.onended = () => { addLog("视频播放完成"); // 清理事件监听和状态 video.onended = null; // 移除事件监听 video.onpause = null; // 移除暂停监听 resolve(); // 完成当前视频任务 }; }); } // 处理PDF function handlePDF(Dom) { return new Promise((resolve) => { const interval = setInterval(() => { const headerBox = document.querySelector(".header-box"); if (headerBox) { const text = headerBox.innerText; if (!text.includes("完成倒计时")) { clearInterval(interval); // 停止轮询 resolve(); // 任务完成,调用 resolve } } }, 1000); // 每秒检查一次 }); } // 获取课程内容类型 function getCourseContentType(Dom) { // 获取DOM元素的HTML内容 const htmlContent = Dom.innerHTML || Dom.outerHTML; // 判断Dom中是否包含<video>标签 if (htmlContent.includes('video')) { return 'video'; } // 判断Dom中是否包含<iframe>标签 if (htmlContent.includes('iframe')) { return 'PDF'; } // 如果都不包含,返回null或者其他默认值 return null; } // 处理课程接口 function handleCourseContent(Dom) { return new Promise((resolve) => { const courseContentType = getCourseContentType(Dom) switch (courseContentType) { case 'video': addLog("开始处理视频任务..."); handleVideo(Dom).then(() => { addLog("视频任务处理完成"); resolve(); }); break; case 'PDF': addLog("开始处理PDF/PPT内容..."); handlePDF(Dom).then(() => { addLog("PDF/PPT任务处理完成"); resolve(); }); break; default: addLog("未知课程类型,跳过该任务"); resolve(); } }); } // 监听任务完成状态的函数 function waitForContentToLoad() { return new Promise((resolve) => { let contentContainer = document.querySelector(".video-box"); if (!contentContainer) { addLog("未找到课程内容模块,直接继续下一个任务"); resolve(); return } let observer = new MutationObserver((mutations, obs) => { if (contentContainer.innerText.trim() !== "") { obs.disconnect(); // 停止监听 // 调用 handleCourseContent,并等待其完成 handleCourseContent(contentContainer).then(() => { resolve(); }); } }); observer.observe(contentContainer, { childList: true, subtree: true }); // 设置超时防止卡死 // setTimeout(() => { // addLog("课程内容加载超时,继续下一个任务"); // observer.disconnect(); // resolve(); // }, 5000); }); } // 任务执行器 async function processNextTask() { if (taskQueue.length === 0) { addLog("所有任务已执行完毕"); return; } let element = taskQueue.shift(); // 取出任务 // console.log(`点击元素:`, element); element.click(); // 触发点击 await waitForContentToLoad(); // 等待课程内容加载处理完成 // 触发下一个任务 setTimeout(() => { document.dispatchEvent(testDealEvent); }, 2000); } // 监听事件,触发下一个任务 document.addEventListener("testRedeal", processNextTask); // 初始化任务执行 function startTaskFlow() { let allTasks = Array.from(document.querySelectorAll(".third-level-box")) .filter(el => !/作业|测验/.test(el.innerText)); // 筛选掉包含"作业"或"测验"的任务 let activeIndex = allTasks.findIndex(el => el.classList.contains("active")); addLog(`本课程共 ${allTasks.length} 个有效任务`); // 仅计算有效任务 if (activeIndex === -1) { console.log("未找到 active 状态的元素,从头开始执行任务流"); taskQueue = allTasks; } else { console.log(`找到 active 元素,从索引 ${activeIndex} 开始执行任务流`); taskQueue = [...allTasks.slice(activeIndex)]; } addLog(`共 ${taskQueue.length} 个任务即将执行`); if (taskQueue.length > 0) { document.dispatchEvent(testDealEvent); } } /* 工具方法 */ /* 工具方法 */ /* 工具方法 */ // 格式化时间函数 function formatTime(seconds) { const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.floor(seconds % 60); return `${minutes}:${remainingSeconds < 10 ? "0" + remainingSeconds : remainingSeconds}`; } // 添加日志方法 function addLog(message) { let logEl = document.getElementById("taskLogsContent"); let logItem = document.createElement("div"); logItem.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; logEl.appendChild(logItem); }; // 设置播放进度 function setProgress(val1, val2, val3, type) { let newContent = ""; switch (type) { case "video": newContent += `<p>播放进度:${(val1/val2 * 100).toFixed(2)}%</p><hr>`; newContent += `<p>播放倍速:${val3}</p><hr>`; newContent += `<p>视频长度:${formatTime(val1)}/${formatTime(val2)}</p>`; break; case "PDF": newContent += `<p>滚动进度:${(val1/val2 * 100).toFixed(2)}%</p><hr>`; newContent += `<p>滚动倍速:${val3}</p><hr>`; newContent += `<p>PDF高度:${formatTime(val1)}/${formatTime(val2)}</p>`; break; default: break; } const taskCountContent = document.getElementById("taskCountContent"); // 获取显示进度的元素 // 更新 taskCountContent 中的内容 taskCountContent.innerHTML = newContent; } // 页面通知提示 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;">' + 'Sweek高教在线刷课助手提示' + '</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); } /* 处理页面路由跳转 */ /* 处理页面路由跳转 */ /* 处理页面路由跳转 */ let lastPath = location.pathname; function handlePathChange() { if (lastPath !== location.pathname) { lastPath = location.pathname; handleInPageNotify() } } // 进入页面提示逻辑 function handleInPageNotify() { // console.log('location.pathname:::+ ', location.pathname) switch (location.pathname) { case '/index/home': notify('高教在线刷课助手已成功启动,已进入高教在线首页,请鼠标悬浮右上角头像点击在学课程', 2500) destroyPopup() break; case '/account/course': notify('已进入在学课程页面', 2500) destroyPopup() break; case '/course/detail/courseStudy': notify('已进入课程学习页面', 2500) // 初始化窗口 initPopup() setTimeout(() => { // 执行任务流 startTaskFlow(); }, 2500); break; default: break; } } // 劫持 history.pushState 和 replaceState const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function (...args) { originalPushState.apply(this, args); handlePathChange(); }; history.replaceState = function (...args) { originalReplaceState.apply(this, args); handlePathChange(); }; window.addEventListener("popstate", handlePathChange); // 方法执行入口 // 方法执行入口 // 方法执行入口 (async function () { // 引入Bootstrap Icons let link = document.createElement("link"); link.rel = "stylesheet"; link.href = "https://cdn.bootcdn.net/ajax/libs/bootstrap-icons/1.10.0/font/bootstrap-icons.css"; document.head.appendChild(link); handleInPageNotify() })();