// ==UserScript== // @name 绵阳公需课 增强版 v6.4(一键全自动 / 秒过) // @namespace http://tampermonkey.net/ // @version 6.4 // @description 基于站点前端源码:video.js 真实倍速播放;解除 visibilitychange 暂停;可拖动进度条;内置进度上报监视器;节间被服务端时间校验(myd201 code=2)拦截时,按刚学完时长估算等待并刷新重试。一键全自动顺序把每节拖到结尾触发完成上报(myd005)并自动切下一节。真实播放/真实上报,不伪造进度。 // @match https://rsjapp.mianyang.cn/jxjy/pc/* // @grant none // @license GPL-3.0 // ==/UserScript== (function () { 'use strict'; const DEFAULT_SPEED = 16; const MAX_SPEED = 64; const MUTE = true; const ADVANCE_THRESHOLD = 0.95; const ADVANCE_COOLDOWN = 4000; const MAX_WAIT = 3600; // 单次最长等待(秒) const MAX_BLOCK_RETRIES = 8; // 最多被拦重试次数 const END_GAP = 0.4; // 跳结尾时离视频末尾的距离(秒),避免直接设到 duration 边界 let speed = DEFAULT_SPEED; let panel, statusEl, seekTrack, seekFill, progText, speedRange, speedLabel, autoBox, waitBox, autoJumpBox, videoStatusEl, jumpEndBtn, manualBtn, scanBtn, reportEl; let lastVideo = null, lastSrc = '', lastAdvance = 0, seeking = false; let autoJump = false, jumpedSrc = ''; // 一键全自动:把每节拖到结尾触发完成并自动顺序切课 let lastCompletedDuration = 0; // 刚学完那节的时长(用于估算等待) let waitRetryTimer = null, waitCountdownTimer = null, waitDeadline = 0, blocked = false; try { Object.defineProperty(document, 'hidden', { get: () => false, configurable: true }); Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true }); } catch (e) {} // ===================== 进度上报监视器(实测核心) ===================== const ENDPOINT_RE = /lcService\/getData\/(myd00[357]|myd201)\.do/; let reportLines = [], reportSeq = 0; function epName(u) { const m = (u || '').match(/myd\d+\.do/); return m ? m[0].replace('.do', '') : '?'; } function parseCode(t) { try { const o = JSON.parse(t); const c = o && o.resultData && o.resultData.data && o.resultData.data.code; return (c === undefined || c === null) ? '?' : String(c); } catch (e) { return '?'; } } function resetBlockState() { try { sessionStorage.removeItem('gxkWaitEst'); sessionStorage.removeItem('gxkBlockRetries'); } catch (e) {} } function showReport(ep, code, extra) { if (!reportEl) return; const time = new Date().toLocaleTimeString(); let tag = ''; if (ep === 'myd005') tag = (code === '1') ? '✅本课完成' : (code === '0' ? '⚠服务端拒绝' : ''); if (ep === 'myd007') tag = (code === '0') ? '⚠进度非法' : ''; if (ep === 'myd201') tag = (code === '2') ? '⛔时间矛盾·被拦' : (code !== '2' && code !== '?' ? '✓本节放行' : ''); const line = `#${++reportSeq} ${time} ${ep} code=${code}${tag ? ' ' + tag : ''}${extra ? ' ' + extra : ''}`; reportLines.unshift(line); reportLines = reportLines.slice(0, 5); reportEl.innerHTML = reportLines.map(l => '
' + esc(l) + '
').join(''); // 状态联动 if (ep === 'myd005' && code === '1') { setStatus('✅ 本课已上报完成'); resetBlockState(); if (autoJump) setTimeout(() => { if (autoJump) { setStatus('⏭ 本节已上报完成,自动切下一节...'); clickNext(); } }, 1000); } if (ep === 'myd201' && code !== '2' && code !== '?') { resetBlockState(); if (blocked) { blocked = false; setStatus('✓ 节间时间校验已放行,继续学习'); } } if (ep === 'myd201' && code === '2') onBlocked(); } function esc(s) { return (s || '').replace(/[&<>]/g, c => ({ '&': '&', '<': '<', '>': '>' }[c])); } // 节间被服务端时间校验拦截:估算等待 -> 刷新重试(刷新后站点会自动点开下一未完成节) function onBlocked() { if (blocked) return; if (!waitBox || !waitBox.checked) { setStatus('⛔ 节间被服务端拦;未开启"自动等待",已暂停自动切课(可手动处理)'); if (autoBox) autoBox.checked = false; return; } let retries = 0, est = 600; try { retries = Number(sessionStorage.getItem('gxkBlockRetries') || 0); est = Number(sessionStorage.getItem('gxkWaitEst') || 0) || 600; } catch (e) {} if (retries >= MAX_BLOCK_RETRIES) { setStatus('⛔ 已连续被拦 ' + MAX_BLOCK_RETRIES + ' 次,停止自动重试,请手动处理或检查网络'); if (autoBox) autoBox.checked = false; resetBlockState(); return; } blocked = true; const waitSec = Math.min(est, MAX_WAIT); retries++; try { sessionStorage.setItem('gxkBlockRetries', String(retries)); sessionStorage.setItem('gxkWaitEst', String(Math.min(est * 1.6, MAX_WAIT))); } catch (e) {} setStatus('⛔ 节间被服务端时间校验拦(' + retries + '次),'+ (lastCompletedDuration ? '按刚学完时长' : '默认') + '等待 ' + fmt(waitSec) + ' 后自动刷新重试'); waitDeadline = Date.now() + waitSec * 1000; clearTimeout(waitRetryTimer); clearInterval(waitCountdownTimer); waitCountdownTimer = setInterval(() => { const left = Math.max(0, Math.round((waitDeadline - Date.now()) / 1000)); setStatus('⛔ 被拦,剩余 ' + fmt(left) + ' 后刷新重试切课(' + retries + '/' + MAX_BLOCK_RETRIES + ')'); }, 1000); waitRetryTimer = setTimeout(() => { clearInterval(waitCountdownTimer); setStatus('🔄 刷新页面重试切课...'); location.reload(); }, waitSec * 1000); } (function installMonitor() { try { const _open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (m, u) { this.__u = u; this.__m = m; return _open.apply(this, arguments); }; const _send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function (body) { if (this.__u && ENDPOINT_RE.test(this.__u)) { const self = this; this.addEventListener('load', function () { try { showReport(epName(self.__u), parseCode(self.responseText)); } catch (e) {} }); } return _send.apply(this, arguments); }; } catch (e) {} try { const _fetch = window.fetch; window.fetch = function (input, init) { const u = (input && input.url) || String(input); const p = _fetch.apply(this, arguments); if (u && ENDPOINT_RE.test(u)) { p.then(r => r.clone().text().then(t => { try { showReport(epName(u), parseCode(t)); } catch (e) {} })).catch(() => {}); } return p; }; } catch (e) {} })(); // ============ 左侧视频列表:章节条目探测(自动连播用) ============ const ACTIVE_CLS_RE = /\b(?:active|on|cur|current|selected|playing|focus|now|checked|item-active|active-item|current-item|cur-item)\b/i; function findLessonItems() { const all = Array.from(document.querySelectorAll('li, div, a, p, tr, [role=listitem], [role=tab], [class*=item], [class*=lesson], [class*=chapter], [class*=course], [class*=video], [class*=node], [class*=catalog], [class*=list]')); return all.filter(el => { const m = (el.textContent || '').match(/【(?:已完成|未完成)】/g); return m && m.length === 1; }); } function getCurrentVideoTitle() { const items = findLessonItems(); if (!items.length) return ''; const heads = Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,.course-title,.video-title,.title,.page-title,.video-name,.name')); for (const h of heads) { const t = h.textContent.trim().replace(/^视频[-–—::]/, '').trim(); if (items.some(it => it.textContent.includes(t) && t.length > 3)) return t; } for (const h of heads) { const t = h.textContent.trim().replace(/^视频[-–—::]/, '').trim(); if (t.length > 3 && /\d+/.test(t)) return t; } return ''; } function findCurrentLesson() { const items = findLessonItems(); if (!items.length) return null; for (const el of items) { if (ACTIVE_CLS_RE.test((el.className || '').toString())) return el; } const title = getCurrentVideoTitle(); if (title) { for (const el of items) { if ((el.textContent || '').trim().includes(title)) return el; } } for (const el of items) { const s = getComputedStyle(el); const c = (s.color + ' ' + s.backgroundColor + ' ' + s.borderColor).toLowerCase(); const rgbs = c.match(/rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)/g) || []; if (rgbs.some(rgb => { const [r, g, b] = rgb.match(/\d+/g).map(Number); return (b > 220 && r < 120 && g < 200) || (r > 220 && g < 120 && b < 120); })) return el; } return items.find(el => /【未完成】/.test(el.textContent)) || items[0]; } function pickNextLesson() { const items = findLessonItems(); if (!items.length) return null; const current = findCurrentLesson(); const idx = current ? items.indexOf(current) : -1; if (idx >= 0) { const t = items.slice(idx + 1).find(el => /【未完成】/.test(el.textContent)); if (t) return t; if (idx + 1 < items.length) return items[idx + 1]; } return items.find(el => /【未完成】/.test(el.textContent)) || null; } const NEXT_KEYWORDS = ['下一', '下一段', '下一节', '下一个', '下一课', '下一章', '后一', '继续', 'next', 'continue', 'goon']; function findNextCandidates() { const tags = 'a,button,li,div,span,input,td,[role=button],[class*=next],[id*=next],[class*=continue],[id*=continue],[class*=play]'; return Array.from(document.querySelectorAll(tags)).filter(el => { const t = (el.textContent || '').trim(); const hay = (t + '|' + (el.className || '') + '|' + (el.id || '') + '|' + ((el.getAttribute && el.getAttribute('aria-label')) || '')).toLowerCase(); return NEXT_KEYWORDS.some(k => hay.includes(k.toLowerCase())) && t.length < 40; }); } function isDisabled(el) { if (!el) return true; if (el.disabled) return true; if (el.hasAttribute && el.hasAttribute('disabled')) return true; const s = getComputedStyle(el); if (s.pointerEvents === 'none') return true; if (s.display === 'none' || s.visibility === 'hidden' || s.opacity === '0') return true; let p = el.parentElement; while (p) { const ps = getComputedStyle(p); if (ps.display === 'none' || ps.visibility === 'hidden') return true; p = p.parentElement; } return false; } function clickEl(el) { try { el.click(); } catch (e) {} try { el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); } catch (e) {} try { el.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, cancelable: true, view: window })); } catch (e) {} try { el.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, cancelable: true, view: window })); } catch (e) {} } function clickRow(row) { if (!row) return false; if (row.tagName === 'A' || row.tagName === 'BUTTON' || row.getAttribute('role') === 'button') { clickEl(row); return true; } const child = row.querySelector('a,button,[role=button]'); if (child) { clickEl(child); return true; } clickEl(row); return true; } function clickNext() { const listTarget = pickNextLesson(); if (listTarget && !isDisabled(listTarget)) { doClick(listTarget, '列表'); return true; } const kw = findNextCandidates().filter(e => !isDisabled(e))[0]; if (kw) { doClick(kw, '关键词按钮'); return true; } console.log('[gxk] 未找到可点击的下一节'); return false; } function doClick(target, kind) { if (!target || isDisabled(target)) return false; console.log('[gxk] 点击' + kind + ':', target.textContent.trim().slice(0, 40), target); clickRow(target); lastAdvance = Date.now(); return true; } // ============ 平台弹窗自动处理 ============ function clickJAlertButton(textRe, preferRe) { const layers = Array.from(document.querySelectorAll('#jAlertParent')); for (const layer of layers) { if (getComputedStyle(layer).display === 'none') continue; const txt = (layer.textContent || ''); if (!textRe.test(txt)) continue; const btns = Array.from(layer.querySelectorAll('button,a,.jAlert_btn,[class*=button]')); const ok = (preferRe ? btns.find(b => preferRe.test(b.textContent)) : null) || btns.find(b => /继续播放|继续学习|在线学习|确定|继续/.test(b.textContent)) || btns[0]; if (ok) { ok.click(); return true; } } return false; } function handlePopups() { // 离页/续播/15分钟检查类(安全类,直接点继续/确定) if (clickJAlertButton(/学习情况状态检查|继续播放视频|实时在线学习|继续学习|点击确定|是否继续/)) return 'continue'; // 非法操作/时间矛盾类:自动关闭残留弹窗,但由监视器 myd201 code=2 驱动重试逻辑(不在此重复触发) if (clickJAlertButton(/请勿非法操作|时间矛盾|请稍后再试/)) return 'blocked'; const ps = document.querySelectorAll('.layui-layer-content p'); for (const p of ps) { if (p.textContent.includes('本平台要求实时在线学习')) { const layer = p.closest('.layui-layer'); const btn = layer && layer.querySelector('.layui-layer-btn0'); if (btn) { btn.click(); return 'continue'; } } } return false; } // ============ 工具 ============ function fmt(sec) { if (!isFinite(sec) || sec < 0) sec = 0; sec = Math.round(sec); const m = Math.floor(sec / 60), s = sec % 60; return m + ':' + (s < 10 ? '0' : '') + s; } function setStatus(t) { if (statusEl) statusEl.textContent = t; } function updateProgress(cur, total) { const pct = total > 0 ? Math.min(100, (cur / total) * 100) : 0; if (seekFill) seekFill.style.width = pct + '%'; if (progText) progText.textContent = fmt(cur) + ' / ' + fmt(total) + ' (' + pct.toFixed(1) + '%)'; document.title = '进度 ' + pct.toFixed(0) + '%'; } function applyToVideo(v) { if (!v) return; try { v.playbackRate = speed; } catch (e) {} try { v.muted = MUTE; v.volume = 0; } catch (e) {} } // 切课/换源后 video.js 会 pause 并重新 setSrc;此时立刻 resume 会与 video.js 换源流程打架, // 导致黑屏/卡死。故切课后 6s 内、或视频数据还没准备好(readyState<1)时,不强制 resume。 function shouldSuppressResume() { return (Date.now() - lastAdvance) < 6000; } function bindVideo(v) { // 换源信号:video.js 切换章节会清空并重建 src,进入 resume 冷却,避免与换源冲突 v.addEventListener('emptied', () => { lastAdvance = Date.now(); }); v.addEventListener('pause', () => { if (v.ended || v.error) return; // 自然播完或出错时不恢复 if (shouldSuppressResume()) return; // 切课换源冷却期内不恢复 if (v.readyState < 1) return; // 还没足够数据,可能正在换源/缓冲 const p = v.play(); if (p && p.catch) p.catch(() => {}); }); v.addEventListener('ended', maybeAutoAdvance); v.addEventListener('timeupdate', maybeAutoAdvance); v.addEventListener('ratechange', () => { if (Math.abs(v.playbackRate - speed) > 0.01) { try { v.playbackRate = speed; } catch (e) {} } }); } function maybeAutoAdvance() { if (autoJump) return; // 一键全自动模式下,切课由"跳结尾+上报确认"驱动,不靠 95% 阈值 if (!autoBox || !autoBox.checked) return; const v = lastVideo; if (!v) return; const now = Date.now(); if (now - lastAdvance < ADVANCE_COOLDOWN) return; const dur = v.duration; const nearEnd = dur && isFinite(dur) && (v.currentTime >= dur * ADVANCE_THRESHOLD); if (!(v.ended || nearEnd)) return; clickNext(); } // 一键全自动:把当前节拖到结尾 -> 触发 ended/myd005 完成上报 -> 由 myd005 驱动切下一节 function autoJumpTick() { if (!autoJump) return; const v = lastVideo; if (!v || v.error) return; if (v.ended) { // 兜底:上报未驱动切课时,5s 后切下一节 if (Date.now() - lastAdvance > 5000) clickNext(); return; } if (v.readyState >= 1 && isFinite(v.duration) && v.duration > 0) { if (jumpedSrc !== lastSrc) { // 本节还没跳过,播放并跳到末尾 try { v.play().catch(() => {}); v.currentTime = Math.max(0, v.duration - END_GAP); } catch (e) {} jumpedSrc = lastSrc; setStatus('⏭ 自动跳结尾:' + (lastSrc.split('/').pop() || '当前节')); } } } // ============ 面板 ============ function createPanel() { if (document.getElementById('MYGXK_panel')) return; panel = document.createElement('div'); panel.id = 'MYGXK_panel'; panel.innerHTML = `
⚡ 绵阳公需课增强版 v6.4
脚本运行中...
0:00 / 0:00 (0%)
倍速 ${speed}x
📡 进度上报监视器(实测拖动是否生效)
等待上报...
`; document.body.appendChild(panel); statusEl = panel.querySelector('#MYGXK_status'); seekTrack = panel.querySelector('#MYGXK_seekTrack'); seekFill = panel.querySelector('#MYGXK_seekFill'); progText = panel.querySelector('#MYGXK_progText'); speedRange = panel.querySelector('#MYGXK_speed'); speedLabel = panel.querySelector('#MYGXK_speedLabel'); autoBox = panel.querySelector('#MYGXK_auto'); waitBox = panel.querySelector('#MYGXK_wait'); autoJumpBox = panel.querySelector('#MYGXK_autojump'); videoStatusEl = panel.querySelector('#MYGXK_vstatus'); jumpEndBtn = panel.querySelector('#MYGXK_jumpEnd'); manualBtn = panel.querySelector('#MYGXK_next'); scanBtn = panel.querySelector('#MYGXK_scan'); reportEl = panel.querySelector('#MYGXK_report'); const monBox = panel.querySelector('#MYGXK_mon'); panel.querySelector('#MYGXK_close').onclick = () => { panel.style.display = 'none'; }; speedRange.addEventListener('input', () => { speed = Number(speedRange.value); speedLabel.textContent = speed + 'x'; applyToVideo(lastVideo); }); manualBtn.addEventListener('click', () => { if (!clickNext()) updateProgress(lastVideo ? lastVideo.currentTime : 0, lastVideo ? lastVideo.duration : 0); }); scanBtn.addEventListener('click', () => { setStatus('已重新扫描'); }); monBox.addEventListener('change', () => { reportEl.style.display = monBox.checked ? '' : 'none'; reportEl.previousElementSibling.style.display = monBox.checked ? '' : 'none'; }); if (autoJumpBox) autoJumpBox.addEventListener('change', () => { autoJump = autoJumpBox.checked; setStatus(autoJump ? '🚀 一键全自动:将顺序把每节拖到结尾并自动切课' : '已关闭一键全自动'); }); jumpEndBtn.addEventListener('click', () => { if (lastVideo && isFinite(lastVideo.duration) && lastVideo.duration > 0) { try { lastVideo.play().catch(() => {}); lastVideo.currentTime = Math.max(0, lastVideo.duration - END_GAP); } catch (e) {} updateProgress(lastVideo.currentTime, lastVideo.duration); setStatus('⏭ 已跳到结尾,观察上报监视器...'); } }); const seekFromEvent = (e) => { const rect = seekTrack.getBoundingClientRect(); const clientX = (e.touches && e.touches[0]) ? e.touches[0].clientX : e.clientX; let x = clientX - rect.left; x = Math.max(0, Math.min(rect.width, x)); const frac = rect.width ? x / rect.width : 0; if (lastVideo && isFinite(lastVideo.duration) && lastVideo.duration > 0) { lastVideo.currentTime = frac * lastVideo.duration; updateProgress(lastVideo.currentTime, lastVideo.duration); } }; seekTrack.addEventListener('pointerdown', (e) => { seeking = true; try { seekTrack.setPointerCapture(e.pointerId); } catch (err) {} seekFromEvent(e); }); seekTrack.addEventListener('pointermove', (e) => { if (seeking) seekFromEvent(e); }); seekTrack.addEventListener('pointerup', () => { seeking = false; }); seekTrack.addEventListener('pointercancel', () => { seeking = false; }); seekTrack.addEventListener('touchstart', (e) => { seeking = true; seekFromEvent(e); }, { passive: true }); seekTrack.addEventListener('touchmove', (e) => { if (seeking) seekFromEvent(e); }, { passive: true }); seekTrack.addEventListener('touchend', () => { seeking = false; }); makeDraggable(panel); } function makeDraggable(el) { const header = el.querySelector('.mygxk-header'); if (!header) return; header.style.cursor = 'move'; let isDown = false, ox = 0, oy = 0; header.addEventListener('mousedown', (e) => { isDown = true; const r = el.getBoundingClientRect(); ox = e.clientX - r.left; oy = e.clientY - r.top; document.onmousemove = (ev) => { if (!isDown) return; el.style.left = (ev.clientX - ox) + 'px'; el.style.top = (ev.clientY - oy) + 'px'; el.style.right = 'auto'; }; document.onmouseup = () => { isDown = false; document.onmousemove = null; document.onmouseup = null; }; }); } // ============ 主循环 ============ function loop() { if (!document.body) return; if (!panel) createPanel(); const pop = handlePopups(); if (pop === 'continue') setStatus('🔓 已自动继续/关闭弹窗'); else if (pop === 'blocked' && !blocked) setStatus('⛔ 检测到服务端拦截弹窗(重试逻辑由上报监视器驱动)'); const v = document.querySelector('video'); if (!v) { if (!blocked) setStatus('⏳ 等待视频加载...'); return; } const curSrc = v.currentSrc || v.src; if (v !== lastVideo || curSrc !== lastSrc) { if (curSrc !== lastSrc) jumpedSrc = ''; // 换源(切课) -> 重置本节跳结尾标记,自动模式会对新节重新跳 lastVideo = v; lastSrc = curSrc; // 不再强制 v.controls=true:video.js 自带控制条已可拖动进度,强制原生控件会与其状态冲突 bindVideo(v); } applyToVideo(v); v.volume = 0; v.muted = true; if (v.ended) { if (isFinite(v.duration) && v.duration > 0) lastCompletedDuration = v.duration; } if (v.ended) { /* nothing */ } else if (v.paused) { if (shouldSuppressResume() || v.readyState < 1 || v.error) setStatus('⏳ 换源/缓冲中,稍候自动播放...'); else { v.play().catch(() => {}); setStatus('▶ 播放中(已解除暂停)'); } } else { setStatus('▶ 播放中 ' + speed + 'x'); } if (!seeking) updateProgress(v.currentTime, v.duration); if (videoStatusEl) { videoStatusEl.textContent = v.ended ? '✅ 当前视频已播放完成' : (v.paused ? '⏸ 已暂停' : '▶ 播放中'); videoStatusEl.className = 'mygxk-vstatus ' + (v.ended ? 'done' : (v.paused ? 'paused' : 'playing')); } maybeAutoAdvance(); autoJumpTick(); } function addStyle() { const css = ` #MYGXK_panel{position:fixed;top:10px;left:10px;z-index:999999;background:rgba(30,30,30,.92);color:#fff; border-radius:8px;max-width:330px;min-width:280px;box-shadow:0 4px 20px rgba(0,0,0,.5); font-size:13px;line-height:1.5;font-family:"Microsoft YaHei",sans-serif;border:1px solid #555;} #MYGXK_panel .mygxk-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px; background:linear-gradient(135deg,#e67e22,#d35400);border-radius:7px 7px 0 0;user-select:none;} #MYGXK_panel .mygxk-title{font-weight:bold;font-size:14px;} #MYGXK_panel .mygxk-close{cursor:pointer;color:rgba(255,255,255,.7);font-size:16px;padding:0 4px;} #MYGXK_panel .mygxk-close:hover{color:#fff;} #MYGXK_panel .mygxk-body{padding:10px 12px;} #MYGXK_panel .mygxk-status{color:#FFD700;margin-bottom:6px;font-size:12px;} #MYGXK_panel .mygxk-seek-track{height:12px;background:#444;border-radius:6px;overflow:hidden;cursor:pointer;position:relative;} #MYGXK_panel .mygxk-seek-fill{height:100%;width:0%;background:linear-gradient(90deg,#2ecc71,#27ae60);border-radius:6px;} #MYGXK_panel .mygxk-prog-text{font-size:12px;color:#aaa;margin-top:3px;text-align:right;} #MYGXK_panel .mygxk-row{display:flex;align-items:center;gap:6px;margin-top:8px;} #MYGXK_panel .mygxk-row input[type=range]{flex:1;} #MYGXK_panel .mygxk-btns button{flex:1;cursor:pointer;background:#34495e;color:#fff;border:none; padding:5px 4px;border-radius:4px;font-size:12px;} #MYGXK_panel .mygxk-btns button:hover{background:#3d566e;} #MYGXK_panel .mygxk-vstatus{margin-top:6px;font-size:12px;} #MYGXK_panel .mygxk-vstatus.playing{color:#2ecc71;} #MYGXK_panel .mygxk-vstatus.paused{color:#e74c3c;} #MYGXK_panel .mygxk-vstatus.done{color:#f1c40f;} #MYGXK_panel .mygxk-rep-title{margin-top:8px;font-size:12px;color:#5dade2;border-top:1px solid #555;padding-top:6px;} #MYGXK_panel .mygxk-report{margin-top:3px;font-size:11px;color:#bdc3c7;background:#1a1a1a;border-radius:4px; padding:4px 6px;max-height:90px;overflow:hidden;font-family:Consolas,Menlo,monospace;} `; const st = document.createElement('style'); st.textContent = css; document.head.appendChild(st); } function init() { addStyle(); createPanel(); setInterval(loop, 1000); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); })();