// ==UserScript== // @name 辽宁在线助手 Pro (最终同步版) // @namespace http://tampermonkey.net/ // @version 1.3 // @description 倒计时完美同步:视频结束后,列表页倒计时继承视频剩余时间+5秒缓冲。 // @author AI Assistant // @match *://*lngbzx.gov.cn/* // @grant GM_setValue // @grant GM_getValue // @license MIT // @run-at document-start // ==/UserScript== (function () { 'use strict'; // ===================== 配置 ===================== const CONFIG = { volume: 0, // 视频静音 bufferSecond: 5, // 【关键修改】视频结束后额外增加的缓冲时间(秒) defaultRefresh: 60 // 列表页初始默认刷新间隔(秒) }; // ===================== 全局变量 ===================== const key = 'urlRefreshMap'; const panelPosKey = 'autoRefreshPanelPos_v1'; const currentUrl = location.href; const originalTitle = document.title; // 状态 let isVideoPage = false; let isLearning = false; let videoElement = null; let lastVideoTime = 0; // 刷新控制 let interval = CONFIG.defaultRefresh; // 当前设定的总时长 let timeLeft = CONFIG.defaultRefresh; // 当前剩余时长 let isPaused = false; // UI 元素 let controlPanel = null; let panelStatusEl = null; let panelCountdownEl = null; // ===================== 初始化 ===================== async function init() { // 1. 读取配置 const config = await loadConfig(); if (config[currentUrl] && config[currentUrl] >= 5) { interval = config[currentUrl]; timeLeft = config[currentUrl]; } else { config[currentUrl] = CONFIG.defaultRefresh; await GM_setValue(key, JSON.stringify(config)); } // 2. 创建面板 await createControlPanel(); // 3. 启动主循环 startMainLoop(); console.log('[脚本] 辽宁在线助手 Pro (v1.3) 已启动'); } // ===================== 主循环 (核心) ===================== function startMainLoop() { // 1. 更新状态和UI updateStatus(); // 2. 业务逻辑 handlePageLogic(); // 3. 倒计时处理 // 只有当 NOT 视频页 且 NOT 暂停 时,才减少全局倒计时 if (!isVideoPage && !isPaused) { timeLeft--; } // 4. 刷新判断 if (timeLeft <= 0 && !isPaused) { document.title = `[🔄 刷新中...] ${originalTitle}`; location.reload(); } else { setTimeout(startMainLoop, 1000); } } // ===================== 状态更新 (核心修改点) ===================== function updateStatus() { const video = document.querySelector('video'); isVideoPage = !!video; // --- 视频页逻辑 --- if (isVideoPage && video && !isNaN(video.duration) && video.duration > 0) { videoElement = video; const videoRemaining = Math.ceil(video.duration - video.currentTime); // 视频页状态 if (panelStatusEl) panelStatusEl.textContent = "📺 视频播放中"; if (panelCountdownEl) panelCountdownEl.textContent = formatTime(videoRemaining); document.title = `[📺 剩余: ${formatTime(videoRemaining)}] ${originalTitle}`; // 【关键修改】当视频即将结束时,同步时间到全局倒计时 // 逻辑:如果视频剩余时间 <= 缓冲时间,说明视频马上播完了 // 此时将全局 timeLeft 设置为 (视频剩余时间) // 这样当视频播完(0秒)时,timeLeft 也变成了 0,然后页面跳转到列表页 // 列表页会检测到 timeLeft 很小,从而触发下面的“继承逻辑” if (videoRemaining <= CONFIG.bufferSecond) { timeLeft = videoRemaining; } } // --- 列表页逻辑 --- else { // 【关键修改】继承逻辑 // 如果刚进列表页,发现 timeLeft 很小(<= 缓冲时间),说明是刚从视频页跳过来的 // 此时我们需要把倒计时“补”回来,设置为 缓冲时间 if (timeLeft <= CONFIG.bufferSecond) { timeLeft = CONFIG.bufferSecond; // 同时也更新 interval,防止重置按钮出错 interval = CONFIG.bufferSecond; } // 列表页状态 if (panelStatusEl) panelStatusEl.textContent = "📋 等待课程"; if (panelCountdownEl) panelCountdownEl.textContent = formatTime(timeLeft); document.title = `[⏱️ 刷新: ${formatTime(timeLeft)}] ${originalTitle}`; } } // ===================== 业务逻辑 (自动点击) ===================== function handlePageLogic() { handlePopups(); if (isVideoPage) { controlVideo(); } else { navigateToMyCourses(); autoStartNextCourse(); } } function handlePopups() { const okBtn = document.querySelector("#app > div.bodys.is_cont > div:nth-child(5) > div > div.el-dialog__footer > span > button"); if (okBtn) { const style = window.getComputedStyle(okBtn); if (style.display !== 'none' && style.visibility !== 'hidden') { okBtn.click(); } } } function controlVideo() { if (!videoElement) return; videoElement.volume = CONFIG.volume; if (videoElement.paused) { videoElement.play().catch(() => {}); } // 防卡死 if (videoElement.currentTime === lastVideoTime && videoElement.currentTime > 0) { videoElement.play(); } lastVideoTime = videoElement.currentTime; } function navigateToMyCourses() { if (isVideoPage) return; const myCoursesElement = document.querySelector("#app > div.is_cont > div.wrapper > ul > li.nav_bar.active > div.nav_bar_title"); if (myCoursesElement) { const textContent = myCoursesElement.textContent.trim(); if (!textContent.includes("我的课程") && !textContent.includes("课程")) { const links = document.querySelectorAll('a'); for(let link of links) { if(link.textContent.includes('我的课程') || link.textContent.includes('课程中心')) { link.click(); return; } } } } } function autoStartNextCourse() { if (isVideoPage || isLearning) return; const startBtn = document.querySelector("#app > div.is_cont > div:nth-child(3) > div.wrapper > div > div.content_right > div.content_course > div > ul > li:nth-child(1) > div:nth-child(1) > div > div.course_list_right > div.course_list_right_timer > div.antClick.clearfix > div > div.Save"); if (startBtn) { const style = window.getComputedStyle(startBtn); if (style.display !== 'none' && style.visibility !== 'hidden' && parseFloat(style.opacity) > 0) { console.log('🎯 自动点击开始学习'); startBtn.click(); isLearning = true; } } } // ===================== UI 面板 ===================== async function createControlPanel() { controlPanel = document.createElement('div'); controlPanel.style.cssText = ` position: fixed; bottom: 10px; right: 10px; background: rgba(0,0,0,0.85); color: white; font-size: 14px; padding: 12px; border-radius: 8px; z-index: 999999; font-family: sans-serif; line-height: 1.6; box-shadow: 0 4px 12px rgba(0,0,0,0.3); user-select: none; min-width: 160px; transition: opacity 0.3s; opacity: 1; `; controlPanel.innerHTML = `
🎓 辽宁在线助手 拖动
初始化... 00:00
`; document.body.appendChild(controlPanel); // 恢复位置 const savedPos = await loadPanelPos(); if (savedPos) { controlPanel.style.left = `${savedPos.left}px`; controlPanel.style.top = `${savedPos.top}px`; controlPanel.style.right = 'auto'; controlPanel.style.bottom = 'auto'; } // 绑定元素 panelStatusEl = controlPanel.querySelector('#panelStatus'); panelCountdownEl = controlPanel.querySelector('#countdown'); const pauseBtn = controlPanel.querySelector('#pauseBtn'); const resetBtn = controlPanel.querySelector('#resetBtn'); const dragHandle = controlPanel.querySelector('#dragHandle'); // 按钮事件 pauseBtn.onclick = () => { isPaused = !isPaused; pauseBtn.textContent = isPaused ? '▶️ 继续' : '⏸ 暂停'; pauseBtn.style.background = isPaused ? '#d9534f' : '#444'; }; resetBtn.onclick = () => { // 重置逻辑:如果是视频页,重置为视频长度;否则重置为设定值 if (isVideoPage && videoElement) { timeLeft = Math.ceil(videoElement.duration); } else { // 列表页重置:如果刚同步过,就重置为缓冲时间;否则重置为默认值 if (interval <= CONFIG.bufferSecond) { timeLeft = CONFIG.bufferSecond; } else { timeLeft = interval; } } }; // 拖拽 let dragging = false; dragHandle.addEventListener('mousedown', (e) => { dragging = true; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!dragging) return; controlPanel.style.left = `${e.clientX - 50}px`; controlPanel.style.top = `${e.clientY - 20}px`; controlPanel.style.right = 'auto'; controlPanel.style.bottom = 'auto'; }); document.addEventListener('mouseup', async () => { if (dragging) { dragging = false; const rect = controlPanel.getBoundingClientRect(); GM_setValue(panelPosKey, JSON.stringify({ left: rect.left, top: rect.top })); } }); // 闲置半透明 controlPanel.addEventListener('mouseenter', () => controlPanel.style.opacity = '1'); controlPanel.addEventListener('mouseleave', () => setTimeout(() => { if(!dragging) controlPanel.style.opacity = '0.5'; }, 1000)); controlPanel.style.opacity = '0.5'; } // ===================== 工具函数 ===================== async function loadConfig() { try { return JSON.parse(await GM_getValue(key, '{}')); } catch { return {}; } } async function loadPanelPos() { try { return JSON.parse(await GM_getValue(panelPosKey, '')); } catch { return null; } } function formatTime(t) { if (t <= 0) return "00:00"; const h = Math.floor(t / 3600); const m = Math.floor((t % 3600) / 60); const s = Math.floor(t % 60); return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`; } function pad(n) { return String(n).padStart(2, '0'); } // 启动 init(); })();