// ==UserScript== // @name 杏林学堂刷课 // @namespace http://tampermonkey.net/ // @version 1.9 // @description 使用ScriptCat框架获取任务列表及课程列表,并自动跳转到课程页面,自动完成视频播放 // @author You // @match https://mbeta.xlxt.net/* // @grant GM_xmlhttpRequest // ==/UserScript== // 添加以下声明以消除IDE报错 /* global GM_xmlhttpRequest */ (function() { 'use strict'; // 目标URL(保持原域名不变) const targetUrl = 'https://wwwbeta.xlxt.net//Task/GetMemberTask?pageindex=1&pagesize=10&StudyStatus=0'; // 匹配的域名 const targetDomain = 'mbeta.xlxt.net'; // 登录页面URL const loginUrl = 'https://mbeta.xlxt.net/#/login?from=home'; // 开始刷课按钮引用 let startBtn; // 处理当前课程按钮引用 let processCurrentCourseBtn; // 跟踪正在处理的课程数量 let processingCourses = 0; // 存储课程队列 let courseQueue = []; // 存储任务队列 let taskQueue = []; // 当前重试次数 let retryCount = 0; // 是否正在刷课 let isBrushing = false; // 当前正在处理的任务索引 let currentTaskIndex = -1; // 信息显示框引用 let infoBox; // 悬浮窗引用 let floatWindow; // 设置窗口引用 let settingsWindow; // 从localStorage加载设置,默认为空则使用默认值 const settings = loadSettings(); // 最大重试次数(从设置中读取) const MAX_RETRIES = settings.maxRetries || 5; // 播放按钮查找延迟(毫秒) const PLAY_BUTTON_DELAY = settings.playButtonDelay || 1000; // 视频元素检测延迟(毫秒) const VIDEO_DETECTION_DELAY = settings.videoDetectionDelay || 1000; // 视频进度调整后等待时间(毫秒) const VIDEO_ADJUST_WAIT = settings.videoAdjustWait || 3000; // 播放按钮最大重试次数 const MAX_PLAY_BUTTON_RETRIES = settings.maxPlayButtonRetries || 5; // 创建信息显示框 infoBox = createInfoBox(); showMessage('欢迎使用自动刷课脚本!', 'info'); showMessage('点击"开始刷课"按钮开始执行任务', 'info'); showMessage('如果需要登录,请点击"前往登录"按钮', 'info'); // 创建悬浮窗 floatWindow = createFloatWindow(); // 创建信息显示框函数 function createInfoBox() { // 创建信息框元素 const box = document.createElement('div'); box.id = 'auto-course-info-box'; box.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 300px; padding: 15px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; font-family: 'Microsoft YaHei', sans-serif; `; // 创建标题栏(包含标题和设置按钮) const titleBar = document.createElement('div'); titleBar.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;'; box.appendChild(titleBar); // 创建标题 const title = document.createElement('h3'); title.textContent = '自动刷课脚本'; title.style.cssText = ` margin: 0; color: #333; border-bottom: 1px solid #eee; padding-bottom: 8px; flex-grow: 1; `; titleBar.appendChild(title); // 创建设置按钮 const settingsBtn = document.createElement('button'); settingsBtn.textContent = '设置'; settingsBtn.style.cssText = ` background-color: #2196F3; color: white; border: none; border-radius: 4px; padding: 4px 8px; cursor: pointer; font-size: 12px; `; settingsBtn.addEventListener('click', openSettingsWindow); titleBar.appendChild(settingsBtn); // 创建内容区域 const content = document.createElement('div'); content.id = 'auto-course-info-content'; content.style.cssText = ` max-height: 200px; overflow-y: auto; margin: 10px 0; font-size: 14px; `; box.appendChild(content); // 创建按钮区域 const buttonArea = document.createElement('div'); buttonArea.style.cssText = ` display: flex; flex-wrap: wrap; justify-content: space-between; margin-top: 15px; gap: 5px; `; box.appendChild(buttonArea); // 创建"开始刷课"按钮 startBtn = document.createElement('button'); startBtn.textContent = '开始刷课'; startBtn.style.cssText = ` background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 100%; margin-bottom: 5px; `; startBtn.addEventListener('click', toggleBrushing); buttonArea.appendChild(startBtn); // 创建"处理当前课程"按钮 processCurrentCourseBtn = document.createElement('button'); processCurrentCourseBtn.textContent = '处理当前课程'; processCurrentCourseBtn.style.cssText = ` background-color: #ff9800; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 100%; margin-bottom: 5px; `; processCurrentCourseBtn.addEventListener('click', processCurrentCourse); buttonArea.appendChild(processCurrentCourseBtn); // 创建"前往登录"按钮 const loginBtn = document.createElement('button'); loginBtn.textContent = '前往登录'; loginBtn.style.cssText = ` background-color: #2196F3; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 calc(50% - 5px); `; loginBtn.addEventListener('click', () => { showMessage('正在跳转到登录页面...', 'info'); window.location.href = loginUrl; }); buttonArea.appendChild(loginBtn); // 创建关闭按钮 const closeBtn = document.createElement('button'); closeBtn.textContent = '关闭'; closeBtn.style.cssText = ` background-color: #f44336; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; flex: 1 1 calc(50% - 5px); `; closeBtn.addEventListener('click', () => { box.style.display = 'none'; floatWindow.style.display = 'block'; }); buttonArea.appendChild(closeBtn); // 添加到页面 document.body.appendChild(box); return box; } // 创建悬浮窗函数 function createFloatWindow() { const floatWin = document.createElement('div'); floatWin.id = 'auto-course-float-window'; floatWin.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 50px; height: 50px; background-color: #4CAF50; color: white; border-radius: 50%; display: flex; justify-content: center; align-items: center; box-shadow: 0 4px 8px rgba(0,0,0,0.2); cursor: pointer; z-index: 9998; display: none; `; floatWin.textContent = '刷课'; floatWin.addEventListener('click', () => { floatWin.style.display = 'none'; infoBox.style.display = 'block'; }); document.body.appendChild(floatWin); return floatWin; } // 打开设置窗口 function openSettingsWindow() { // 如果设置窗口已存在,直接显示 if (settingsWindow) { settingsWindow.style.display = 'block'; return; } // 创建设置窗口 settingsWindow = document.createElement('div'); settingsWindow.id = 'auto-course-settings-window'; settingsWindow.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 350px; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.2); z-index: 10000; font-family: 'Microsoft YaHei', sans-serif; `; // 创建标题 const title = document.createElement('h3'); title.textContent = '脚本设置'; title.style.cssText = 'margin-top: 0; color: #333; border-bottom: 1px solid #eee; padding-bottom: 10px; margin-bottom: 15px;'; settingsWindow.appendChild(title); // 创建表单 const form = document.createElement('form'); settingsWindow.appendChild(form); // 添加播放按钮查找延迟设置 addSettingField(form, 'playButtonDelay', '播放按钮查找延迟(毫秒)', PLAY_BUTTON_DELAY); // 添加视频元素检测延迟设置 addSettingField(form, 'videoDetectionDelay', '视频元素检测延迟(毫秒)', VIDEO_DETECTION_DELAY); // 添加视频进度调整后等待时间设置 addSettingField(form, 'videoAdjustWait', '视频调整后等待时间(毫秒)', VIDEO_ADJUST_WAIT); // 添加视频元素最大重试次数设置 addSettingField(form, 'maxRetries', '视频元素最大重试次数', MAX_RETRIES); // 添加播放按钮最大重试次数设置 addSettingField(form, 'maxPlayButtonRetries', '播放按钮最大重试次数', MAX_PLAY_BUTTON_RETRIES); // 创建按钮区域 const buttonArea = document.createElement('div'); buttonArea.style.cssText = 'margin-top: 20px; display: flex; justify-content: flex-end;'; settingsWindow.appendChild(buttonArea); // 创建"保存并关闭"按钮 const saveBtn = document.createElement('button'); saveBtn.type = 'button'; saveBtn.textContent = '保存并关闭'; saveBtn.style.cssText = ` background-color: #4CAF50; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; `; saveBtn.addEventListener('click', saveSettings); buttonArea.appendChild(saveBtn); // 创建遮罩层 const overlay = document.createElement('div'); overlay.id = 'auto-course-settings-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 9999; `; overlay.addEventListener('click', () => { closeSettingsWindow(); }); document.body.appendChild(overlay); document.body.appendChild(settingsWindow); } // 添加设置字段 function addSettingField(form, id, labelText, value) { const fieldContainer = document.createElement('div'); fieldContainer.style.cssText = 'margin-bottom: 15px;'; const label = document.createElement('label'); label.textContent = labelText; label.htmlFor = id; label.style.cssText = 'display: block; margin-bottom: 5px; font-weight: bold;'; const input = document.createElement('input'); input.type = 'number'; input.id = id; input.name = id; input.value = value; input.min = '0'; input.style.cssText = 'width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;'; fieldContainer.appendChild(label); fieldContainer.appendChild(input); form.appendChild(fieldContainer); } // 保存设置 function saveSettings() { const newSettings = { playButtonDelay: parseInt(document.getElementById('playButtonDelay').value, 10) || 1000, videoDetectionDelay: parseInt(document.getElementById('videoDetectionDelay').value, 10) || 1000, videoAdjustWait: parseInt(document.getElementById('videoAdjustWait').value, 10) || 3000, maxRetries: parseInt(document.getElementById('maxRetries').value, 10) || 5, maxPlayButtonRetries: parseInt(document.getElementById('maxPlayButtonRetries').value, 10) || 5 }; // 保存到localStorage localStorage.setItem('autoCourseSettings', JSON.stringify(newSettings)); // 更新当前设置 Object.assign(settings, newSettings); showMessage('设置已保存', 'success'); closeSettingsWindow(); } // 关闭设置窗口 function closeSettingsWindow() { if (settingsWindow) { settingsWindow.style.display = 'none'; } const overlay = document.getElementById('auto-course-settings-overlay'); if (overlay) { overlay.style.display = 'none'; } } // 加载设置 function loadSettings() { try { const savedSettings = localStorage.getItem('autoCourseSettings'); if (savedSettings) { return JSON.parse(savedSettings); } } catch (e) { console.error('加载设置失败:', e); } return {}; } // 显示消息函数 - 支持不同类型的消息(带颜色) function showMessage(message, type = 'log') { const content = document.getElementById('auto-course-info-content'); const messageElement = document.createElement('p'); messageElement.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; messageElement.style.margin = '5px 0'; messageElement.style.padding = '3px 5px'; messageElement.style.borderRadius = '3px'; // 根据类型设置不同颜色 switch(type) { case 'error': messageElement.style.color = '#f44336'; // 红色 messageElement.style.backgroundColor = '#ffebee'; console.error(`[${new Date().toLocaleTimeString()}] ${message}`); break; case 'warning': messageElement.style.color = '#ff9800'; // 橙色 messageElement.style.backgroundColor = '#fff3e0'; console.warn(`[${new Date().toLocaleTimeString()}] ${message}`); break; case 'success': messageElement.style.color = '#4caf50'; // 绿色 messageElement.style.backgroundColor = '#e8f5e9'; console.log(`[${new Date().toLocaleTimeString()}] ${message}`); break; case 'info': messageElement.style.color = '#2196f3'; // 蓝色 messageElement.style.backgroundColor = '#e3f2fd'; console.log(`[${new Date().toLocaleTimeString()}] ${message}`); break; default: messageElement.style.color = '#666'; // 默认颜色 console.log(`[${new Date().toLocaleTimeString()}] ${message}`); } content.appendChild(messageElement); content.scrollTop = content.scrollHeight; // 保持最多显示10条消息 if (content.children.length > 10) { content.removeChild(content.firstChild); } } // 切换刷课状态 function toggleBrushing() { if (isBrushing) { // 停止刷课 stopBrushing(); } else { // 开始刷课 startBrushing(); } } // 开始刷课 function startBrushing() { isBrushing = true; startBtn.textContent = '停止刷课'; startBtn.style.backgroundColor = '#f44336'; showMessage('开始刷课,正在获取任务列表...', 'info'); sendRequestWithCookie(); } // 停止刷课 function stopBrushing() { isBrushing = false; startBtn.textContent = '开始刷课'; startBtn.style.backgroundColor = '#4CAF50'; // 清空队列 courseQueue = []; taskQueue = []; processingCourses = 0; currentTaskIndex = -1; showMessage('已停止刷课', 'warning'); } // 直接处理当前课程 function processCurrentCourse() { if (window.location.href.includes('/course-info')) { showMessage('正在处理当前课程...', 'info'); // 临时保存按钮状态 const originalText = processCurrentCourseBtn.textContent; const originalColor = processCurrentCourseBtn.style.backgroundColor; // 更改按钮状态 processCurrentCourseBtn.textContent = '处理中...'; processCurrentCourseBtn.style.backgroundColor = '#cccccc'; processCurrentCourseBtn.disabled = true; // 执行课程处理逻辑 const dummyCourse = { jumpUrl: window.location.href }; handleVideoPlaybackForCurrentCourse(dummyCourse, () => { // 恢复按钮状态 processCurrentCourseBtn.textContent = originalText; processCurrentCourseBtn.style.backgroundColor = originalColor; processCurrentCourseBtn.disabled = false; showMessage('当前课程处理完毕', 'success'); }); } else { showMessage('当前不在课程页面', 'error'); } } // 处理当前课程的视频播放 function handleVideoPlaybackForCurrentCourse(course, callback) { showMessage('正在尝试自动点击播放按钮...', 'info'); clickPlayButtonForCurrentCourse(course, callback); } // 为当前课程尝试点击播放按钮 function clickPlayButtonForCurrentCourse(course, callback) { let playButtonRetryCount = 0; const MAX_PLAY_BUTTON_RETRIES = settings.maxPlayButtonRetries || 5; setTimeout(() => { findAndClickPlayButton(); }, settings.playButtonDelay || 1000); function findAndClickPlayButton() { const playButton = document.querySelector('.prism-big-play-btn') || document.querySelector('#J_prismPlayer_component_F46C34E7-1A34-4EFB-AA07-68B666B6A1DD') || document.querySelector('.play-button') || document.querySelector('button[aria-label*="播放"]'); if (playButton) { try { playButton.click(); showMessage('已成功点击播放按钮', 'success'); setTimeout(() => { checkVideoElementForCurrentCourse(course, callback); }, settings.videoDetectionDelay || 1000); } catch (e) { showMessage('点击播放按钮时出错: ' + e.message, 'error'); checkVideoElementForCurrentCourse(course, callback); } } else { playButtonRetryCount++; if (playButtonRetryCount < MAX_PLAY_BUTTON_RETRIES) { showMessage(`未找到播放按钮,${playButtonRetryCount}秒后重试(${playButtonRetryCount}/${MAX_PLAY_BUTTON_RETRIES})`, 'warning'); setTimeout(() => { findAndClickPlayButton(); }, 1000); } else { showMessage('超过最大重试次数,无法找到播放按钮', 'error'); checkVideoElementForCurrentCourse(course, callback); } } } } // 为当前课程检查视频元素 function checkVideoElementForCurrentCourse(course, callback) { let localRetryCount = 0; const MAX_LOCAL_RETRIES = settings.maxRetries || 5; function check() { localRetryCount++; let v = document.querySelector('video'); if (!v) v = document.querySelector('video[src]'); if (!v) v = document.querySelector('.video-player video'); if (!v) v = document.querySelector('#video-player video'); if (v && typeof v.currentTime !== 'undefined' && typeof v.duration !== 'undefined' && !isNaN(v.duration) && v.duration > 0) { try { v.currentTime = v.duration; showMessage('视频进度已调整至最后', 'success'); setTimeout(callback, settings.videoAdjustWait || 3000); } catch (error) { showMessage('调整视频进度时出错', 'error'); if (localRetryCount < MAX_LOCAL_RETRIES) { setTimeout(check, settings.videoDetectionDelay || 1000); } else { showMessage('超过最大重试次数,无法调整视频进度', 'error'); callback(); } } } else { if (localRetryCount < MAX_LOCAL_RETRIES) { showMessage(`未检测到有效的视频元素(${localRetryCount}/${MAX_LOCAL_RETRIES})`, 'warning'); setTimeout(check, settings.videoDetectionDelay || 1000); } else { showMessage('超过最大重试次数,无法检测到视频元素', 'error'); callback(); } } } check(); } // 重置开始按钮状态 function resetStartButton() { if (startBtn && processingCourses === 0 && taskQueue.length === 0) { startBtn.disabled = false; startBtn.style.backgroundColor = '#4CAF50'; startBtn.textContent = '开始刷课'; isBrushing = false; showMessage('所有任务和课程处理完毕,您可以再次点击"开始刷课"按钮', 'success'); } else if (startBtn && processingCourses === 0 && taskQueue.length > 0) { // 还有任务未处理,自动处理下一个任务 currentTaskIndex++; if (currentTaskIndex < taskQueue.length) { const nextTask = taskQueue[currentTaskIndex]; showMessage(`开始处理下一个任务: ${nextTask.Name}`, 'info'); getCourseList(nextTask.TaskID, document.cookie); } } } // 处理视频播放和跳转 function handleVideoPlayback() { if (!isBrushing) { showMessage('刷课已停止,无法继续处理课程', 'warning'); return; } if (courseQueue.length === 0) { showMessage('当前任务的课程队列为空', 'info'); processingCourses = 0; resetStartButton(); return; } const nextCourse = courseQueue[0]; showMessage('页面加载完毕,正在尝试自动点击播放按钮...', 'info'); // 尝试点击播放按钮 clickPlayButton(nextCourse); } // 尝试点击播放按钮 function clickPlayButton(nextCourse) { if (!isBrushing) return; // 添加重试计数和最大重试次数 let playButtonRetryCount = 0; const MAX_PLAY_BUTTON_RETRIES = settings.maxPlayButtonRetries || 5; // 延迟后开始查找 setTimeout(() => { findAndClickPlayButton(); }, settings.playButtonDelay || 1000); function findAndClickPlayButton() { if (!isBrushing) return; // 查找播放按钮 const playButton = document.querySelector('.prism-big-play-btn') || document.querySelector('#J_prismPlayer_component_F46C34E7-1A34-4EFB-AA07-68B666B6A1DD') || document.querySelector('.play-button') || document.querySelector('button[aria-label*="播放"]'); if (playButton) { try { playButton.click(); showMessage('已成功点击播放按钮', 'success'); // 点击成功后,等待设置的延迟时间再检测视频元素 setTimeout(() => { checkVideoElement(nextCourse); }, settings.videoDetectionDelay || 1000); } catch (e) { showMessage('点击播放按钮时出错: ' + e.message, 'error'); // 点击失败,立即检测视频元素 checkVideoElement(nextCourse); } } else { playButtonRetryCount++; if (playButtonRetryCount < MAX_PLAY_BUTTON_RETRIES) { showMessage(`未找到播放按钮,${playButtonRetryCount}秒后重试(${playButtonRetryCount}/${MAX_PLAY_BUTTON_RETRIES})`, 'warning'); // 1秒后重试 setTimeout(() => { findAndClickPlayButton(); }, 1000); } else { showMessage('超过最大重试次数,无法找到播放按钮', 'error'); // 直接尝试处理视频元素 checkVideoElement(nextCourse); } } } } // 检查视频元素并处理 - 改进版包含重试机制 function checkVideoElement(nextCourse) { if (!isBrushing) return; // 增加重试计数 retryCount++; const MAX_LOCAL_RETRIES = settings.maxRetries || 5; // 尝试多种方式查找视频元素 let v = document.querySelector('video'); if (!v) v = document.querySelector('video[src]'); if (!v) v = document.querySelector('.video-player video'); if (!v) v = document.querySelector('#video-player video'); console.log(`第 ${retryCount} 次尝试检测视频元素:`, v); if (v && typeof v.currentTime !== 'undefined' && typeof v.duration !== 'undefined' && !isNaN(v.duration) && v.duration > 0) { // 视频元素存在且属性可读 showMessage('检测到视频元素,正在调整进度...', 'info'); console.log('视频元素存在,当前时间:', v.currentTime, '持续时间:', v.duration); // 重置重试计数 retryCount = 0; try { // 将视频进度拖至最后 v.currentTime = v.duration; showMessage('视频进度已调整至最后', 'success'); console.log('视频进度已调整至最后'); // 从队列中移除当前课程 courseQueue.shift(); processingCourses--; console.log(`剩余处理中的课程数量: ${processingCourses}`); showMessage(`当前课程处理完毕,还剩 ${courseQueue.length} 个课程待处理`, 'info'); // 等待设置的时间后跳转到下一个课程 setTimeout(() => { if (!isBrushing) return; if (courseQueue.length > 0) { const nextJumpUrl = courseQueue[0].jumpUrl; showMessage(`正在跳转到下一个课程页面...`, 'info'); console.log(`正在跳转到下一个课程页面: ${nextJumpUrl}`); window.location.href = nextJumpUrl; } else { showMessage('当前任务的所有课程处理完毕', 'success'); processingCourses = 0; resetStartButton(); } }, settings.videoAdjustWait || 3000); } catch (error) { showMessage('调整视频进度时出错', 'error'); console.error('调整视频进度时出错:', error); // 发生错误时继续尝试 if (retryCount < MAX_LOCAL_RETRIES) { setTimeout(() => { checkVideoElement(nextCourse); }, settings.videoDetectionDelay || 1000); } else { showMessage('超过最大重试次数,无法调整视频进度', 'error'); // 从队列中移除当前课程,继续处理下一个 courseQueue.shift(); processingCourses--; showMessage(`当前课程处理完毕,还剩 ${courseQueue.length} 个课程待处理`, 'info'); setTimeout(() => { if (!isBrushing) return; if (courseQueue.length > 0) { const nextJumpUrl = courseQueue[0].jumpUrl; window.location.href = nextJumpUrl; } else { showMessage('当前任务的所有课程处理完毕', 'success'); processingCourses = 0; resetStartButton(); } }, settings.videoDetectionDelay || 1000); } } } else { // 视频元素不存在或属性不可读 if (retryCount < MAX_LOCAL_RETRIES) { showMessage(`未检测到有效的视频元素(${retryCount}/${MAX_LOCAL_RETRIES})`, 'warning'); console.log(`未检测到有效的视频元素,第 ${retryCount} 次重试`); // 等待设置的延迟时间后再次检查 setTimeout(() => { checkVideoElement(nextCourse); }, settings.videoDetectionDelay || 1000); } else { showMessage('超过最大重试次数,无法检测到视频元素,尝试重新点击播放按钮...', 'error'); console.log('超过最大重试次数,无法检测到视频元素,尝试重新点击播放按钮'); // 重置重试计数 retryCount = 0; // 尝试重新点击播放按钮 clickPlayButton(nextCourse); } } } // 携带cookie发送请求 function sendRequestWithCookie() { // 直接使用document.cookie const cookieString = document.cookie; console.log('使用document.cookie:', cookieString); showMessage('已获取Cookie,准备发送请求...', 'info'); GM_xmlhttpRequest({ method: 'GET', url: targetUrl, headers: { 'Content-Type': 'application/json', 'Cookie': cookieString }, onload: function(response) { try { // 解析返回的JSON数据 const taskData = JSON.parse(response.responseText); console.log('任务列表获取成功:', taskData); showMessage('任务列表获取成功', 'success'); // 检查是否有401错误(未授权) if (taskData.Code === 401) { console.error('未授权访问,请先登录:', taskData.Msg); showMessage('未授权访问,请先登录', 'error'); // 重置按钮状态 stopBrushing(); window.location.href = loginUrl; return; } // 处理任务数据 processTasks(taskData, cookieString); } catch (error) { console.error('解析任务数据失败:', error); showMessage('解析任务数据失败', 'error'); // 发生错误时重置按钮 stopBrushing(); } }, onerror: function(error) { console.error('请求任务列表失败:', error); showMessage('请求任务列表失败', 'error'); // 发生错误时重置按钮 stopBrushing(); } }); } // 处理任务数据的函数 function processTasks(taskData, cookieString) { if (!isBrushing) return; // 这里实现任务数据的处理逻辑 if (taskData.Data && taskData.Data.length > 0) { console.log(`共获取到 ${taskData.Data.length} 个任务`); showMessage(`共获取到 ${taskData.Data.length} 个任务`, 'info'); // 存储任务队列 taskQueue = taskData.Data; currentTaskIndex = 0; if (taskQueue.length > 0) { const firstTask = taskQueue[currentTaskIndex]; showMessage(`开始处理第一个任务: ${firstTask.Name}`, 'info'); getCourseList(firstTask.TaskID, cookieString); } } else { console.log('没有获取到任务数据'); showMessage('没有获取到任务数据', 'warning'); // 没有任务数据时重置按钮 stopBrushing(); } } // 获取任务的课程列表 function getCourseList(taskId, cookieString) { if (!isBrushing) return; const courseUrl = `https://wwwbeta.xlxt.net//Task/GetMemberCourseProgress?TaskID=${taskId}&pageindex=1&pagesize=30`; console.log(`请求课程列表的URL: ${courseUrl}`); showMessage(`正在获取任务ID ${taskId} 的课程列表...`, 'info'); GM_xmlhttpRequest({ method: 'GET', url: courseUrl, headers: { 'Content-Type': 'application/json', 'Cookie': cookieString }, onload: function(response) { try { // 解析返回的JSON数据 const courseData = JSON.parse(response.responseText); console.log(`任务ID ${taskId} 的课程列表获取成功:`, courseData); showMessage(`任务ID ${taskId} 的课程列表获取成功`, 'success'); // 检查是否有401错误(未授权) if (courseData.Code === 401) { console.error('未授权访问,请先登录:', courseData.Msg); showMessage('未授权访问,请先登录', 'error'); // 重置按钮状态 stopBrushing(); window.location.href = loginUrl; return; } // 处理课程数据 processCourseData(taskId, courseData); } catch (error) { console.error(`解析任务ID ${taskId} 的课程数据失败:`, error); showMessage(`解析任务ID ${taskId} 的课程数据失败`, 'error'); // 发生错误时继续处理下一个任务 currentTaskIndex++; if (currentTaskIndex < taskQueue.length) { const nextTask = taskQueue[currentTaskIndex]; showMessage(`跳过当前任务,开始处理下一个任务: ${nextTask.Name}`, 'warning'); getCourseList(nextTask.TaskID, cookieString); } else { showMessage('所有任务处理完毕', 'success'); stopBrushing(); } } }, onerror: function(error) { console.error(`请求任务ID ${taskId} 的课程列表失败:`, error); showMessage(`请求任务ID ${taskId} 的课程列表失败`, 'error'); // 发生错误时继续处理下一个任务 currentTaskIndex++; if (currentTaskIndex < taskQueue.length) { const nextTask = taskQueue[currentTaskIndex]; showMessage(`跳过当前任务,开始处理下一个任务: ${nextTask.Name}`, 'warning'); getCourseList(nextTask.TaskID, cookieString); } else { showMessage('所有任务处理完毕', 'success'); stopBrushing(); } } }); } // 处理课程数据的函数 function processCourseData(taskId, courseData) { if (!isBrushing) return; // 这里实现课程数据的处理逻辑 if (courseData.Data && Array.isArray(courseData.Data) && courseData.Data.length > 0) { console.log(`任务ID ${taskId} 共获取到 ${courseData.Data.length} 个课程`); showMessage(`任务ID ${taskId} 共获取到 ${courseData.Data.length} 个课程`, 'info'); // 设置正在处理的课程数量 processingCourses = courseData.Data.length; console.log(`当前正在处理的课程数量: ${processingCourses}`); // 清空课程队列 courseQueue = []; // 遍历课程列表,构建课程队列 courseData.Data.forEach((course, index) => { console.log(`课程 ${index + 1}:`, course); showMessage(`发现课程 ${index + 1}`, 'info'); // 提取CourseID(尝试多种可能的字段名) const courseId = course.CourseID || course.courseId || course.id; if (courseId) { console.log(`课程 ${index + 1} 的CourseID: ${courseId}`); showMessage(`课程 ${index + 1} 的CourseID: ${courseId}`, 'info'); // 构造跳转URL const jumpUrl = `https://mbeta.xlxt.net/#/course-info?courseID=${courseId}&TaskID=${taskId}`; console.log(`课程 ${index + 1} 的跳转URL: ${jumpUrl}`); // 添加到课程队列 courseQueue.push({ index: index, courseId: courseId, jumpUrl: jumpUrl }); } else { console.log(`课程 ${index + 1} 未找到CourseID`); showMessage(`课程 ${index + 1} 未找到CourseID`, 'warning'); processingCourses--; } }); // 如果课程队列不为空,跳转到第一个课程 if (courseQueue.length > 0) { const firstCourse = courseQueue[0]; showMessage(`准备跳转到第一个课程页面...`, 'info'); setTimeout(() => { if (isBrushing) { window.location.href = firstCourse.jumpUrl; } }, 1000); } else { showMessage('当前任务没有有效的课程可处理', 'warning'); // 继续处理下一个任务 currentTaskIndex++; if (currentTaskIndex < taskQueue.length) { const nextTask = taskQueue[currentTaskIndex]; showMessage(`开始处理下一个任务: ${nextTask.Name}`, 'info'); getCourseList(nextTask.TaskID, document.cookie); } else { showMessage('所有任务处理完毕', 'success'); stopBrushing(); } } } else { console.log(`任务ID ${taskId} 没有获取到课程数据`); showMessage(`任务ID ${taskId} 没有获取到课程数据`, 'warning'); // 继续处理下一个任务 currentTaskIndex++; if (currentTaskIndex < taskQueue.length) { const nextTask = taskQueue[currentTaskIndex]; showMessage(`开始处理下一个任务: ${nextTask.Name}`, 'info'); getCourseList(nextTask.TaskID, document.cookie); } else { showMessage('所有任务处理完毕', 'success'); stopBrushing(); } } } // 页面加载完成后检查是否在课程页面 function checkIfInCoursePage() { if (window.location.href.includes('/course-info')) { showMessage('已进入课程页面,准备处理视频...', 'info'); console.log('已进入课程页面,开始处理视频'); if (isBrushing) { handleVideoPlayback(); } } else { console.log('当前不在课程页面,URL:', window.location.href); } } // 使用DOMContentLoaded事件确保DOM已完全加载 document.addEventListener('DOMContentLoaded', function() { console.log('DOM已完全加载'); checkIfInCoursePage(); }); // 同时监听load事件以确保所有资源都已加载 window.addEventListener('load', function() { console.log('页面所有资源已加载'); checkIfInCoursePage(); }); // 监听URL变化,处理单页应用的路由变化 let lastUrl = window.location.href; window.addEventListener('popstate', function() { if (window.location.href !== lastUrl) { lastUrl = window.location.href; checkIfInCoursePage(); } }); })();