// ==UserScript== // @name 宝武学习助手 // @namespace http://tampermonkey.net/ // @version 1.2.7 // @description 1.自动静音、自动播放、自动下一节;2.自动重播完成学习时长; // @author jinxhcn // @match *://learn.baowugroup.com/* // @grant GM_info // @license MIT // @run-at document-idle // ==/UserScript== (function () { 'use strict'; console.log(`✅ ${GM_info.script.name} v${GM_info.script.version}`); function gT() { const n = new Date(); const h = String(n.getHours()).padStart(2, '0'); const m = String(n.getMinutes()).padStart(2, '0'); const s = String(n.getSeconds()).padStart(2, '0'); return h + ':' + m + ':' + s; } const oL = console.log.bind(console); console.log = function () { oL(`[${gT()}]`, ...arguments); }; const oW = console.warn.bind(console); console.warn = function () { oW(`[${gT()}]`, ...arguments); }; const oE = console.error.bind(console); console.error = function () { oE(`[${gT()}]`, ...arguments); }; const C = 2e3; const D = 1e3; const W = 1e3; let s = { a: !1, b: '', c: !1, d: null, e: !1, f: !1, g: 0, h: 0, i: !1, j: !1, k: !1, l: null }; function gV() { return document.querySelector('#xg-video video'); } function eM(video) { if (!video) return !1; if (!video.muted) { video.muted = !0; console.log('🔇 已静音'); return !0; } return !1; } function hNS() { const current = document.querySelector('.el-tree-node.is-current'); if (!current) return !1; let node = current; while (node) { let next = node.nextElementSibling; while (next) { if (next.classList.contains('el-tree-node')) return !0; next = next.nextElementSibling; } node = node.parentElement?.closest?.('.el-tree-node'); } return !1; } function cNS() { const candidates = ['播放下一节', '下一节']; for (const text of candidates) { const btns = document.querySelectorAll('button'); for (const btn of btns) { if (btn.offsetParent !== null && (btn.innerText.trim() === text || btn.innerText.includes(text))) { btn.click(); console.log('✅ 已点击"下一节"'); return !0; } } } const hints = document.querySelectorAll('*'); for (const el of hints) { if (el.offsetParent !== null && el.innerText?.trim() === '本节播放结束, 是否继续播放?') { const container = el.closest('.play-chapter') || el.parentElement; if (container) { const btn = container.querySelector('button'); if (btn && btn.offsetParent !== null) { btn.click(); console.log('✅ 已点击"下一节"(通过结束提示)'); return !0; } } break; } } const primaryBtns = document.querySelectorAll('.el-button--primary'); for (const btn of primaryBtns) { if (btn.offsetParent !== null && btn.innerText.includes('下一节')) { btn.click(); console.log('✅ 已点击"下一节"(Primary 按钮)'); return !0; } } console.warn('⚠️ 未找到"下一节"按钮'); return !1; } function cRB() { const containers = document.querySelectorAll('.play-chapter'); for (const container of containers) { if (container.offsetParent !== null) { const btn = container.querySelector('button'); if (btn && btn.offsetParent !== null && btn.innerText.includes('重新播放')) { btn.click(); console.log('🔄 已点击"重新播放"按钮(时长未达标)'); return !0; } } } const btns = document.querySelectorAll('button'); for (const btn of btns) { if (btn.offsetParent !== null && btn.innerText.trim() === '重新播放') { btn.click(); console.log('🔄 已点击"重新播放"按钮(备用查找)'); return !0; } } console.warn('⚠️ 未找到"重新播放"按钮'); return !1; } function pCV(video) { if (!video) return !1; eM(video); if (video.ended || !video.paused) return !1; const playBtn = document.querySelector('.xgplayer-start'); if (playBtn && playBtn.offsetParent !== null) { playBtn.click(); console.log('▶️ 点击中央播放按钮'); return !0; } const icon = document.querySelector('.xgplayer-play .xgplayer-icon'); if (icon && icon.offsetParent !== null) { icon.click(); console.log('▶️ 点击控制栏播放图标'); return !0; } const container = document.querySelector('#xg-video, .xgplayer'); if (container) { container.click(); console.log('▶️ 点击视频画面区域'); return !0; } video.play().catch(() => { }); console.log('▶️ 调用原生 video.play()'); return !0; } function rPS() { clearTimeout(s.d); if (s.l) { clearTimeout(s.l); s.l = null; } s.k = !1; s.a = !1; s.c = !0; s.e = !1; s.d = setTimeout(() => { s.c = !1; if (!document.hidden) { const video = gV(); if (video) { eM(video); if (!s.k) { s.k = !0; pCV(video); s.l = setTimeout(() => { s.k = !1; s.l = null; }, 1500); } } } }, 300); } function eD(dialog) { const rows = dialog.querySelectorAll('tr.vxe-body--row'); let total = 0, learned = 0; for (const row of rows) { const cells = row.querySelectorAll('td.vxe-body--column'); if (cells.length < 3) continue; const firstText = cells[0].textContent.trim(); if (firstText.includes('学习时长')) { let match = firstText.match(/[((]\s*(\d+\.?\d*)\s*分钟[))]/); if (!match) { match = firstText.match(/(\d+\.?\d*)\s*分钟/); } if (match) { total = parseFloat(match[1]); } const thirdText = cells[2].textContent.trim(); const learnedMatch = thirdText.match(/(\d+\.?\d*)\s*分钟/); if (learnedMatch) { learned = parseFloat(learnedMatch[1]); } break; } } console.log(`📊 提取结果:总 ${total} 分钟,已学 ${learned} 分钟`); return { total, learned }; } function fP() { return new Promise((resolve) => { const buttons = document.querySelectorAll('button'); let targetBtn = null; for (const btn of buttons) { if (btn.offsetParent !== null && btn.innerText.trim() === '完成情况') { targetBtn = btn; break; } } if (!targetBtn) { console.warn('⚠️ 未找到"完成情况"按钮'); resolve(null); return; } targetBtn.click(); console.log('🔍 点击"完成情况"按钮,获取学习进度'); let attempts = 0; const maxAttempts = D / 200; const interval = setInterval(() => { attempts++; let dialog = null; const dialogs = document.querySelectorAll('.el-dialog'); for (const d of dialogs) { const header = d.querySelector('.el-dialog__header .text'); if (header && header.textContent.trim() === '完成情况') { dialog = d; break; } } if (!dialog) { const allDialogs = document.querySelectorAll('[role="dialog"]'); for (const d of allDialogs) { const label = d.getAttribute('aria-label'); if (label === '完成情况') { dialog = d; break; } } } if (dialog) { clearInterval(interval); setTimeout(() => { let data = eD(dialog); if (data.total === 0) { setTimeout(() => { data = eD(dialog); cD(dialog); resolve(data); }, 500); } else { cD(dialog); resolve(data); } }, W); return; } if (attempts >= maxAttempts) { clearInterval(interval); console.warn('⚠️ 等待"完成情况"弹窗超时'); resolve(null); } }, 200); }); } function cD(dialog) { const closeBtn = dialog.querySelector('.el-dialog__headerbtn'); if (closeBtn) { closeBtn.click(); console.log('🔒 已关闭"完成情况"弹窗'); } else { const overlay = dialog.closest('.el-overlay'); if (overlay) { const mask = overlay.querySelector('.el-overlay-dialog'); if (mask) mask.click(); } } } async function cR() { if (s.f) return; s.f = !0; try { const data = await fP(); if (!data || data.total === 0) { console.warn('⚠️ 获取学习时长数据无效,稍后重试'); s.j = !1; return; } s.g = data.total; s.h = data.learned; const video = gV(); if (!video) return; if (video.ended && !hNS()) { if (s.h < s.g) { console.log(`⏰ 学习时长不足(${s.h}/${s.g}),尝试重新播放`); const clicked = cRB(); if (clicked) { rPS(); s.a = !1; s.j = !1; s.i = !1; } else { s.j = !1; } } else { console.log(`✅ 学习时长已达标(${s.h}/${s.g}),停止自动操作`); s.i = !0; } } } catch (e) { console.error('❌ 检查学习时长时出错:', e); s.j = !1; } finally { s.f = !1; } } function hCD() { const overlays = document.querySelectorAll('.el-overlay'); for (const ov of overlays) { if (getComputedStyle(ov).display === 'none') continue; const dialog = ov.querySelector('.el-dialog'); if (!dialog) continue; const header = dialog.querySelector('.el-dialog__header'); if (header && header.textContent.includes('温馨提示')) { const continueBtn = dialog.querySelector('.el-button--primary'); if (continueBtn && continueBtn.innerText.trim() === '继续') { continueBtn.click(); console.log('✅ 检测到专注提醒弹窗,点击"继续"'); return; } } } } function mL() { hCD(); if (s.i) return; const video = gV(); if (video) { const currentSrc = video.currentSrc || video.src; if (currentSrc && currentSrc !== s.b) { s.b = currentSrc; rPS(); } } if (video && !video.paused) { eM(video); if (s.k) { clearTimeout(s.l); s.l = null; s.k = !1; } if (s.c) { clearTimeout(s.d); s.c = !1; } if (!s.e) { const startBtn = document.querySelector('.xgplayer-start'); if (startBtn && startBtn.offsetParent !== null) { startBtn.classList.add('hide'); console.log('🎯 隐藏中央按钮'); } const overlay = document.querySelector('.video-overlay'); if (overlay && overlay.offsetParent !== null) { overlay.style.display = 'none'; console.log('🎯 隐藏视频覆盖层'); } s.e = !0; } return; } if (video && video.ended) { if (hNS() && !s.a) { if (cNS()) { s.a = !0; } } else if (!hNS()) { if (!s.j) { s.j = !0; console.log('🏁 所有课程已播放完毕,检查学习时长...'); cR(); } } return; } if (video && video.paused && !s.c) { if (s.k) { console.log('⏳ 播放操作进行中,跳过本次'); return; } const overlay = document.querySelector('.video-overlay'); if (overlay) { overlay.style.display = ''; console.log('🔄 恢复视频覆盖层显示'); } s.e = !1; s.k = !0; if (s.l) { clearTimeout(s.l); s.l = null; } if (pCV(video)) { s.l = setTimeout(() => { s.k = !1; s.l = null; }, 1500); } else { s.k = !1; } return; } if (!s.a) { const hints = document.querySelectorAll('*'); for (const el of hints) { if (el.offsetParent !== null && el.innerText?.trim() === '本节播放结束, 是否继续播放?') { if (hNS()) { if (cNS()) { s.a = !0; } } else { console.log('🏁 检测到结束提示,但无下一节,触发检查'); if (!s.j) { s.j = !0; cR(); } } break; } } } } let n = null; function sL() { if (n) { clearTimeout(n); n = null; } function tick() { mL(); n = setTimeout(tick, C); } n = setTimeout(tick, C); } document.addEventListener('visibilitychange', () => { if (!document.hidden) { if (s.c) { clearTimeout(s.d); s.c = !1; const video = gV(); if (video) { eM(video); if (!s.k) { s.k = !0; if (s.l) { clearTimeout(s.l); s.l = null; } pCV(video); s.l = setTimeout(() => { s.k = !1; s.l = null; }, 1500); } } } setTimeout(mL, 200); } }); sL(); })();