// ==UserScript== // @name 高教在线刷课助手(最终修复版) // @namespace http://tampermonkey.net/ // @version 0.0.6 // @description 修复PPT提前跳转、窗口拖拽及多窗口问题 // @author Sweek // @match *://*.cqooc.com/* // @license GPLv3 // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // 防止重复加载(核心修复多窗口问题) const instanceKey = 'sweek_helper_instance'; if (GM_getValue(instanceKey)) { return; // 已存在实例,不再加载 } GM_setValue(instanceKey, true); window.addEventListener('beforeunload', () => { GM_setValue(instanceKey, false); }); // 强制样式(解决窗口显示异常) GM_addStyle(` #sweek-helper-window { position: fixed !important; top: 20px !important; left: 20px !important; width: 380px !important; height: 500px !important; background: white !important; border: 2px solid #479e82 !important; border-radius: 8px !important; z-index: 999999 !important; box-shadow: 0 0 15px rgba(0,0,0,0.2) !important; overflow: hidden !important; font-family: "Microsoft YaHei", sans-serif !important; display: block !important; } #sweek-window-header { height: 45px !important; background: #479e82 !important; color: white !important; padding: 0 15px !important; line-height: 45px !important; font-size: 15px !important; font-weight: bold !important; cursor: move !important; display: flex !important; justify-content: space-between !important; align-items: center !important; } #sweek-minimize-btn { width: 30px !important; height: 30px !important; line-height: 30px !important; text-align: center !important; cursor: pointer !important; border-radius: 4px !important; background: rgba(255,255,255,0.2) !important; } #sweek-tab-container { height: 40px !important; display: flex !important; border-bottom: 1px solid #eee !important; } .sweek-tab { flex: 1 !important; text-align: center !important; line-height: 40px !important; cursor: pointer !important; font-size: 14px !important; color: #666 !important; background: #f5f5f5 !important; } .sweek-tab.active { background: white !important; color: #479e82 !important; border-bottom: 2px solid #479e82 !important; } #sweek-content-container { height: calc(100% - 85px) !important; overflow: hidden !important; } .sweek-content { height: 100% !important; overflow-y: auto !important; padding: 10px !important; display: none !important; } .sweek-content.active { display: block !important; } #sweek-log-container { color: #333 !important; font-size: 13px !important; line-height: 1.6 !important; } #sweek-status-container { color: #333 !important; font-size: 13px !important; line-height: 1.8 !important; } .sweek-log-item { margin: 3px 0 !important; padding: 2px 0 !important; border-bottom: 1px dashed #f0f0f0 !important; } .sweek-notification { position: fixed !important; top: 20px !important; right: 20px !important; padding: 12px 15px !important; background: white !important; border-left: 4px solid #479e82 !important; box-shadow: 0 2px 10px rgba(0,0,0,0.1) !important; z-index: 999998 !important; font-size: 14px !important; color: #333 !important; } `); // 全局变量 let taskQueue = []; let isWindowMinimized = false; let dragOffset = { x: 0, y: 0 }; let isDragging = false; let activeTimer = null; // 唯一定时器 let currentContentType = null; // 当前内容类型 // 初始化单窗口(确保只创建一个窗口) function initWindow() { // 先移除可能存在的旧窗口 const oldWindow = document.getElementById('sweek-helper-window'); if (oldWindow) { oldWindow.remove(); } // 创建新窗口 const windowEl = document.createElement('div'); windowEl.id = 'sweek-helper-window'; windowEl.innerHTML = `
高教在线刷课助手 v0.0.5
状态信息
执行日志

初始化中...

