// ==UserScript== // @name AHNYJY安徽公需课-安徽农业大学科目三继续教育自动刷课(免费版) // @namespace ahnyjy/auto // @version 6.0.0 // @author 道道龙 // @description 【免费版】安徽公需课-安徽农业大学科目三继续教育自动刷课,列表页读取全部课程,播放页自动倍速,播完关闭,列表页自动继续下一课 // @match https://www.ahnyjy.cn/* // @match https://ilearn.cfyedu.com/student/* // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_deleteValue // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; var IS_AH_LIST = /ahnyjy\.cn\/course\/center\/detail/.test(location.href); var IS_AH_PREVIEW = /ahnyjy\.cn\/course\/preview/.test(location.href); var IS_AH_OTHER = /ahnyjy\.cn/.test(location.href); var IS_IL = /ilearn\.cfyedu\.com/.test(location.href); var K_COURSES = 'ahnyjy5_courses'; var K_CURSOR = 'ahnyjy5_cursor'; var K_SPEED = 'ahnyjy5_speed'; var K_RUNNING = 'ahnyjy5_running'; var K_PHASE = 'ahnyjy5_phase'; // 'idle' | 'opening' | 'playing' | 'done' function log() { console.log('[AHNYJY-v5] ' + Array.prototype.slice.call(arguments).join(' ')); } function sleep(ms) { return new Promise(function(r) { setTimeout(r, ms); }); } function $1(s) { return document.querySelector(s); } function $A(s) { return Array.prototype.slice.call(document.querySelectorAll(s)); } function cfg(k, f) { try { var v = GM_getValue(k); return v !== undefined ? v : f; } catch(e) { return f; } } function setCfg(k, v) { try { GM_setValue(k, v); } catch(e) {} } function fmt(s) { if (!s || isNaN(s)) return '--:--'; return String(Math.floor(s/60)).padStart(2,'0') + ':' + String(Math.floor(s%60)).padStart(2,'0'); } // ================================================================ // 列表页 UI // ================================================================ function createListUI() { var old = $1('#ah5-panel'); if (old) { try { old.parentNode.removeChild(old); } catch(e) {} } if ($1('#ah5-panel')) return; var html = '
' + '
' + '🎓' + '安徽继续教育刷课助手' + 'FREE' + 'v6.0
' + '
准备中...
' + '
' + '' + '
' + '' + '' + '' + '' + '
' + '
' + '倍速' + '' + '
' + '' + '
' + 'ℹ️ 读取全部课程 → 开始刷课 → 播放页自动播放倍速 → 播完关闭 → 自动继续下一课
' + '⚠️ 免费版全部强制播放;代刷8元 👉 微信 wkds995
'; document.body.insertAdjacentHTML('beforeend', html); $1('#ah5-speed').value = String(cfg(K_SPEED, 2)); $1('#ah5-speed').onchange = function() { setCfg(K_SPEED, Number(this.value)); }; $1('#ah5-btn-read').onclick = function() { readAllCourses(); }; $1('#ah5-btn-start').onclick = function() { startAuto(); }; $1('#ah5-btn-stop').onclick = function() { stopAuto(); }; $1('#ah5-btn-next').onclick = function() { openNextCourse(); }; } function setStatus(text, sub) { var el = $1('#ah5-status'); if (el) el.textContent = text; var sub_el = $1('#ah5-sub'); if (sub_el) sub_el.textContent = sub || ''; } function setProgress(cur, total, label) { var wrap = $1('#ah5-pbar'); if (!wrap) return; var pct = total > 0 ? Math.round(cur / total * 100) : 0; wrap.style.display = 'block'; var fill = $1('#ah5-pfill'); var lbl = $1('#ah5-plabel'); var pct_el = $1('#ah5-ppct'); if (fill) fill.style.width = pct + '%'; if (lbl) lbl.textContent = label || (cur + ' / ' + total); if (pct_el) pct_el.textContent = pct + '%'; } function syncButtons() { var running = cfg(K_RUNNING, false); var courses = cfg(K_COURSES, []); var startBtn = $1('#ah5-btn-start'); var stopBtn = $1('#ah5-btn-stop'); var readBtn = $1('#ah5-btn-read'); var nextBtn = $1('#ah5-btn-next'); if (startBtn) { startBtn.disabled = running || courses.length === 0; startBtn.style.opacity = (running || courses.length === 0) ? '0.5' : '1'; } if (stopBtn) { stopBtn.disabled = !running; stopBtn.style.background = running ? '#ef4444' : '#475569'; stopBtn.style.cursor = running ? 'pointer' : 'not-allowed'; } if (readBtn) { readBtn.disabled = running; readBtn.style.opacity = running ? '0.5' : '1'; } if (nextBtn) { nextBtn.disabled = running; nextBtn.style.opacity = running ? '0.5' : '1'; } } function renderList(courses, cursor) { var list = $1('#ah5-list'); if (!list || !courses || courses.length === 0) { if (list) list.style.display = 'none'; return; } list.style.display = 'block'; list.innerHTML = '
' + '课程列表(' + courses.length + '门)· 已完成 ' + cursor + ' 门
'; courses.forEach(function(c, i) { var done = i < cursor; var cur = i === cursor; var div = document.createElement('div'); div.style.cssText = 'font-size:11px;padding:3px 4px;color:' + (done ? '#10b981' : cur ? '#fbbf24' : '#94a3b8') + ';' + 'cursor:' + (cur ? 'pointer' : 'default') + ';' + 'white-space:nowrap;overflow:hidden;text-overflow:ellipsis;'; var icon = done ? '✅ ' : cur ? '👉 ' : ' '; div.textContent = icon + (i+1) + '. ' + c.courseName.substring(0, 28) + ' [' + (c.classHour || '?') + 'h] ' + (c.learnRate || 0) + '%'; if (cur) { div.style.background = 'rgba(251,191,36,.1)'; div.style.borderRadius = '4px'; div.onclick = function() { openNextCourse(); }; div.title = '点击手动打开'; } list.appendChild(div); }); } // ================================================================ // 列表页:读取课程(支持分页) // ================================================================ async function readAllCourses() { setStatus('正在读取课程...', ''); var allCourses = []; var totalInput = $1('.el-pagination .el-input__inner[type="number"]'); var totalPages = totalInput ? parseInt(totalInput.getAttribute('max') || '1') : 1; var currentPager = $1('.el-pagination .el-pager'); var currentActive = currentPager ? currentPager.querySelector('.number.active') : null; var currentPage = currentActive ? parseInt(currentActive.textContent.trim()) : 1; log('total pages:', totalPages, 'current:', currentPage); if (currentPage !== 1) { var firstBtn = $1('.el-pagination .el-pager .number'); if (firstBtn) { var evt = new MouseEvent('click', { bubbles: true, cancelable: true, view: window }); firstBtn.dispatchEvent(evt); await sleep(1500); } } while (true) { currentPager = $1('.el-pagination .el-pager'); currentActive = currentPager ? currentPager.querySelector('.number.active') : null; currentPage = currentActive ? parseInt(currentActive.textContent.trim()) : 1; var pageCourses = readPageCourses(); log('page', currentPage, ':', pageCourses.length, 'courses'); var existingIds = new Set(allCourses.map(function(c) { return c.courseId + '_' + c.clazzId; })); pageCourses.forEach(function(c) { var key = c.courseId + '_' + c.clazzId; if (!existingIds.has(key)) { allCourses.push(c); existingIds.add(key); } }); setStatus('第' + currentPage + '页 / 共' + totalPages + '页', allCourses.length + '门已读取'); setProgress(currentPage, totalPages, allCourses.length + '门已读取'); if (currentPage >= totalPages) break; var nextBtn = $1('.el-pagination .btn-next'); if (!nextBtn || nextBtn.disabled) break; nextBtn.click(); await sleep(1500); } if (allCourses.length === 0) { setStatus('未找到课程数据', ''); return; } setCfg(K_COURSES, allCourses); setCfg(K_CURSOR, 0); setCfg(K_RUNNING, false); setCfg(K_PHASE, 'idle'); setStatus('已读取 ' + allCourses.length + ' 门课程', '免费版:全部强制播放'); setProgress(0, allCourses.length, '待播放'); renderList(allCourses, 0); syncButtons(); log('total:', allCourses.length, 'courses'); allCourses.forEach(function(c, i) { log(' ' + (i+1) + '. ' + c.courseName + ' [clazzId:' + c.clazzId + ' courseId:' + c.courseId + ']'); }); } function readPageCourses() { var vueApps = []; document.querySelectorAll('*').forEach(function(el) { if (el.__vue__) vueApps.push(el.__vue__); }); for (var i = 0; i < vueApps.length; i++) { var vue = vueApps[i]; var data = vue.$data || vue._data; if (!data) continue; var courseList = null; if (data.clazz && data.clazz.clazzCourses) courseList = data.clazz.clazzCourses; else if (data.clazzCourses) courseList = data.clazzCourses; if (courseList && courseList.length > 0) { return courseList.map(function(c) { var activityNodes = []; try { activityNodes = JSON.parse(c.activityNodes || '[]'); } catch(e) {} return { clazzId: c.clazzId, courseId: c.courseId, onlineCourseId: c.onlineCourseId, courseStudentId: c.courseStudentId, courseName: c.courseName || '', classHour: c.classHour || 0, learnRate: c.learnRate || 0, status: c.status || '', activityNodes: activityNodes }; }); } } return []; } // ================================================================ // 列表页:开始 / 停止 / 下一课 // ================================================================ function openNextCourse() { var courses = cfg(K_COURSES, []); var cursor = cfg(K_CURSOR, 0); if (courses.length === 0) { setStatus('请先读取课程', ''); return; } if (cursor >= courses.length) { setStatus('🎉 全部 ' + courses.length + ' 门刷完!', ''); setCfg(K_RUNNING, false); syncButtons(); return; } var c = courses[cursor]; var url = 'https://www.ahnyjy.cn/course/preview/' + c.courseId + '?clazzId=' + c.clazzId; log('open:', c.courseName, '->', url); setCfg(K_RUNNING, true); setCfg(K_PHASE, 'opening'); setStatus('(' + (cursor+1) + '/' + courses.length + ') ' + c.courseName.substring(0, 25), '正在打开...'); window.open(url, '_blank'); setProgress(cursor + 1, courses.length, '播放中'); syncButtons(); } function startAuto() { var courses = cfg(K_COURSES, []); if (courses.length === 0) { setStatus('请先读取课程', ''); return; } setCfg(K_RUNNING, true); setStatus('自动刷课已开始', '免费版:全部强制播放'); syncButtons(); openNextCourse(); } function stopAuto() { setCfg(K_RUNNING, false); setCfg(K_PHASE, 'idle'); setStatus('已停止', ''); syncButtons(); } // 列表页:持续轮询,等待播放页更新cursor function setupListPolling() { var prevCursor = cfg(K_CURSOR, 0); setInterval(function() { var cursor = cfg(K_CURSOR, 0); var running = cfg(K_RUNNING, false); var courses = cfg(K_COURSES, []); var phase = cfg(K_PHASE, 'idle'); // 检测cursor变化(播放页完成了一个课程) if (cursor > prevCursor) { log('cursor changed:', prevCursor, '->', cursor); prevCursor = cursor; setProgress(cursor, courses.length, '已完成'); renderList(courses, cursor); // 播完了 if (cursor >= courses.length) { setStatus('🎉 全部 ' + courses.length + ' 门刷完!', ''); setCfg(K_RUNNING, false); setCfg(K_PHASE, 'done'); syncButtons(); return; } // 自动继续下一课(cursor变化说明播放页刚关闭,强制设running=true) setCfg(K_RUNNING, true); setStatus('(' + (cursor+1) + '/' + courses.length + ') 自动打开下一课...', ''); setTimeout(function() { if (cfg(K_RUNNING, false)) { openNextCourse(); } }, 2500); } }, 2000); } // ================================================================ // 播放页 UI(只监控,不控制播放) // ================================================================ function createPlayUI() { var old = $1('#ah5-panel'); if (old) { try { old.parentNode.removeChild(old); } catch(e) {} } if ($1('#ah5-panel')) return; var html = '
' + '
' + '🎬' + '播放页监控' + 'FREE' + 'v5.0
' + '
等待视频...
' + '
' + '
' + '
' + '进度
' + '
' + '
' + '
' + '' + '
' + '
' + '倍速' + '' + '' + '' + '
' + '
' + '
' + '🎬 自动监控:检测到视频自动播放,代刷8元 👉 微信 wkds995
'; document.body.insertAdjacentHTML('beforeend', html); var speed = cfg(K_SPEED, 2); var speedEl = $1('#ah5-speed'); speedEl.value = String(speed); speedEl.onchange = function() { var s = Number(this.value); setCfg(K_SPEED, s); applySpeedToVideo(s); }; $1('#ah5-btn-close').onclick = function() { doClose(); }; $1('#ah5-btn-speed-up').onclick = function() { changeSpeed(1); }; $1('#ah5-btn-speed-down').onclick = function() { changeSpeed(-1); }; } function setPlayStatus(text, sub) { var el = $1('#ah5-status'); if (el) el.textContent = text; var sub_el = $1('#ah5-sub'); if (sub_el) sub_el.textContent = sub || ''; } function setPlayProgress(pct) { var fill = $1('#ah5-pfill'); var pct_el = $1('#ah5-ppct'); if (fill) fill.style.width = pct + '%'; if (pct_el) pct_el.textContent = pct + '%'; } function addLog(msg) { var logEl = $1('#ah5-log'); if (!logEl) return; var ts = new Date().toLocaleTimeString(); var line = document.createElement('div'); line.textContent = '[' + ts.substring(3, 8) + '] ' + msg; logEl.appendChild(line); while (logEl.children.length > 5) logEl.removeChild(logEl.firstChild); } // ================================================================ // 播放页:视频播放 + 倍速 + 关闭逻辑 // 策略: // 1. 有多个视频 → 都监控,暂停就点播放 // 2. 进度100%不代表完成 → 等"已经是最后一个任务了"出现才算完成 // ================================================================ var currentSpeed = 2; var currentVideo = null; var monitorTimer = null; function changeSpeed(dir) { var speeds = [1, 1.5, 2, 3, 5]; var idx = speeds.indexOf(currentSpeed); var newIdx = Math.max(0, Math.min(speeds.length - 1, idx + dir)); currentSpeed = speeds[newIdx]; setCfg(K_SPEED, currentSpeed); var el = $1('#ah5-speed'); if (el) el.value = String(currentSpeed); // 同步到所有Video.js播放器 if (typeof videojs !== 'undefined') { videojs.getAllPlayers().forEach(function(p) { try { p.playbackRate(currentSpeed); } catch(e) {} }); } addLog('倍速: ' + currentSpeed + 'x'); } // 点击大播放按钮(触发用户互动,解除浏览器拦截) function clickBigPlayButton() { var btn = $1('.vjs-big-play-button'); if (!btn) { addLog('clickBigPlayButton: 按钮不存在'); return false; } try { btn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); addLog('已点击 .vjs-big-play-button'); return true; } catch(e) { addLog('点击btn失败: ' + e.message); return false; } } // 让所有Video.js播放器播放 function playAllPlayers() { if (typeof videojs === 'undefined') return; videojs.getAllPlayers().forEach(function(p) { try { p.play().catch(function() {}); } catch(e) {} }); } // 关闭播放页:更新cursor并关闭窗口 async function doClose() { if (monitorTimer) { clearInterval(monitorTimer); monitorTimer = null; } addLog('doClose: 更新cursor'); var cursor = cfg(K_CURSOR, 0); cursor++; setCfg(K_CURSOR, cursor); setCfg(K_RUNNING, false); setCfg(K_PHASE, 'done'); addLog('cursor -> ' + cursor + ',准备关闭'); await sleep(500); window.close(); } // 检测页面是否出现"已经是最后一个任务了"提示 function checkLastTaskHint() { var all = document.querySelectorAll('*'); for (var i = 0; i < all.length; i++) { var el = all[i]; if (el.children.length > 0) continue; var txt = (el.textContent || '').replace(/\s+/g, ''); if (txt.indexOf('已经是最后一个任务了') !== -1) { return true; } } return false; } // 获取页面所有视频元素 function getAllVideos() { var videos = []; document.querySelectorAll('video').forEach(function(v) { if (document.contains(v)) videos.push(v); }); return videos; } // 主监控逻辑(每2秒执行一次) function monitorTick() { var videos = getAllVideos(); if (videos.length === 0) { addLog('本页无video元素'); setPlayStatus('等待视频加载...', ''); return; } // 汇总进度 var totalDur = 0, totalCur = 0, allEnded = true, allPaused = true; videos.forEach(function(v) { totalDur += v.duration || 0; totalCur += v.currentTime || 0; if (v.duration > 5) { if (!v.ended && v.currentTime < v.duration - 0.5) allEnded = false; if (!v.paused) allPaused = false; } }); var pct = totalDur > 0 ? Math.round(totalCur / totalDur * 100) : 0; var hint = allEnded ? ' [全部播完]' : ''; setPlayStatus('🎬 ' + currentSpeed + 'x' + hint, '已看 ' + pct + '% (' + videos.length + '个视频)'); setPlayProgress(pct); // 信号1:出现"最后一个任务"提示 = 全部完成 if (checkLastTaskHint()) { addLog('检测到"最后一个任务",全部完成!'); setPlayStatus('✅ 全部完成!5秒后关闭...', ''); setTimeout(function() { doClose(); }, 5000); return; } // 信号2:有视频暂停了 → 点击播放 if (allPaused) { addLog('视频暂停,点击播放'); clickBigPlayButton(); playAllPlayers(); } // 信号3:所有视频都ended了但还没出提示 → 点击播放按钮 + 立刻再次检查提示 if (allEnded && videos[0].duration > 5) { addLog('所有视频播完,点击播放触发完成检测'); setPlayStatus('⏳ 等最后一个任务提示...', ''); clickBigPlayButton(); playAllPlayers(); // 点击后立刻再检查一次提示(避免等下一个2秒tick) if (checkLastTaskHint()) { addLog('点击后立即检测到提示,关闭!'); setPlayStatus('✅ 全部完成!5秒后关闭...', ''); setTimeout(function() { doClose(); }, 5000); return; } } } // 开始监控 function startMonitor(video) { if (monitorTimer) clearInterval(monitorTimer); addLog('startMonitor()'); if (video) { currentVideo = video; addLog('监控指定video'); } // 立即播放 clickBigPlayButton(); playAllPlayers(); // 设倍速到所有player if (typeof videojs !== 'undefined') { videojs.getAllPlayers().forEach(function(p) { try { p.playbackRate(currentSpeed); } catch(e) {} }); } monitorTimer = setInterval(monitorTick, 2000); } // ================================================================ // 初始化 // ================================================================ // ================================================================ // 路由检测:注入页面,捕获所有SPA导航 // ================================================================ (function() { if (window._ahRouteInjected) return; window._ahRouteInjected = true; var last = location.pathname; var lastHash = location.hash; function onRoute() { var p = location.pathname; var h = location.hash; if (p === last && h === lastHash) return; last = p; lastHash = h; // postMessage通知主脚本 window.postMessage({ type: 'AH_ROUTE_CHANGE', path: p + h }, '*'); } // 拦截所有导航API var _push = history.pushState; history.pushState = function() { _push.apply(history, arguments); onRoute(); }; var _rep = history.replaceState; history.replaceState = function() { _rep.apply(history, arguments); onRoute(); }; window.addEventListener('popstate', onRoute); // 兜底轮询(SPA可能不触发任何事件) setInterval(onRoute, 800); // hash路由也监控 setInterval(function() { if (location.hash !== lastHash) onRoute(); }, 800); })(); function init() { if (IS_AH_LIST) { createListUI(); var courses = cfg(K_COURSES, []); var cursor = cfg(K_CURSOR, 0); if (courses.length > 0) { setStatus('已读取 ' + courses.length + ' 门课程', '免费版:全部强制播放'); setProgress(cursor, courses.length, '待播放'); renderList(courses, cursor); } else { setStatus('请点击"读取全部课程"', ''); } syncButtons(); setupListPolling(); } else if (IS_AH_PREVIEW) { setStatus('等待跳转到播放页...', ''); var t0 = Date.now(); var iv = setInterval(function() { if (/ilearn/.test(location.href)) { clearInterval(iv); setTimeout(function() { location.reload(); }, 1500); } else if (Date.now() - t0 > 90000) { clearInterval(iv); setStatus('跳转超时,请手动刷新', ''); } }, 1000); } else if (IS_IL) { createPlayUI(); currentSpeed = cfg(K_SPEED, 2); findAndActivate(); } else if (IS_AH_OTHER) { createWaitUI(); setupRouteWatch(); } } // ================================================================ // postMessage 接收路由变化通知 // ================================================================ window.addEventListener('message', function(e) { if (!e.data || e.data.type !== 'AH_ROUTE_CHANGE') return; var path = e.data.path || ''; var isList = /ahnyjy\.cn\/course\/center\/detail/.test(location.href); var isOtherAH = /ahnyjy\.cn/.test(location.href) && !isList; if (isList) { // 进入列表页 → 重新加载 var panel = $1('#ah5-panel'); if (panel) { try { panel.parentNode.removeChild(panel); } catch(err) {} } setTimeout(function() { location.reload(); }, 200); } else if (isOtherAH) { // 离开列表页 → 显示等待UI var panel = $1('#ah5-panel'); if (panel) { try { panel.parentNode.removeChild(panel); } catch(err) {} } setTimeout(function() { createWaitUI(); setupRouteWatch(); }, 200); } }); // ================================================================ // 首页提示UI + 路由监控 // ================================================================ function createWaitUI() { var old = $1('#ah5-panel'); if (old) { try { old.parentNode.removeChild(old); } catch(e) {} } if ($1('#ah5-panel')) return; var html = '
' + '
' + '🎓' + '安徽继续教育刷课助手' + 'FREE' + 'v6.0
' + '
' + '
⚠️ 请进入课程列表页
' + '
点击菜单"个人中心"后"点击需学习课程",' + '进入课程列表页后,本助手将自动激活。
' + '
' + '目标页面示例:' + location.host + '/course/center/detail/{clazzId}
' + '
等待进入课程列表页...
' + '
' + 'ℹ️ 读取全部课程 → 开始刷课 → 播放页自动播放倍速 → 播完关闭 → 自动继续下一课
' + '⚠️ 免费版全部强制播放;代刷8元 👉 微信 wkds995
'; document.body.insertAdjacentHTML('beforeend', html); } function setWaitStatus(text) { var el = $1('#ah5-status'); if (el) el.textContent = text; } // 路由监控:监听 SPA 路由变化 + 定期检查 URL var routeWatchTimer = null; var lastKnownPath = location.pathname; function setupRouteWatch() { // 先清理旧的 if (routeWatchTimer) { clearInterval(routeWatchTimer); routeWatchTimer = null; } window.removeEventListener('popstate', onRouteChange); // popstate:浏览器前进后退 window.addEventListener('popstate', onRouteChange); // pushState/replaceState:Vue路由跳转(SPA核心) var _pushState = history.pushState; var _replaceState = history.replaceState; history.pushState = function() { _pushState.apply(history, arguments); onRouteChange(); }; history.replaceState = function() { _replaceState.apply(history, arguments); onRouteChange(); }; // 兜底轮询 routeWatchTimer = setInterval(onRouteChange, 1000); } function onRouteChange() { var currentPath = location.pathname + location.hash; if (currentPath === lastKnownPath) return; lastKnownPath = currentPath; var nowIsList = /ahnyjy\.cn\/course\/center\/detail/.test(location.href); var nowIsAH = /ahnyjy\.cn/.test(location.href); // 进入或离开课程列表页 → 强制重新加载脚本 // 这是最可靠的SPA导航处理方式 if (nowIsList) { clearInterval(routeWatchTimer); window.removeEventListener('popstate', onRouteChange); location.reload(); } else if (nowIsAH) { // 其他ahnyjy页面 → 移除旧面板,重新显示等待UI clearInterval(routeWatchTimer); window.removeEventListener('popstate', onRouteChange); var panel = $1('#ah5-panel'); if (panel) { try { panel.parentNode.removeChild(panel); } catch(e) {} } setTimeout(function() { createWaitUI(); setupRouteWatch(); }, 100); } } // ================================================================ // 播放页:查找播放器并激活 // ================================================================ function findAndActivate() { addLog('findAndActivate() 启动, speed=' + currentSpeed + 'x'); function phase2() { if (tryFindAndPlay()) return; setTimeout(function() { if (tryFindAndPlay()) return; setTimeout(function() { if (tryFindAndPlay()) return; addLog('未找到播放器,请手动播放'); setPlayStatus('未找到播放器', '请手动播放'); }, 1000); }, 500); } function phase1() { var c = $1('.video-js'); if (c) { addLog('.video-js 容器已就绪'); setTimeout(phase2, 2000); } else { setTimeout(phase1, 300); } } phase1(); } // ================================================================ // 播放页:Video.js播放器查找 + 播放 // ================================================================ function tryFindAndPlay() { addLog('tryFindAndPlay()'); // 方式1:videojs.getAllPlayers() if (typeof videojs !== 'undefined') { var players = videojs.getAllPlayers(); addLog('getAllPlayers: ' + players.length); if (players.length > 0) { playViaVideoJS(players[0]); return true; } } else { addLog('videojs 全局不存在'); } // 方式2:.video-js容器下的video标签 var c = $1('.video-js'); if (c) { var vs = c.querySelectorAll('video'); addLog('.video-js下video: ' + vs.length); if (vs.length > 0) { playNativeVideo(vs[0]); return true; } } // 方式3:页面任意video var v = $1('video'); addLog('任意video: ' + (v ? '有' : '无')); if (v) { playNativeVideo(v); return true; } return false; } function playViaVideoJS(player) { addLog('playViaVideoJS()'); if (typeof player.ready === 'function') { player.ready(function() { doPlay(player); }); } else { doPlay(player); } } function doPlay(player) { // 获取video标签 var v = null; try { v = player.tech_ && player.tech_.el_; } catch(e) {} if (!v || v.tagName !== 'VIDEO') { try { var all = player.el_.querySelectorAll('video'); if (all.length > 0) v = all[0]; } catch(e) {} } if (!v) { addLog('doPlay: 无法获取video'); startMonitor(null); return; } addLog('video: tag=' + v.tagName + ' readyState=' + v.readyState); currentVideo = v; // 设倍速 try { player.playbackRate(currentSpeed); addLog('player.rate -> ' + currentSpeed + 'x'); } catch(e) {} try { v.playbackRate = currentSpeed; addLog('v.rate -> ' + currentSpeed + 'x'); } catch(e) {} // 锁定playbackRate lockRate(v, currentSpeed); // 点击大按钮建立用户互动 clickBigPlayButton(); // 播放 try { player.play().then(function() { addLog('player.play() OK'); startMonitor(v); }).catch(function(e) { addLog('player.play() 阻止: ' + e.message.substring(0, 50)); startMonitor(v); }); } catch(e) { addLog('player.play() 异常: ' + e.message); startMonitor(v); } } function playNativeVideo(v) { addLog('playNativeVideo() readyState=' + v.readyState); currentVideo = v; lockRate(v, currentSpeed); clickBigPlayButton(); v.play().then(function() { addLog('v.play() OK'); startMonitor(v); }).catch(function(e) { addLog('v.play() 阻止: ' + e.message.substring(0, 50)); startMonitor(v); }); } function lockRate(video, speed) { if (!video) return; try { Object.defineProperty(video, 'playbackRate', { get: function() { return speed; }, set: function(v2) { /* 忽略外部设置,保持倍速 */ } }); video.playbackRate = speed; addLog('lockRate OK'); } catch(e) { addLog('lockRate失败,用轮询同步'); var si = setInterval(function() { if (!document.contains(video)) { clearInterval(si); return; } if (Math.abs(video.playbackRate - speed) > 0.05) { video.playbackRate = speed; } }, 3000); video._rateSync = si; } } function clickBigPlayButton() { var btn = $1('.vjs-big-play-button'); if (!btn) { addLog('clickBtn: 不存在'); return; } try { btn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); addLog('已点击大播放按钮'); } catch(e) { addLog('点击失败: ' + e.message); try { btn.click(); addLog('btn.click() 备用成功'); } catch(e2) { addLog('btn.click() 也失败'); } } } // ================================================================ // 播放页:视频播放 + 倍速 + 关闭逻辑 // 策略: // 1. 有多个视频 → 都监控,暂停就点播放 // 2. 进度100%不代表完成 → 等"已经是最后一个任务了"出现才算完成 // ================================================================ var currentSpeed = 2; var currentVideo = null; var monitorTimer = null; function changeSpeed(dir) { var speeds = [1, 1.5, 2, 3, 5]; var idx = speeds.indexOf(currentSpeed); var newIdx = Math.max(0, Math.min(speeds.length - 1, idx + dir)); currentSpeed = speeds[newIdx]; setCfg(K_SPEED, currentSpeed); var el = $1('#ah5-speed'); if (el) el.value = String(currentSpeed); if (typeof videojs !== 'undefined') { videojs.getAllPlayers().forEach(function(p) { try { p.playbackRate(currentSpeed); } catch(e) {} }); } addLog('倍速: ' + currentSpeed + 'x'); } // 关闭播放页 async function doClose() { if (monitorTimer) { clearInterval(monitorTimer); monitorTimer = null; } var cursor = cfg(K_CURSOR, 0); cursor++; setCfg(K_CURSOR, cursor); setCfg(K_RUNNING, false); setCfg(K_PHASE, 'done'); addLog('cursor -> ' + cursor + ',准备关闭'); await sleep(500); window.close(); } // 检测"最后一个任务"提示 function checkLastTaskHint() { var all = document.querySelectorAll('*'); for (var i = 0; i < all.length; i++) { var el = all[i]; if (el.children.length > 0) continue; var txt = (el.textContent || '').replace(/\s+/g, ''); if (txt.indexOf('已经是最后一个任务了') !== -1) return true; } return false; } // 获取所有视频 function getAllVideos() { var vs = []; document.querySelectorAll('video').forEach(function(v) { if (document.contains(v)) vs.push(v); }); return vs; } // 监控循环(每2秒) function monitorTick() { var videos = getAllVideos(); if (videos.length === 0) { addLog('无video元素'); setPlayStatus('等待视频加载...', ''); return; } var totalDur = 0, totalCur = 0, allEnded = true, allPaused = true; videos.forEach(function(v) { totalDur += v.duration || 0; totalCur += v.currentTime || 0; if (v.duration > 5) { if (!v.ended && v.currentTime < v.duration - 0.5) allEnded = false; if (!v.paused) allPaused = false; } }); var pct = totalDur > 0 ? Math.round(totalCur / totalDur * 100) : 0; var hint = allEnded ? ' [全部播完]' : ''; setPlayStatus('🎬 ' + currentSpeed + 'x' + hint, '已看 ' + pct + '% (' + videos.length + '个)'); setPlayProgress(pct); // 信号1:最后一个任务提示 if (checkLastTaskHint()) { addLog('检测到"最后一个任务",全部完成!'); setPlayStatus('✅ 全部完成!5秒后关闭...', ''); setTimeout(function() { doClose(); }, 5000); return; } // 信号2:视频暂停 → 点击播放 if (allPaused && !allEnded) { addLog('视频暂停,点击播放'); clickBigPlayButton(); if (typeof videojs !== 'undefined') { videojs.getAllPlayers().forEach(function(p) { try { p.play(); } catch(e) {} }); } } // 信号3:所有视频播完但没提示 → 等提示 if (allEnded && videos[0].duration > 5) { addLog('视频全部播完,等提示出现...'); setPlayStatus('⏳ 等最后一个任务提示...', ''); if (typeof videojs !== 'undefined') { videojs.getAllPlayers().forEach(function(p) { try { p.play(); } catch(e) {} }); } } } function startMonitor(video) { if (monitorTimer) clearInterval(monitorTimer); if (video) { currentVideo = video; } else if (currentVideo && document.contains(currentVideo)) { video = currentVideo; } else { addLog('monitor: 无video,等待...'); setTimeout(function() { startMonitor(null); }, 1000); return; } if (video._rateSync) { clearInterval(video._rateSync); video._rateSync = null; } // 重新锁定 try { Object.defineProperty(video, 'playbackRate', { get: function() { return currentSpeed; }, set: function() {} }); } catch(e) {} addLog('startMonitor OK, videos=' + getAllVideos().length); monitorTimer = setInterval(monitorTick, 2000); } if (document.readyState === 'complete') setTimeout(init, 800); else window.addEventListener('load', function() { setTimeout(init, 800); }); })();