// ==UserScript== // @name 广开网络教学平台/广东开放大学【最高16倍】自动刷课 // @namespace http://course.ougd.cn/ // @version 2.3.0 // @description 广东开放大学课程视频自动播放、倍速、自动滚动文档、自动切换下一个学习任务,模拟观看行为 // @author GDOU Assistant // @match *://course.ougd.cn/* // @grant none // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // ========== 防止在iframe中执行 ========== if (window.top !== window) return; // ========== 全局单例锁 - 防止重复执行 ========== if (window.__GDOUScriptInitialized__) return; window.__GDOUScriptInitialized__ = true; // ========== 配置与存储 ========== var STORAGE_KEY = 'gdou_automation_v2'; var defaultConfig = { autoPlay: true, videoSpeed: 2.0, rememberSettings: true, autoScrollDocs: true, autoNext: false, nextDelay: 3000, showPanel: true, enableShortcuts: true, autoCompletePages: true, debugMode: false }; function loadConfig() { try { var saved = localStorage.getItem(STORAGE_KEY); if (saved) { var parsed = JSON.parse(saved); var merged = {}; for (var key in defaultConfig) { merged[key] = (parsed && typeof parsed[key] !== 'undefined') ? parsed[key] : defaultConfig[key]; } return merged; } } catch(e) { console.warn('加载配置失败:', e); } return Object.assign({}, defaultConfig); } function saveConfig() { if (!CONFIG.rememberSettings) return; try { localStorage.setItem(STORAGE_KEY, JSON.stringify(CONFIG)); } catch(e) { console.warn('保存配置失败:', e); } } var CONFIG = loadConfig(); // ========== 工具函数 ========== function log() { if (!CONFIG.debugMode) return; var args = ['[GDOU助手]']; for (var i = 0; i < arguments.length; i++) args.push(arguments[i]); console.log.apply(console, args); } function getPageType() { var url = location.href; if (url.indexOf('mod/ougdresource/view.php') !== -1) return 'video'; if (url.indexOf('mod/page/view.php') !== -1) return 'page'; if (url.indexOf('mod/forum/view.php') !== -1) return 'forum'; if (url.indexOf('mod/forum/discuss.php') !== -1) return 'discuss'; if (url.indexOf('mod/quiz/view.php') !== -1) return 'quiz'; if (url.indexOf('course/view.php') !== -1) return 'course'; if (url.indexOf('/my/') !== -1 || url.indexOf('/my/index') !== -1) return 'dashboard'; if (url.indexOf('login/index.php') !== -1 || url.indexOf('authserver') !== -1) return 'login'; return 'other'; } function toast(msg, duration) { duration = duration || 2000; var t = document.getElementById('gdou-toast'); if (!t) { t = document.createElement('div'); t.id = 'gdou-toast'; t.style.cssText = [ 'position: fixed', 'top: 80px', 'left: 50%', 'transform: translateX(-50%)', 'z-index: 9999999', 'background: rgba(0,100,200,0.92)', 'color: white', 'padding: 12px 24px', 'border-radius: 8px', 'font-family: sans-serif', 'font-size: 14px', 'transition: opacity 0.3s', 'box-shadow: 0 4px 12px rgba(0,0,0,0.2)', 'max-width: 80%', 'text-align: center', 'pointer-events: none' ].join(';'); document.body.appendChild(t); } t.textContent = msg; t.style.opacity = '1'; clearTimeout(t._timer); t._timer = setTimeout(function() { t.style.opacity = '0'; }, duration); } function findNextActivityLink() { // ========== 方式1:查找带箭头的链接(►)——首选,最精确 ========== var arrowLinks = document.querySelectorAll('a[href*="mod/"]'); for (var m = 0; m < arrowLinks.length; m++) { var linkText = arrowLinks[m].textContent.trim(); if (linkText.indexOf('►') !== -1 || linkText.indexOf('»') !== -1) { if (linkText.length < 100 && arrowLinks[m].href.length > 10) { return { type: 'link', url: arrowLinks[m].href, title: linkText }; } } } // ========== 方式2:通过"跳至..."下拉框定位下一个活动 ========== var jumpSelect = document.querySelector('select#jump-to-activity, select[name="jump"], select#jump, select[aria-label*="跳至"]'); if (jumpSelect && jumpSelect.options.length > 1) { var idMatch = location.href.match(/[?&]id=(\d+)/); var currentId = idMatch ? idMatch[1] : null; var currentIndex = -1; if (currentId) { for (var i = 0; i < jumpSelect.options.length; i++) { var optValue = jumpSelect.options[i].value; if (optValue && optValue.indexOf('id=' + currentId) !== -1) { currentIndex = i; break; } } } var startIdx = (currentIndex >= 0) ? currentIndex + 1 : 1; for (var k = startIdx; k < jumpSelect.options.length; k++) { var opt = jumpSelect.options[k]; if (!opt.value || opt.value.length < 10) continue; if (opt.value.indexOf('mod/quiz') !== -1) continue; if (opt.value.indexOf('mod/assign') !== -1) continue; if (opt.value.indexOf('ougdresource') !== -1 || opt.value.indexOf('mod/page') !== -1 || opt.value.indexOf('mod/forum') !== -1 || opt.value.indexOf('mod/') !== -1) { var fullUrl = opt.value; if (fullUrl.indexOf('http') !== 0) { fullUrl = location.origin + opt.value; } return { type: 'jump', url: fullUrl, title: opt.textContent.trim(), rawIndex: k }; } } } // ========== 方式3:查找accesskey="n"的下一页链接 ========== var accesskeyLinks = document.querySelectorAll('a[accesskey="n"]'); if (accesskeyLinks.length > 0) { return { type: 'link', url: accesskeyLinks[0].href, title: accesskeyLinks[0].textContent.trim() }; } return null; } function goToNextActivity() { var next = findNextActivityLink(); if (!next) { toast('没有找到下一个学习任务'); updatePanelStatus('⚠ 未找到下一个活动,已停止'); return; } // 显示提示信息 var msg = '即将跳转到: ' + next.title.substring(0, 30); if (next.type === 'jump') msg = '📋 ' + msg; else if (next.type === 'link') msg = '🔗 ' + msg; toast(msg + '...', 2000); updatePanelStatus('⏩ 跳转到: ' + next.title.substring(0, 40)); // 直接用location.href跳转 setTimeout(function() { location.href = next.url; }, 1500); } // ========== 控制面板 UI ========== function createPanel() { if (!CONFIG.showPanel || document.getElementById('gdou-panel')) return; var panel = document.createElement('div'); panel.id = 'gdou-panel'; panel.style.cssText = [ 'position: fixed', 'top: 100px', 'right: 20px', 'z-index: 999999', 'background: rgba(30, 60, 114, 0.96)', 'color: white', 'padding: 14px', 'border-radius: 10px', 'min-width: 260px', 'box-shadow: 0 4px 20px rgba(0,0,0,0.35)', 'font-family: -apple-system, "Microsoft YaHei", sans-serif', 'font-size: 13px', 'backdrop-filter: blur(10px)', 'border: 1px solid rgba(255,255,255,0.1)' ].join(';'); var pageType = getPageType(); var typeText = { 'video': '视频播放', 'page': '文档阅读', 'forum': '讨论区', 'discuss': '讨论话题', 'quiz': '测验', 'course': '课程页面', 'dashboard': '课程主页', 'login': '登录页面', 'other': '其他页面' }; panel.innerHTML = '
🎓 广开自动学习助手
' + buildPanelBody() + '
'; document.body.appendChild(panel); // 折叠功能 var body = panel.querySelector('#gdou-panel-body'); var collapseBtn = panel.querySelector('#gdou-collapse-btn'); collapseBtn.addEventListener('click', function() { if (body.style.display === 'none') { body.style.display = 'block'; collapseBtn.textContent = '−'; } else { body.style.display = 'none'; collapseBtn.textContent = '+'; } }); // 拖动功能 var header = panel.querySelector('#gdou-panel-header'); var dragging = false, startX, startY, origX, origY; header.addEventListener('mousedown', function(e) { if (e.target.id === 'gdou-collapse-btn') return; dragging = true; startX = e.clientX; startY = e.clientY; var rect = panel.getBoundingClientRect(); origX = rect.left; origY = rect.top; panel.style.right = 'auto'; panel.style.left = origX + 'px'; panel.style.top = origY + 'px'; e.preventDefault(); }); document.addEventListener('mousemove', function(e) { if (!dragging) return; var newX = Math.max(0, Math.min(window.innerWidth - 280, origX + e.clientX - startX)); var newY = Math.max(0, Math.min(window.innerHeight - 50, origY + e.clientY - startY)); panel.style.left = newX + 'px'; panel.style.top = newY + 'px'; }); document.addEventListener('mouseup', function() { dragging = false; }); // 绑定UI事件 bindPanelEvents(panel); updatePanelStatus('初始化中...'); } // 倍速选项列表(支持最高16倍) var SPEED_OPTIONS = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5, 6, 8, 10, 12, 16]; function buildPanelBody() { var pageType = getPageType(); var html = ''; // 倍速控制 - 上下选择模式 html += '
视频倍速
'; html += ''; html += '
' + CONFIG.videoSpeed + 'x
'; html += ''; html += '
'; // 快速选择网格 html += '
'; // 开关选项 html += '
'; // 操作按钮 html += '
'; // 状态显示 html += '
状态: 初始化中...
'; // 联系方式 html += '
高质量答题及合作加Q:640105435
问题反馈Q群:949193546 Q群2:1103746012
'; return html; } function updatePanelStatus(msg) { var el = document.getElementById('gdou-status'); if (el) { var time = new Date(); var timeStr = String(time.getHours()).padStart(2, '0') + ':' + String(time.getMinutes()).padStart(2, '0') + ':' + String(time.getSeconds()).padStart(2, '0'); el.innerHTML = '
[' + timeStr + '] ' + msg + '
' + el.innerHTML; // 只保留最近5条 var children = el.children; while (children.length > 5) el.removeChild(children[children.length - 1]); } } // 辅助函数:设置倍速 function setSpeed(val) { CONFIG.videoSpeed = val; var display = document.getElementById('gdou-speed-display'); if (display) display.textContent = val + 'x'; applyVideoSpeed(val); updateSpeedButtonsUI(val); saveConfig(); toast('倍速设置为 ' + val + 'x'); } // 辅助函数:更新倍速按钮高亮 function updateSpeedButtonsUI(current) { var btns = document.querySelectorAll('.gdou-speed-btn'); btns.forEach(function(b) { var v = parseFloat(b.getAttribute('data-speed')); if (Math.abs(v - current) < 0.01) { b.style.background = '#1e88e5'; b.style.fontWeight = 'bold'; } else { b.style.background = '#2d4a7a'; b.style.fontWeight = 'normal'; } }); } function bindPanelEvents(panel) { var speedDisplay = panel.querySelector('#gdou-speed-display'); var speedGrid = panel.querySelector('#gdou-speed-grid'); var speedDown = panel.querySelector('#gdou-speed-down'); var speedUp = panel.querySelector('#gdou-speed-up'); // 点击数字显示区域:显示/隐藏选择网格 speedDisplay.addEventListener('click', function() { if (speedGrid.style.display === 'none') { speedGrid.style.display = 'grid'; } else { speedGrid.style.display = 'none'; } }); // 减号按钮:降低倍速 speedDown.addEventListener('click', function() { var idx = SPEED_OPTIONS.indexOf(CONFIG.videoSpeed); if (idx > 0) { setSpeed(SPEED_OPTIONS[idx - 1]); } else if (idx === -1) { // 找到最近的较低值 for (var i = SPEED_OPTIONS.length - 1; i >= 0; i--) { if (SPEED_OPTIONS[i] < CONFIG.videoSpeed) { setSpeed(SPEED_OPTIONS[i]); break; } } } }); // 加号按钮:提高倍速 speedUp.addEventListener('click', function() { var idx = SPEED_OPTIONS.indexOf(CONFIG.videoSpeed); if (idx >= 0 && idx < SPEED_OPTIONS.length - 1) { setSpeed(SPEED_OPTIONS[idx + 1]); } else if (idx === -1) { // 找到最近的较高值 for (var i = 0; i < SPEED_OPTIONS.length; i++) { if (SPEED_OPTIONS[i] > CONFIG.videoSpeed) { setSpeed(SPEED_OPTIONS[i]); break; } } } }); // 快速选择按钮 var buttons = panel.querySelectorAll('.gdou-speed-btn'); buttons.forEach(function(btn) { btn.addEventListener('click', function() { var val = parseFloat(this.getAttribute('data-speed')); setSpeed(val); speedGrid.style.display = 'none'; }); }); // 开关 panel.querySelector('#gdou-autoplay').addEventListener('change', function() { CONFIG.autoPlay = this.checked; saveConfig(); if (this.checked && getPageType() === 'video') { handleVideoPage(); toast('已启用自动播放'); } }); panel.querySelector('#gdou-autoscroll').addEventListener('change', function() { CONFIG.autoScrollDocs = this.checked; saveConfig(); if (this.checked && getPageType() === 'page') { handlePage_autoScroll(); toast('已启用自动滚动'); } }); panel.querySelector('#gdou-autonext').addEventListener('change', function() { CONFIG.autoNext = this.checked; saveConfig(); toast(this.checked ? '已启用自动切换到下一个' : '已关闭自动切换'); }); // 操作按钮 panel.querySelector('#gdou-btn-next').addEventListener('click', goToNextActivity); panel.querySelector('#gdou-btn-play').addEventListener('click', function() { var vids = getAllVideos(); vids.forEach(function(v) { v.play(); }); toast('正在尝试播放所有视频...'); }); panel.querySelector('#gdou-btn-scroll').addEventListener('click', function() { handlePage_autoScroll(true); toast('开始滚动文档...'); }); } // ========== 视频处理 ========== function getAllVideos() { var videos = []; // 主文档 document.querySelectorAll('video').forEach(function(v) { videos.push(v); }); // iframe内 var iframes = document.querySelectorAll('iframe'); for (var i = 0; i < iframes.length; i++) { try { var iframeDoc = iframes[i].contentDocument || iframes[i].contentWindow.document; iframeDoc.querySelectorAll('video').forEach(function(v) { videos.push(v); }); } catch(e) { /* 跨域忽略 */ } } return videos; } function applyVideoSpeed(speed) { var vids = getAllVideos(); vids.forEach(function(v) { try { v.playbackRate = speed; v.defaultPlaybackRate = speed; log('设置视频倍速:', speed); } catch(e) { log('设置倍速失败:', e); } }); } var videoProcessed = false; function handleVideoPage() { if (videoProcessed) return; // 1. 等待iframe加载并设置倍速 var attempts = 0; var maxAttempts = 20; var trySetup = function() { attempts++; var vids = getAllVideos(); if (vids.length > 0) { videoProcessed = true; // 设置倍速 applyVideoSpeed(CONFIG.videoSpeed); // 监听视频事件保持倍速 vids.forEach(function(v) { v.addEventListener('ratechange', function() { if (Math.abs(v.playbackRate - CONFIG.videoSpeed) > 0.1) { v.playbackRate = CONFIG.videoSpeed; } }); v.addEventListener('loadedmetadata', function() { v.playbackRate = CONFIG.videoSpeed; }); v.addEventListener('play', function() { v.playbackRate = CONFIG.videoSpeed; }); // 自动播放 if (CONFIG.autoPlay && v.paused) { var p = v.play(); if (p && p.catch) { p.catch(function() { v.muted = true; v.play().catch(function() { log('自动播放失败,请手动点击播放'); }); }); } } }); updatePanelStatus('✓ 已设置视频倍速 ' + CONFIG.videoSpeed.toFixed(1) + 'x'); toast('视频倍速设置为 ' + CONFIG.videoSpeed.toFixed(1) + 'x'); // 如果启用了自动下一个,监听视频播放进度(匹配系统70%完成规则) if (CONFIG.autoNext) { var triggered = false; vids.forEach(function(v) { v.addEventListener('ended', function() { if (!triggered) { triggered = true; updatePanelStatus('✓ 视频播放完成,等待切换下一个...'); setTimeout(goToNextActivity, CONFIG.nextDelay); } }); v.addEventListener('timeupdate', function() { if (!triggered && v.duration && v.duration > 0) { var progress = v.currentTime / v.duration; if (progress >= 0.70) { triggered = true; updatePanelStatus('✓ 进度70%(系统标记完成),准备切换下一个...'); toast('视频完成进度已达70%,即将跳转下一个...'); setTimeout(goToNextActivity, CONFIG.nextDelay); } } }); v.addEventListener('play', function() { // 重新设置倍速(有些播放器在play时会重置倍速) if (Math.abs(v.playbackRate - CONFIG.videoSpeed) > 0.1) { v.playbackRate = CONFIG.videoSpeed; } }); }); } } else if (attempts < maxAttempts) { setTimeout(trySetup, 1000); } else { updatePanelStatus('⚠ 未发现视频元素,请刷新页面'); } }; trySetup(); // 使用MutationObserver监测iframe变化 var observer = new MutationObserver(function() { var vids = getAllVideos(); if (vids.length > 0) { vids.forEach(function(v) { if (Math.abs(v.playbackRate - CONFIG.videoSpeed) > 0.1) { v.playbackRate = CONFIG.videoSpeed; } }); } }); // 监控所有iframe的加载 var iframes = document.querySelectorAll('iframe'); iframes.forEach(function(iframe) { iframe.addEventListener('load', function() { setTimeout(handleVideoPage, 1500); }); }); observer.observe(document.documentElement, { childList: true, subtree: true }); } // ========== 文档页面处理 ========== var pageProcessing = false; function handlePage_autoScroll(force) { if (pageProcessing && !force) return; // 如果不启用自动滚动,且不是强制调用,则跳过 if (!CONFIG.autoScrollDocs && !force) return; pageProcessing = true; updatePanelStatus('📄 开始自动阅读文档...'); // 自动滚动到底部以触发"查看"状态 var totalHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight); var viewport = window.innerHeight; var scrollAmount = Math.min(300, Math.max(100, (totalHeight - viewport) / 20)); var current = 0; var scrollInterval = null; scrollInterval = setInterval(function() { current += scrollAmount; window.scrollTo(0, current); if (current >= totalHeight - viewport - 50) { clearInterval(scrollInterval); window.scrollTo(0, totalHeight); updatePanelStatus('✓ 文档阅读完成'); // 自动下一个 if (CONFIG.autoNext) { setTimeout(function() { goToNextActivity(); }, CONFIG.nextDelay); } } }, 300); } // ========== 讨论区页面处理 ========== function handleForumPage() { updatePanelStatus('💬 讨论区 - 请手动阅读和参与讨论'); } // ========== 课程列表页面处理 ========== function handleCoursePage() { var count = document.querySelectorAll('a[href*="mod/"]').length; updatePanelStatus('📚 课程页面,发现 ' + count + ' 个学习资源链接'); } // ========== 仪表盘页面处理 ========== function handleDashboardPage() { var courseLinks = document.querySelectorAll('[data-course-id], a[href*="course/view.php"]'); updatePanelStatus('🏠 课程主页,共发现 ' + courseLinks.length + ' 个课程入口'); } // ========== 快捷键 ========== function setupShortcuts() { if (!CONFIG.enableShortcuts) return; document.addEventListener('keydown', function(e) { var active = document.activeElement; if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable)) return; var key = e.key; var handled = false; if (key === '[' || key === '【') { // 减速 CONFIG.videoSpeed = Math.max(0.5, CONFIG.videoSpeed - 0.25); applyVideoSpeed(CONFIG.videoSpeed); updateShortcutUI(); toast('倍速: ' + CONFIG.videoSpeed.toFixed(2) + 'x'); handled = true; } else if (key === ']' || key === '】') { // 加速 CONFIG.videoSpeed = Math.min(4.0, CONFIG.videoSpeed + 0.25); applyVideoSpeed(CONFIG.videoSpeed); updateShortcutUI(); toast('倍速: ' + CONFIG.videoSpeed.toFixed(2) + 'x'); handled = true; } else if (key === ';') { // 1.5x CONFIG.videoSpeed = 1.5; applyVideoSpeed(CONFIG.videoSpeed); updateShortcutUI(); toast('倍速: 1.50x'); handled = true; } else if (key === "'" || key === '\"') { // 2x CONFIG.videoSpeed = 2.0; applyVideoSpeed(CONFIG.videoSpeed); updateShortcutUI(); toast('倍速: 2.00x'); handled = true; } else if (key === '\\' || key === '|') { // 1x CONFIG.videoSpeed = 1.0; applyVideoSpeed(CONFIG.videoSpeed); updateShortcutUI(); toast('倍速: 1.00x'); handled = true; } else if (key === ' ') { // 空格: 播放/暂停 var vids = getAllVideos(); if (vids.length > 0) { vids.forEach(function(v) { if (v.paused) v.play(); else v.pause(); }); handled = true; } } else if (key === 'n' || key === 'N') { // N键: 下一个 goToNextActivity(); handled = true; } if (handled) { e.preventDefault(); saveConfig(); } }); } function updateShortcutUI() { var display = document.querySelector('#gdou-speed-display'); if (display) display.textContent = CONFIG.videoSpeed + 'x'; updateSpeedButtonsUI(CONFIG.videoSpeed); } // ========== 主初始化 ========== function init() { var pageType = getPageType(); log('页面类型:', pageType, 'URL:', location.href); // 创建控制面板 createPanel(); // 根据页面类型处理 switch(pageType) { case 'video': handleVideoPage(); break; case 'page': setTimeout(handlePage_autoScroll, 1500); break; case 'forum': case 'discuss': handleForumPage(); break; case 'course': handleCoursePage(); break; case 'dashboard': handleDashboardPage(); break; case 'login': updatePanelStatus('🔐 登录页面,请手动登录'); break; default: // 尝试检测是否有视频 setTimeout(function() { var vids = getAllVideos(); if (vids.length > 0) { applyVideoSpeed(CONFIG.videoSpeed); updatePanelStatus('✓ 发现 ' + vids.length + ' 个视频,已应用倍速'); } }, 2000); } // 设置快捷键 setupShortcuts(); // 定期检查和刷新倍速(防止页面重置) setInterval(function() { if (getPageType() === 'video') { applyVideoSpeed(CONFIG.videoSpeed); } }, 5000); log('GDOU学习助手初始化完成'); } // 根据页面加载状态启动 if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(init, 1200); } else { document.addEventListener('DOMContentLoaded', function() { setTimeout(init, 1200); }); } // 兜底方案:3秒后强制执行一次 setTimeout(function() { if (!document.getElementById('gdou-panel')) init(); }, 3000); })();