[${getCurrentTime()}] 日志启动
`; document.body.appendChild(windowEl); // 绑定窗口事件 bindWindowEvents(); addLog('窗口初始化完成'); } // 绑定窗口事件(修复拖拽问题) function bindWindowEvents() { const windowEl = document.getElementById('sweek-helper-window'); const headerEl = document.getElementById('sweek-window-header'); const minimizeBtn = document.getElementById('sweek-minimize-btn'); const tabContainer = document.getElementById('sweek-tab-container'); // 最小化功能 minimizeBtn.addEventListener('click', () => { isWindowMinimized = !isWindowMinimized; const contentContainer = document.getElementById('sweek-content-container'); const tabContainerEl = document.getElementById('sweek-tab-container'); if (isWindowMinimized) { contentContainer.style.display = 'none'; tabContainerEl.style.display = 'none'; windowEl.style.height = '45px'; minimizeBtn.textContent = '□'; } else { contentContainer.style.display = 'block'; tabContainerEl.style.display = 'flex'; windowEl.style.height = '500px'; minimizeBtn.textContent = '—'; } }); // Tab切换 tabContainer.addEventListener('click', (e) => { const tab = e.target.closest('.sweek-tab'); if (!tab) return; document.querySelectorAll('.sweek-tab').forEach(el => el.classList.remove('active')); tab.classList.add('active'); const tabName = tab.dataset.tab; document.querySelectorAll('.sweek-content').forEach(el => el.classList.remove('active')); document.getElementById(`sweek-${tabName}-container`).classList.add('active'); }); // 窗口拖拽(核心修复拖拽问题) headerEl.addEventListener('mousedown', (e) => { e.preventDefault(); // 防止默认行为干扰 isDragging = true; // 计算鼠标相对窗口的位置 dragOffset.x = e.clientX - windowEl.getBoundingClientRect().left; dragOffset.y = e.clientY - windowEl.getBoundingClientRect().top; headerEl.style.cursor = 'grabbing'; }); // 鼠标移动时拖动窗口 document.addEventListener('mousemove', (e) => { if (isDragging) { e.preventDefault(); const windowEl = document.getElementById('sweek-helper-window'); if (!windowEl) { isDragging = false; return; } // 计算新位置 const newX = e.clientX - dragOffset.x; const newY = e.clientY - dragOffset.y; // 限制在可视区域内 const maxX = window.innerWidth - windowEl.offsetWidth; const maxY = window.innerHeight - windowEl.offsetHeight; const constrainedX = Math.max(0, Math.min(newX, maxX)); const constrainedY = Math.max(0, Math.min(newY, maxY)); // 设置新位置 windowEl.style.left = `${constrainedX}px`; windowEl.style.top = `${constrainedY}px`; } }); // 释放拖动 document.addEventListener('mouseup', () => { isDragging = false; headerEl.style.cursor = 'move'; }); } // 工具函数 function getCurrentTime() { const date = new Date(); return date.toLocaleTimeString('zh-CN', { hour12: false }); } function formatTime(seconds) { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs < 10 ? '0' + secs : secs}`; } function addLog(message) { const logContainer = document.getElementById('sweek-log-container'); if (!logContainer) return; const logItem = document.createElement('div'); logItem.className = 'sweek-log-item'; logItem.textContent = `[${getCurrentTime()}] ${message}`; logContainer.appendChild(logItem); logContainer.scrollTop = logContainer.scrollHeight; } function updateStatus(html) { const statusContainer = document.getElementById('sweek-status-container'); if (statusContainer) { statusContainer.innerHTML = html; } } function showNotification(message, duration = 3000) { const notification = document.createElement('div'); notification.className = 'sweek-notification'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => notification.remove(), 300); }, duration); } // 内容类型识别(增强PPT识别) function getContentType() { const body = document.body.innerHTML.toLowerCase(); // 增强PPT识别规则 if (body.includes('ppt') || body.includes('powerpoint') || body.includes('幻灯片') || body.includes('slide') && !body.includes('video')) { return 'ppt'; } else if (body.includes('video') || body.includes('dplayer')) { return 'video'; } else if (body.includes('pdf') || body.includes('pdf-viewer')) { return 'pdf'; } return 'unknown'; } // 修复PPT提前跳转问题(精确计时) function handlePPT() { return new Promise((resolve) => { currentContentType = 'ppt'; const totalSeconds = 35; let remainingSeconds = totalSeconds; addLog(`开始处理PPT,将在${totalSeconds}秒后完成`); // 清除可能存在的旧定时器 if (activeTimer) clearInterval(activeTimer); // 立即更新一次状态 updatePPTStatus(remainingSeconds, totalSeconds); // 使用精确计时 const startTime = Date.now(); activeTimer = setInterval(() => { // 计算实际已过去的时间(解决定时器不准问题) const elapsed = Math.floor((Date.now() - startTime) / 1000); remainingSeconds = totalSeconds - elapsed; if (remainingSeconds <= 0) { clearInterval(activeTimer); activeTimer = null; addLog('PPT处理完成,准备跳转下一节'); resolve(); } else { updatePPTStatus(remainingSeconds, totalSeconds); } }, 1000); // 每秒检查一次 }); } // 更新PPT状态 function updatePPTStatus(remaining, total) { const progress = ((total - remaining) / total) * 100; updateStatus(`

内容类型: PPT

处理进度: ${progress.toFixed(1)}%

剩余时间: ${remaining}秒

总耗时: ${total}秒

