// ==UserScript== // @name 学起Plus-极简版(增强)— 含后台播放+防挂机 // @namespace http://scriptcat.net/ // @version 0.7 // @description 视频自动静音+2倍速+自动播放+结束切节+防后台检测+模拟鼠标移动防挂机 // @match *://*.chinaedu.net/* // @match *://*.sccchina.net/* // @run-at document-start // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; /* ========== 1. 可见性检测禁用(隐蔽版) ========== */ function disableVisibilityCheck() { const origAddEventListener = EventTarget.prototype.addEventListener; EventTarget.prototype.addEventListener = function (type, listener, options) { if (type === 'visibilitychange') return; return origAddEventListener.call(this, type, listener, options); }; Object.defineProperty(document, 'hidden', { configurable: true, get: () => false }); Object.defineProperty(document, 'visibilityState', { configurable: true, get: () => 'visible' }); console.log('[学起Plus] 页面可见性检测已禁用'); } disableVisibilityCheck(); /* ========== 2. 等待 jQuery 加载的工具函数 ========== */ function waitForJQuery(callback, timeout = 10000) { if (window.jQuery) { callback(window.jQuery); return; } const start = Date.now(); const interval = setInterval(() => { if (window.jQuery) { clearInterval(interval); callback(window.jQuery); } else if (Date.now() - start > timeout) { clearInterval(interval); console.error('[学起Plus] jQuery 加载超时,脚本部分功能可能失效'); } }, 50); } /* ========== 3. 模拟随机鼠标移动(防挂机) ========== */ function startIdleMouseSimulator(options) { const defaults = { idleThreshold: 60000, // 空闲超过60秒开始模拟 minInterval: 30000, // 模拟间隔最小值 maxInterval: 120000, // 模拟间隔最大值 moveRange: 5 // 随机移动范围(像素) }; const config = Object.assign({}, defaults, options); let lastActivity = Date.now(); let simulateTimer = null; let lastMouseX = window.innerWidth / 2; let lastMouseY = window.innerHeight / 2; function recordActivity() { lastActivity = Date.now(); if (simulateTimer) { clearTimeout(simulateTimer); simulateTimer = null; } } ['mousemove', 'keydown', 'scroll', 'click', 'touchstart'].forEach(evt => { document.addEventListener(evt, recordActivity, { passive: true }); }); document.addEventListener('mousemove', e => { lastMouseX = e.clientX; lastMouseY = e.clientY; }); function simulateMouseMove() { const dx = (Math.random() - 0.5) * config.moveRange * 2; const dy = (Math.random() - 0.5) * config.moveRange * 2; const newX = Math.min(Math.max(lastMouseX + dx, 0), window.innerWidth); const newY = Math.min(Math.max(lastMouseY + dy, 0), window.innerHeight); const event = new MouseEvent('mousemove', { clientX: newX, clientY: newY, bubbles: true, cancelable: true }); document.dispatchEvent(event); lastMouseX = newX; lastMouseY = newY; } function scheduleNext() { if (Date.now() - lastActivity >= config.idleThreshold) { simulateMouseMove(); } const delay = config.minInterval + Math.random() * (config.maxInterval - config.minInterval); simulateTimer = setTimeout(scheduleNext, delay); } scheduleNext(); console.log('[学起Plus] 空闲鼠标模拟器已启动(阈值:' + (config.idleThreshold/1000) + '秒)'); } /* ========== 4. 等待 jQuery 就绪后,执行业务逻辑 ========== */ waitForJQuery(function($) { // 启动鼠标模拟(不依赖 jQuery,但放在这里可以确保 DOM 存在) startIdleMouseSimulator({ idleThreshold: 60000 }); // 视频页逻辑 if (location.href.includes('video.html')) { console.log('[学起Plus] 视频页脚本启动'); const observer = new MutationObserver((mutations, obs) => { const v = document.querySelector('video'); if (v) { setupVideo(v); obs.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); const existingVideo = document.querySelector('video'); if (existingVideo) { setupVideo(existingVideo); observer.disconnect(); } function setupVideo(v) { v.muted = true; v.autoplay = true; v.playbackRate = 2; // 重试播放函数:失败后1秒再试,直到成功 function tryPlay() { v.play().then(() => { console.log('[学起Plus] 视频开始播放'); }).catch(() => { console.log('[学起Plus] 播放失败,1秒后重试'); setTimeout(tryPlay, 1000); }); } // 如果视频已经准备好,直接尝试;否则等 canplay 事件 if (v.readyState >= 2) { // HAVE_CURRENT_DATA 或更高 tryPlay(); } else { v.addEventListener('canplay', tryPlay, { once: true }); // 防止 canplay 一直不触发,加个兜底 setTimeout(() => { if (v.paused) tryPlay(); }, 2000); } // 视频结束通知父窗口(整个 iframe 即将刷新,这里不需要再处理新视频) v.addEventListener('ended', () => { console.log('[学起Plus] 视频结束,发送 studyNext 消息'); window.parent.postMessage('studyNext', '*'); }); } return; // 视频页结束 } // 大纲页逻辑 console.log('[学起Plus] 大纲页脚本启动'); const cleanMask = () => { const mask = document.querySelector('#pop, #cover'); if (mask) mask.remove(); }; cleanMask(); setInterval(cleanMask, 2000); window.addEventListener('message', (e) => { if (e.data === 'studyNext') dispatchSectionNext(); }); const strategies = [ { match: () => document.querySelector('#ztree_online li'), handler: zTreeNext }, { match: () => document.querySelector('ul.sub-menu'), handler: nestedMenuNext } ]; function dispatchSectionNext() { for (const strategy of strategies) { if (strategy.match()) { console.log('[学起Plus] 匹配到策略:', strategy.handler.name); strategy.handler(); return; } } console.error('[学起Plus] 未找到匹配的课程目录结构'); } function zTreeNext() { const $ = window.$; const $items = $('#ztree_online li'); if (!$items.length) return; let $active = $items.find('a.curSelectedNode').closest('li'); if (!$active.length && window.currOutlineId) { $active = $items.filter('#' + window.currOutlineId); } if (!$active.length) $active = $items.first(); const currentIndex = $items.index($active); const $next = $items.eq(currentIndex + 1); if ($next.length) { $items.find('a').removeClass('curSelectedNode'); $next.find('a').addClass('curSelectedNode'); $next.find('a')[0].click(); $next[0].scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } else { console.log('[学起Plus] 已经是最后一个节点'); } } function nestedMenuNext() { const $ = window.$; const $li = $('li').filter((_, li) => !$(li).find('ul.sub-menu').length); let $active = $li.filter('[class*="activeState"]'); if (!$active.length && window.currOutlineId) { $active = $li.filter(`[nid="${window.currOutlineId}"]`); } const $next = $li.eq($li.index($active) + 1); if ($next.length) { $('ul.sub-menu').hide() .filter($next.parents('li').map((_, li) => $(li).children('ul.sub-menu')[0])) .show() .parent().addClass('open'); $next.find('a')[0].click(); } } }); })();