// ==UserScript== // @name 华为人才在线课程助手 (Huawei Talent Helper) - Visual // @namespace http://tampermonkey.net/ // @version 0.9 // @description 【可视化回归版】恢复 GUI 界面,支持倍速调节与日志下载,采用 Shadow DOM 隔离检测。 // @author Antigravity // @match *://e.huawei.com/cn/talent/* // @match *://talent.shixizhi.huawei.com/* // @grant none // @run-at document-end // ==/UserScript== (function () { 'use strict'; const CONFIG = { autoNext: true, playbackSpeed: 1.0, minDelay: 5000, maxDelay: 15000 }; // 增强型日志系统 const logger = { buffer: JSON.parse(localStorage.getItem('huawei_helper_logs') || "[]"), log: function (msg, type = "INFO") { const time = new Date().toLocaleTimeString(); const logMsg = `[${time}] [${type}] ${msg}`; this.buffer.push(logMsg); // 持久化到 localStorage (保留最近 500 条) if (this.buffer.length > 500) this.buffer.shift(); localStorage.setItem('huawei_helper_logs', JSON.stringify(this.buffer)); if (type === "SUCCESS") console.log(`%c${logMsg}`, "color: #28a745; font-weight: bold;"); else if (type === "WARN") console.warn(logMsg); else console.log(logMsg); updateUIRunStatus(logMsg); }, download: function () { if (this.buffer.length === 0) { alert("当前没有可导出的日志。"); return; } const blob = new Blob([this.buffer.join("\n")], { type: "text/plain" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `huawei_logs_${new Date().getTime()}.txt`; a.click(); URL.revokeObjectURL(url); this.log("日志已成功导出到本地。", "SUCCESS"); }, clear: function () { this.buffer = []; localStorage.removeItem('huawei_helper_logs'); this.log("日志已清空。"); } }; // UI 相关逻辑 (采用 Shadow DOM 提高安全性) let uiRoot = null; function createUI() { if (document.getElementById('huawei-helper-host')) return; const host = document.createElement('div'); host.id = 'huawei-helper-host'; host.style = "position: fixed; top: 50px; right: 20px; z-index: 2147483647;"; document.body.appendChild(host); uiRoot = host.attachShadow({ mode: 'closed' }); // 使用 closed 模式进一步隐藏 const panel = document.createElement('div'); panel.style = ` background: #fff; border: 2px solid #ee0000; padding: 12px; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.2); font-family: system-ui, -apple-system, sans-serif; font-size: 13px; width: 200px; color: #333; `; panel.innerHTML = `
华为助手 v0.8 [_]
倍速: (安全建议 1.0)
就绪...
`; uiRoot.appendChild(panel); // 事件绑定 uiRoot.getElementById('auto-next-toggle').addEventListener('change', (e) => { CONFIG.autoNext = e.target.checked; logger.log("自动连播已" + (CONFIG.autoNext ? "开启" : "关闭")); }); uiRoot.getElementById('speed-input').addEventListener('input', (e) => { CONFIG.playbackSpeed = parseFloat(e.target.value) || 1.0; logger.log(`期望倍速已设为: ${CONFIG.playbackSpeed}x`); }); uiRoot.getElementById('dl-log-btn').addEventListener('click', () => logger.download()); uiRoot.getElementById('minimize-btn').addEventListener('click', function () { const ctrl = uiRoot.getElementById('controls'); if (ctrl.style.display === 'none') { ctrl.style.display = 'block'; this.innerText = '[_]'; } else { ctrl.style.display = 'none'; this.innerText = '[+]'; } }); } function updateUIRunStatus(msg) { if (!uiRoot) return; const el = uiRoot.getElementById('ui-current-status'); if (el) el.innerText = msg.replace(/\[.*?\] /g, ""); } // 快捷键 window.addEventListener('keydown', (e) => { if (e.altKey && e.shiftKey && e.code === 'KeyL') { e.preventDefault(); logger.download(); } }); function runLoop() { const videos = document.querySelectorAll('video'); videos.forEach(video => { if (video.playbackRate !== CONFIG.playbackSpeed) { video.playbackRate = CONFIG.playbackSpeed; } if (video.paused && !video.ended && video.readyState >= 2) { video.play().catch(() => { }); } if (video.ended && CONFIG.autoNext && !video.dataset.v8Jumped) { video.dataset.v8Jumped = "true"; const delay = Math.floor(Math.random() * (CONFIG.maxDelay - CONFIG.minDelay) + CONFIG.minDelay); logger.log(`视频完毕,${Math.round(delay / 1000)}s 后跳转...`, "SUCCESS"); setTimeout(doJump, delay); } }); setTimeout(runLoop, 3000); } function doJump() { const sel = ['.course-next-item', '.kltCourse-btn-next', '.vjs-next-button', '.course-side-catalog-item.active + li']; let ok = false; for (let s of sel) { let b = document.querySelector(s); if (b && b.offsetParent) { b.click(); ok = true; break; } } if (!ok) { for (let el of document.querySelectorAll('span, button, div')) { if (el.innerText.includes('下一讲') && el.offsetParent) { el.click(); ok = true; break; } } } if (ok) logger.log("跳转成功", "SUCCESS"); } // 初始化 setInterval(createUI, 3000); setTimeout(runLoop, 5000); })();