`); } // 处理视频 function handleVideo() { return new Promise((resolve) => { currentContentType = 'video'; addLog('开始处理视频内容'); const video = document.querySelector('video'); if (!video) { addLog('未找到视频元素,35秒后重试'); setTimeout(resolve, 35000); return; } // 清除旧定时器 if (activeTimer) clearInterval(activeTimer); // 强制播放 function forcePlay() { if (video.paused && !video.ended) { video.play().catch(() => { video.muted = true; video.play().catch(err => addLog(`播放失败: ${err.message}`)); }); } } // 设置倍速 video.playbackRate = 1.5; addLog(`视频倍速设置为: ${video.playbackRate}x`); forcePlay(); video.addEventListener('pause', forcePlay); // 进度更新 activeTimer = setInterval(() => { if (video.duration) { const progress = (video.currentTime / video.duration) * 100; updateStatus(`

内容类型: 视频

播放进度: ${progress.toFixed(1)}%

当前时长: ${formatTime(video.currentTime)}/${formatTime(video.duration)}

播放倍速: ${video.playbackRate}x

`); } }, 1000); // 视频结束 const endHandler = () => { clearInterval(activeTimer); activeTimer = null; video.removeEventListener('ended', endHandler); video.removeEventListener('pause', forcePlay); addLog('视频播放完成'); resolve(); }; video.addEventListener('ended', endHandler); }); } // 处理PDF function handlePDF() { return new Promise((resolve) => { currentContentType = 'pdf'; addLog('开始处理PDF,35秒后完成'); let countdown = 35; if (activeTimer) clearInterval(activeTimer); activeTimer = setInterval(() => { countdown--; const progress = ((35 - countdown) / 35) * 100; updateStatus(`

内容类型: PDF

处理进度: ${progress.toFixed(1)}%

剩余时间: ${countdown}秒

`); if (countdown <= 0) { clearInterval(activeTimer); activeTimer = null; addLog('PDF处理完成'); resolve(); } }, 1000); }); } // 处理当前内容 async function processCurrentContent() { const contentType = getContentType(); addLog(`检测到内容类型: ${contentType}`); switch (contentType) { case 'video': await handleVideo(); break; case 'pdf': await handlePDF(); break; case 'ppt': await handlePPT(); // 使用修复后的PPT处理函数 break; default: addLog('未知内容类型,35秒后跳过'); await new Promise(resolve => setTimeout(resolve, 35000)); break; } } // 任务处理流程 function goToNextTask() { if (taskQueue.length === 0) { addLog('所有任务已完成'); updateStatus('

所有任务已完成!

'); return; } const nextTask = taskQueue.shift(); const taskText = nextTask.textContent.trim().substring(0, 20) + '...'; addLog(`准备处理任务: ${taskText}`); updateStatus(`

准备处理: ${taskText}

`); // 点击任务 setTimeout(() => { nextTask.click(); // 等待内容加载 setTimeout(async () => { await processCurrentContent(); // 延迟跳转,确保记录已保存 setTimeout(goToNextTask, 3000); }, 5000); // 等待5秒确保页面加载完成 }, 1000); } // 获取任务列表 function getTaskList() { return Array.from(document.querySelectorAll('.third-level-box')).filter(el => { const text = el.textContent.toLowerCase(); return !text.includes('作业') && !text.includes('测验') && !text.includes('考试'); }); } // 开始任务 function startTasks() { const tasks = getTaskList(); if (tasks.length === 0) { addLog('未找到可处理的任务'); updateStatus('

未找到可处理的任务

'); return; } // 找到当前活跃任务 const activeIndex = tasks.findIndex(el => el.classList.contains('active')); taskQueue = tasks.slice(activeIndex > -1 ? activeIndex : 0); addLog(`共找到 ${tasks.length} 个任务,从第 ${activeIndex + 1} 个开始`); updateStatus(`

总任务数: ${tasks.length} 个

剩余任务: ${taskQueue.length} 个

开始处理...

`); goToNextTask(); } // 初始化 function init() { // 确保只初始化一次 if (document.getElementById('sweek-helper-window')) { return; } initWindow(); addLog('刷课助手已启动(最终修复版)'); const currentPath = window.location.pathname; if (currentPath.includes('/course/detail/courseStudy')) { showNotification('已进入课程学习页面,即将开始处理', 3000); setTimeout(startTasks, 2000); } else if (currentPath.includes('/account/course')) { showNotification('请选择要学习的课程', 3000); } else { showNotification('请先进入"在学课程"页面', 3000); } } // 启动 init(); })();