// ==UserScript== // @name 四川精英人才教育培训中心_四川专继-课程助手 // @namespace http://tampermonkey.net/zzzzzzys_ 四川精英人才教育培训中心_四川专继-课程助手 // @version 1.0.0 // @copyright zzzzzzys.All Rights Reserved. // @description 四川精英人才教育培训中心_四川专继-课程助手,脚本免费功能有限,可自动静音播放视频,防暂停。更全自动方法,请查看文档介绍! // @author zzzzzzys // @match https://www.sczjpx.com/* // @require https://fastly.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js // @resource https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css // @require https://fastly.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js // @require https://scriptcat.org/lib/637/1.4.5/ajaxHooker.js#sha256=EGhGTDeet8zLCPnx8+72H15QYRfpTX4MbhyJ4lJZmyg= // @connect fc-mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.next.bspapp.com // @connect mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.cdn.bspapp.com // @connect zys-api.zzzzzzys.com // @connect 1.zzzzzzys.com // @grant unsafeWindow // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM_addStyle // @run-at document-start // @antifeature ads 有弹窗广告,介绍完全自动化软件 // @antifeature payment 脚本基础功能使用免费,但需要使用完全自动化软件时,可能收费 // ==/UserScript== // 变更记录(2026-09-09 定制版 v1.3.0): // 1) 强化防暂停(需求1):pause 事件恢复 + 2s 看门狗 + 窗口聚焦/页面可见恢复 // 2) 自动连播(需求2):当前课程内前 N 个视频按顺序自动学习(播完→刷新→自动跳下一节) // 3) 学满提醒(需求3):课程前 N 个视频全部学完后弹窗,内容待定 → 改 Utils.REMIND_TEXT // 4) 范围限制(需求4):只自动学每门课的前 N(=Utils.MAX_COURSES)个视频, // 第 N+1 个及之后打开页面仅提示、不自动播放 // 修改点集中在 Course.run / goNextCatalog / autoPlay 与 Utils 配置区。 class Runner { constructor() { this.runner = null this.initAjaxHooker() this.waitForDOMLoaded() } initAjaxHooker() { ajaxHooker.hook(request => { if (request.url.includes('vedioValidQuestions/getQuestions')) { request.response = res => { window.QuestionInfo = JSON.parse(res.responseText).data console.log("QuestionInfo:", window.QuestionInfo) }; } else if (request.url.includes('p/play/config')) { request.response = res => { const json = JSON.parse(res.responseText) console.log("play/config:"); console.log(json); window.playConfig = json.data }; } else if (request.url.includes('learning/learnVerify/checkCode')) { request.abort = true request.response = res => { res.responseText = '{"code":0,"msg":null,"data":{"data":"请勿频繁请求","status":9999}}' } } else if (request.url.includes('learning/learnVerify')) { request.abort = true } }) } waitForDOMLoaded() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.run()); } else { // DOM已经就绪,直接执行 this.run(); } } run() { this.runner = new Course("channel-hunau") } } class Course { constructor(channel = "channel-my") { this.panel = new AuthWindow() this.channel = new BroadcastChannel(channel) this.VIP = false this.running = false this.init() } init() { this.panel.setOnVerifyCallback(async (data) => { this.url = await Utils.validateCode(data) if (this.url) { this.panel.setTip(Utils.vipText) this.VIP = true return true } }) this.panel.setOnBegin(() => { if (!this.running) { this.running = true console.log("运行时:", this.VIP) this.run().then(r => { this.running = false }) } }) this.panel.setOnVIP(async () => { if (!this.url) { await this.panel.handleVerify() } await this.runVIP() }) this.loadVIPStatus() try { Swal.fire({ title: "提示", text: Utils.swFireText, icon: 'info', timer: 5000, confirmButtonText: '确定', timerProgressBar: true, willClose: () => { this.panel.startAutomation() } }); } catch (e) { console.error(e) this.panel.startAutomation() } } loadVIPStatus() { if (Utils.loadStatus()) { this.panel.setTip(Utils.vipText) this.VIP = true } else { this.panel.setTip(Utils.baseText) this.VIP = false } console.log("VIP:", this.VIP) } async runVIP() { try { Utils.showUpgradeAlert() } catch (error) { console.error(error) Swal.fire({ title: "高级功能执行失败!", text: "若一直失败,请联系进行售后处理!", icon: 'error', confirmButtonText: '确定', allowOutsideClick: false, willClose: () => { console.log(' 用户确认错误,脚本已停止'); } }); } } /** * 自动静音播放:设置静音 + 防暂停(需求1:解除暂停播放限制) * 三层防护: * 1) 事件驱动:站点失焦/切后台触发 pause 后自动恢复 * 2) 看门狗:每 2s 巡检,遇到未触发 pause 事件的暂停也能恢复 * 3) 焦点/可见性恢复:窗口重新聚焦、页面重新可见时兜底续播 */ async autoPlay() { const video = document.querySelector('video') if (!video) return const setMuted = () => { video.volume = 0 video.muted = true } setMuted() // 清理旧监听与未执行的恢复定时器,避免重复绑定 if (this._preventPauseHandler) { video.removeEventListener('pause', this._preventPauseHandler) clearTimeout(this._resumeTimer) clearInterval(this._watchdogTimer) window.removeEventListener('focus', this._focusHandler) document.removeEventListener('visibilitychange', this._visibilityHandler) } const tryPlay = () => { if (video.ended) return setMuted() const p = video.play() if (p && p.catch) p.catch(e => console.error("恢复播放失败:", e)) } // 1) 防暂停:站点在窗口失焦/切后台时可能强制 pause,收到 pause 后 500ms 自动恢复 this._preventPauseHandler = () => { if (video.ended) return clearTimeout(this._resumeTimer) this._resumeTimer = setTimeout(() => { console.log("检测到视频暂停,自动恢复播放...") tryPlay() }, 500) } video.addEventListener('pause', this._preventPauseHandler) // 2) 看门狗:2s 巡检兜底 this._watchdogTimer = setInterval(() => { if (video.paused && !video.ended) { console.log("看门狗检测到暂停,自动恢复播放...") tryPlay() } }, 2000) // 3) 窗口重新聚焦 / 页面重新可见时兜底续播 this._focusHandler = () => { if (video.paused && !video.ended) { console.log("窗口重新聚焦,继续播放...") tryPlay() } } window.addEventListener('focus', this._focusHandler) this._visibilityHandler = () => { if (!document.hidden && video.paused && !video.ended) { console.log("页面重新可见,继续播放...") tryPlay() } } document.addEventListener('visibilitychange', this._visibilityHandler) // 播放结束后:本站会自动标记"已学完"并刷新页面; // 若 1.5s 后页面仍未刷新,则手动刷新触发跳转流程 video.addEventListener('ended', () => { console.log("视频播放结束,等待站点标记完成后刷新...") setTimeout(() => { console.log("页面未自动刷新,手动刷新进入下一节") location.reload() }, 1500) }, { once: true }) try { await video.play() } catch (e) { console.error("初始自动播放失败:", e) } } /** * 判断当前章节的学习状态 * 本站播完一节后会标记"已学完"并自动刷新页面,因此: * 脚本启动时若发现当前章节已是"已学完",即视为"刚播完",触发跳转流程 * @returns {'completed'|'playing'|'unknown'} */ getCurrentSectionStatus() { const path = location.pathname const link = Array.from(document.querySelectorAll('.chapter-item a[href*="/study/"]')) .find(a => (a.getAttribute('href') || '').indexOf(path) !== -1) if (!link) return 'unknown' const item = link.closest('.chapter-item') if (!item) return 'unknown' if (item.classList.contains('completed')) return 'completed' const span = item.querySelector('span') if (span && span.textContent.includes('已学完')) return 'completed' return 'playing' } /** * 从学习页侧边目录中提取所有章节链接(按 课程-小节 顺序,去重) * 本站目录链接格式: /home/User/study/course_id/{课程ID}/id/{小节ID}.html */ getCatalogLinks() { const seen = new Set() const list = [] document.querySelectorAll('a[href*="/study/"]').forEach(a => { const href = a.getAttribute('href') || '' const m = href.match(/\/home\/User\/study\/course_id\/(\d+)\/id\/(\d+)\.html/) if (!m) return const key = `${m[1]}-${m[2]}` if (seen.has(key)) return seen.add(key) // 读取侧边栏该章节的完成状态(div.chapter-item.completed / 文字"已学完") const item = a.closest('.chapter-item') const span = item ? item.querySelector('span') : null const completed = item ? (item.classList.contains('completed') || !!(span && span.textContent.includes('已学完'))) : false list.push({ courseId: m[1], sectionId: m[2], url: href, title: Utils.escapeHtml(a.textContent.trim().replace(/\s+/g, ' ')), completed: completed, }) }) return list } /** * 解析学习页 URL,返回 { courseId, sectionId };非小节播放页返回 null */ parseStudyUrl(url) { const m = String(url || location.href).match(/\/home\/User\/study\/course_id\/(\d+)\/id\/(\d+)\.html/i) return m ? { courseId: m[1], sectionId: m[2] } : null } /** * 取"当前课程"目录:过滤出与 courseId 相同的全部小节(保持页面目录顺序), * allowed = 其中前 Utils.MAX_COURSES 个小节(自动学习范围)。 * @returns {{items: Array, allowed: Array}} */ getCurrentCourseCatalog(courseId) { const items = this.getCatalogLinks() if (!courseId) return { items: [], allowed: [] } const courseItems = items.filter(i => i.courseId === courseId) const allowed = courseItems.slice(0, Utils.MAX_COURSES) return { items: courseItems, allowed } } /** * 需求4:超出范围提示。当前小节是某课程第 N+1 个及以后的视频时,只提示、不自动播放。 */ showOutOfRangeTip(section, order) { Swal.fire({ title: "已超出自动学习范围", html: `当前小节是该课程第 ${order} 个视频:
${section.title}

` + `本脚本每门课程只自动学习前 ${Utils.MAX_COURSES} 个视频,第 ${Utils.MAX_COURSES + 1} 个及之后不自动播放。
` + `如需学习请手动播放,或打开该课程目录中的前 ${Utils.MAX_COURSES} 个视频。`, icon: 'warning', confirmButtonText: '知道了', allowOutsideClick: false, }) } /** * 需求3:前 N 个视频全部学完后的提醒弹窗(内容待定 → 修改 Utils.REMIND_TEXT 即可) */ showStudyReminder(courseId, learnedCount) { console.log(`课程[${courseId}] 前 ${learnedCount} 个视频已全部自动学习完成,弹出提醒`) const title = `本课程前 ${learnedCount} 个视频已学习完成` Swal.fire({ title: title, html: Utils.REMIND_TEXT, icon: 'success', confirmButtonText: '确定', allowOutsideClick: false, }) } /** * 自动连播(当前课程内):从当前课程的前 Utils.MAX_COURSES 个视频中, * 找到第一个"未学完"的并跳转;若前 N 个已全部学完 → 弹提醒(需求3),不再连播。 */ goNextCatalog(courseId) { const { allowed } = this.getCurrentCourseCatalog(courseId) if (!allowed.length) { console.log("未找到当前课程目录") return } // 若范围内前 N 个视频已全部学完:弹提醒并停止(需求3,不再跳转范围外小节) const pending = allowed.find(s => !s.completed) if (!pending) { this.showStudyReminder(courseId, allowed.length) return } console.log("跳转下一节:", pending.url) Swal.fire({ title: "视频已播放完毕", html: `即将跳转到本课程下一个视频:
${pending.title}`, icon: 'success', timer: 5000, timerProgressBar: true, confirmButtonText: '立即跳转', showCancelButton: true, cancelButtonText: '取消', }).then((result) => { // isConfirmed=点了确认,isDismissed+timer=倒计时结束,两种情况都跳转 if (result.isConfirmed || result.dismiss === Swal.DismissReason.timer) { window.location.href = pending.url } // dismiss === 'cancel' 时用户主动取消,不跳转 }) } async run() { try { // 解析当前学习页 URL:/home/User/study/course_id/{课程ID}/id/{小节ID}.html const study = this.parseStudyUrl() if (!study) { // 非课程小节页(如首页/列表页)不处理,避免误判 console.log("当前页面不是课程小节学习页,脚本不启动自动连播") return } const { courseId, sectionId } = study // 获取当前课程的目录(含自动学习范围=前 MAX_COURSES 个小节) const { items, allowed } = this.getCurrentCourseCatalog(courseId) if (!items.length) { console.log("未找到当前课程目录") return } // 当前小节在该课程中的序号(从 1 开始) const order = items.findIndex(i => i.sectionId === sectionId) + 1 // 需求4:本课程第 MAX_COURSES+1 个及之后的小节,一律不自动播放,仅提示 if (order > Utils.MAX_COURSES) { const cur = items[order - 1] console.log(`当前小节是课程第 ${order} 个视频,超出自动学习范围(前 ${Utils.MAX_COURSES} 个)`) this.showOutOfRangeTip(cur, order) return } // 本站播完一节后会标记"已学完"并刷新页面: // 刷新后脚本重新运行,若当前节已是"已学完"→ 刚播完 → 跳转下一节 / 弹完成框 if (this.getCurrentSectionStatus() === 'completed') { this.goNextCatalog(courseId) return } // 等待站点播放器创建完成(篡改猴注入时机早于播放器渲染,需轮询等待) await Utils.getStudyNode('video', 'node', 20000) // 未学完 → 自动静音播放(播完后由站点刷新页面,脚本重新运行接管跳转) await this.autoPlay() } catch (e) { console.error(e) Swal.fire({ title: "失败!", text: `视频基础播放失败!`, icon: 'error', confirmButtonColor: "#FF4DAFFF", confirmButtonText: "确定", timer: 5000, timerProgressBar: true }) } } sendMsg(msg) { // 复用构造时创建的 BroadcastChannel 实例(修复:原代码误将实例当字符串传入构造器) this.channel.postMessage(msg) } finish() { if (!this.VIP) { Swal.fire({ title: "请升级高级版!", text: `脚本已停止!基础版只能连播几个视频!`, icon: 'info', confirmButtonColor: "#FF4DAFFF", confirmButtonText: "确定", timer: 0 }) return } this.sendMsg('finish') Swal.fire({ title: "学习完成!", text: `学习完成,5s后页面自动关闭!`, icon: 'success', confirmButtonColor: "#FF4DAFFF", confirmButtonText: "确定", timer: 5000, willClose: () => { history.back() setTimeout(() => { location.reload() }, 1000) } }) } } class Utils { constructor() { } // ========================================================= // 【必改配置】每个网站脚本只需修改这一块 // ========================================================= // 【自动学习范围】每门课程只自动学习【前 N 个小节(视频)】, // 即:第 1→2→…→N 个视频按顺序自动连播;学满前 N 个视频后自动停止并弹出提醒; // 第 N+1 个及之后的小节若被手动打开,脚本只会提示“不在自动学习范围”,不自动播放。 static MAX_COURSES = 3 // 【第 N 个视频学完后的提醒弹窗内容】(需求3:内容待定,只改这一句即可) static REMIND_TEXT = '提醒内容待定:请在此处填写你想提醒的话(可修改 Utils.REMIND_TEXT)。例如:今天先学到这里,剩余小节请合理安排时间继续学习。' // 网站唯一ID,用于跳转到对应文档页面 static webId = '0a1734236025f5cacc17b263' // 脚本启动时弹窗提示文字(简短说明脚本怎么用) static swFireText = "请在视频播放页面使用脚本,脚本检测到视频会自动开始,脚本功能有限,也可能页面有所更新,导致脚本不能正常使用,建议下载软件使用!全自动学习所有未完成视频" // 控制面板顶部提示文字(基础版状态) static baseText = '建议下载软件使用!全自动学习所有未完成视频' // 控制面板顶部提示文字(VIP/已验证状态) static vipText = '全自动建议下载客户端使用! 四川精英人才教育培训中心_四川专继-客户端' // 【当前脚本】功能列表,显示在功能说明弹窗左侧 static scriptFeatures = [ "辅助当前页面视频播放", "防暂停", "自动静音播放", "仅限单页面使用", ] // 【客户端软件】功能列表,显示在功能说明弹窗右侧 static softwareFeatures = [ "输入账号密码即可全自动", "全自动完成所有未完成课程", "支持批量多账号同时学习", ] // ========================================================= // 【下载/链接配置】一般不需要改 // ========================================================= // 阿里云盘下载链接 static aliLink = "https://www.alipan.com/s/wViqbLvgSF8" // 直链下载地址(备用) static directLink = 'https://zzzzzzys.lanzouu.com/b00zxor42h' // 授权码购买链接 static link = [ "https://68n.cn/IJ8QB", ] // 备用网址列表(官网打不开时使用,总有一个能用) static web_list = [ { name: "备用地址0", url: "https://www.zzzzzzys.com/" }, { name: "备用地址1", url: "https://zzzzzzys.lovestoblog.com/" }, { name: "备用地址2", url: "https://zzzzzzys.us.kg/" }, { name: "备用地址3", url: "https://zzysdocs.dpdns.org/" }, { name: "备用地址4", url: "https://zzzzzzys.dpdns.org/" }, { name: "备用地址5", url: "https://zzzzzzys.kesug.com/" }, { name: "备用地址6", url: "https://zzysdocs.great-site.net/" }, ] // 官网文档链接(会自动拼接 webId) static docLink = `${Utils.web_list[0].url}?webId=` + Utils.webId static loadStatus() { // 当前版本固定为基础版 return false } static async validateCode(data) { // 基础版不执行远程校验,直接展示升级引导(原实现 return 之后的代码为不可达死代码,已移除) Utils.showUpgradeAlert() return undefined } /** * HTML 转义,防止课程标题等外部文本注入弹窗 HTML(显示效果不变) */ static escapeHtml(str) { return String(str).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])) } static getStudyNode(selector, type = 'node', timeout = 10000) { return new Promise((resolve, reject) => { if (!['node', 'nodeList'].includes(type)) { console.error('Invalid type parameter. Expected "node" or "nodeList"'); reject('Invalid type parameter. Expected "node" or "nodeList"'); return } const cleanup = (timeoutId, intervalId) => { clearTimeout(timeoutId); clearInterval(intervalId); }; const handleSuccess = (result, timeoutId, intervalId) => { console.log(`${selector} ready!`); cleanup(timeoutId, intervalId); resolve(result); }; const handleFailure = (timeoutId, intervalId) => { cleanup(timeoutId, intervalId); resolve(null); }; const checkNode = () => { try { if (type === 'node') { return document.querySelector(selector) } const nodes = document.querySelectorAll(selector); return nodes.length > 0 ? nodes : null; } catch (error) { console.error('节点检查错误:', error); reject('节点检查错误:', error) } }; const intervalId = setInterval(() => { const result = checkNode(); if (result) { handleSuccess(result, timeoutId, intervalId); } else { console.log(`等待节点: ${selector}...`); } }, 1000); const timeoutId = setTimeout(() => { console.error(`节点获取超时: ${selector}`); handleFailure(timeoutId, intervalId); }, timeout); }); } static showUpgradeAlert() { return Swal.fire({ title: '全自动学习功能说明', html: `

⚠️ 网页脚本有局限性

浏览器脚本运行在沙盒中,无法跨页面、无法自动登录、无法批量管理账号
若需要 输入账号密码后全自动完成所有视频,推荐使用本地客户端。

📄 当前脚本

    ${Utils.scriptFeatures.map(f => `
  • ${f}
  • `).join('')}
推荐

🖥️ 客户端工具

    ${Utils.softwareFeatures.map((f, i) => `
  • ${i === 0 ? `${f}` : f}
  • `).join('')}

🔗 前往查看 / 下载

官网文档 · 查看使用说明 & 下载
📦 阿里云盘备用下载 ⚡ 直链下载,提取密码zzzzys
▸ 官网无法访问?点击展开备用网址
${Utils.web_list.map(item => ` ${item.name} `).join('')}
`, showCancelButton: true, confirmButtonText: '前往官网', cancelButtonText: '关闭', confirmButtonColor: '#38a169', cancelButtonColor: '#a0aec0', width: '620px', padding: '1.8em', background: '#ffffff', backdrop: 'rgba(0,0,0,0.25)', customClass: { popup: 'shadow-lg' } }).then((result) => { if (result.isConfirmed) { window.open(Utils.docLink, '_blank'); } }); } } class AuthWindow { constructor({ VIPInfo = "您正在使用基础版本,功能可能存在限制" } = {}) { this.storageKey = 'AuthData'; this.injectGlobalStyles(); this.initDOM(); this.loadPersistedData(); this.show(); this.setVIPBtnText(Utils.vipText); this.setTip(VIPInfo) } injectGlobalStyles() { GM_addStyle(` .auth-window { position: fixed; right: 30px; bottom: 80px; z-index: 999999999999; background: white; padding: 24px; border-radius: 12px; box-shadow: 0 6px 30px rgba(0,0,0,0.15); border: 1px solid #e4e7ed; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-width: 320px; transform: translateY(20px); opacity: 0; transition: all 0.3s ease; } .auth-window.visible { transform: translateY(0); opacity: 1; } .auth-title { margin: 0 0 16px; font-size: 20px; color: #2c3e50; font-weight: 600; display: flex; align-items: center; gap: 8px; } .auth-version { font-size: 12px; color: #95a5a6; font-weight: normal; } .auth-tip { margin: 0 0 20px; color: #ffbb00; font-size: 14px; font-weight: 600; line-height: 1.5; } .input-group { margin-bottom: 18px; } .input-label { display: block; margin-bottom: 6px; color: #34495e; font-size: 14px; font-weight: 500; } .input-field { width: 80%; padding: 10px 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 14px; transition: border-color 0.2s; } .input-field:focus { outline: none; border-color: #3498db; box-shadow: 0 0 0 3px rgba(52,152,219,0.1); } .auth-button { width: 100%; padding: 12px; background: #3498db; color: white; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; gap: 8px; } .auth-button:hover { background: #2980b9; transform: translateY(-1px); } .auth-button:active { transform: translateY(0); } .error-message { color: #e74c3c; font-size: 13px; margin-top: 8px; padding: 8px; background: #fdeded; border-radius: 6px; display: none; animation: shake 0.4s; } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } } .control-panel { opacity: 1; transform: translateY(10px); transition: all 0.3s ease; } .control-panel.visible { opacity: 1; transform: translateY(0); } .auth-button[disabled] { background: #bdc3c7 !important; cursor: not-allowed; } .window-toggle:hover .toggle-icon { animation: bounce 0.6s; } .toggle-icon { width: 20px; height: 20px; transition: transform 0.3s ease; } @keyframes bounce { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(4px); } } /* VIP 按钮特效 */ .vip-btn { width: 100%; position: relative; padding: 12px 24px; border: none; border-radius: 8px; background: linear-gradient(135deg, #ffd700 0%, #ffd900 30%, #ffae00 70%, #ff8c00 100%); color: #2c1a00; font-weight: 600; font-family: 'Segoe UI', sans-serif; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; box-shadow: 0 4px 15px rgba(255, 174, 0, 0.3); } /* 辉光动画效果 */ .glow-effect::after { content: ''; position: absolute; inset: 0; background: radial-gradient(circle at 50% 0%, rgba(255, 255, 255, 0.4) 0%, transparent 70%); opacity: 0; transition: opacity 0.3s; } /* 悬停交互 */ .vip-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255, 174, 0, 0.5); } .vip-btn:hover::after { opacity: 1; } /* 点击反馈 */ .vip-btn:active { transform: translateY(1px); box-shadow: 0 2px 8px rgba(255, 174, 0, 0.3); } /* 皇冠图标动画 */ .crown-icon { width: 20px; height: 20px; margin-right: 8px; vertical-align: middle; transition: transform 0.3s; } .vip-btn:hover .crown-icon { transform: rotate(10deg) scale(1.1); } /* 文字渐变特效 */ .vip-text { background: linear-gradient(45deg, #2c1a00, #5a3a00); -webkit-background-clip: text; background-clip: text; color: transparent; display: inline-block; } /* 弹窗容器 */ .vip-alert-popup { border: 2px solid #ffd700; border-radius: 12px; background: linear-gradient(145deg, #1a1a1a, #2d2d2d); } /* 标题区域 */ .alert-header { border-bottom: 1px solid #404040; padding-bottom: 12px; margin-bottom: 15px; } .swal-vip-icon { color: #ffd700; font-size: 2.2em; margin-right: 8px; } /* 需求列表 */ .requirements-box { background: rgba(255,215,0,0.1); border-radius: 8px; padding: 15px; margin: 15px 0; } .requirement-item { display: flex; align-items: center; margin: 10px 0; } .number-badge { background: #ffd700; color: #000; width: 24px; height: 24px; border-radius: 50%; text-align: center; margin-right: 12px; font-weight: bold; } /* 状态标签 */ .status-tag { padding: 4px 8px; border-radius: 4px; font-size: 0.9em; } .free-status { background: #ff4444; color: white; } /* 操作引导 */ .action-guide { background: rgba(255,255,255,0.05); padding: 15px; border-radius: 8px; } .step-list li { margin: 8px 0; padding-left: 8px; } .pricing-link { color: #00ff9d !important; text-decoration: underline dotted; transition: all 0.3s; } .pricing-link:hover { color: #00cc7a !important; text-decoration: underline; } /* 确认按钮 */ .vip-confirm-btn { background: linear-gradient(135deg, #ffd700 0%, #ff9900 100%) !important; border: none !important; font-weight: bold !important; transition: transform 0.2s !important; } .vip-confirm-btn:hover { transform: scale(1.05); } `) GM_addStyle(` div.swal2-container { all: initial !important; position: fixed !important; z-index: 999999 !important; inset: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; background: rgba(0,0,0,0.4) !important; } .swal2-popup { all: initial !important; max-width: 600px !important; width: 90vw !important; min-width: 300px !important; position: relative !important; box-sizing: border-box !important; padding: 20px !important; background: white !important; border-radius: 8px !important; font-family: Arial !important; animation: none !important; } @keyframes swal2-show { 0% { transform: scale(0.9); opacity: 0 } 100% { transform: scale(1); opacity: 1 } } `) } initDOM() { this.container = document.createElement('div'); this.container.className = 'auth-window'; // 标题区域 const title = document.createElement('h3'); title.className = 'auth-title'; title.innerHTML = ` 脚本控制台v${GM_info.script.version} `; // 提示信息 const tip = document.createElement('p'); tip.className = 'auth-tip'; tip.textContent = '您正在使用基础版本,功能可能存在限制'; this.tip = tip // 输入框组 this.authInput = this.createInput(' 授权密钥', 'password', '#auth'); // 授权链接 const link = Utils.link const authLink1 = this.createLink('authLink1', link[0], '获取授权链接1'); const authLink2 = this.createLink('authLink2', link[1], '获取授权链接2'); // 验证按钮 this.verifyBtn = document.createElement('button'); this.verifyBtn.className = 'auth-button'; this.verifyBtn.innerHTML = ` 验证授权码 `; this.verifyBtn.onclick = () => this.handleVerify(); // 启动控制面板 this.controlPanel = document.createElement('div'); this.controlPanel.className = 'control-panel'; this.controlPanel.style.cssText = ` margin-top: 20px; border-top: 1px solid #eee; padding-top: 16px; `; this.vipBtn = document.createElement('button'); this.vipBtn.className = 'vip-btn glow-effect'; this.vipBtn.innerHTML = ` 高级功能-全自动挂机 `; this.vipBtn.addEventListener('click', () => { this.handleVIPClick() }) // 计时器 this.timerDisplay = document.createElement('div'); this.timerDisplay.className = 'timer'; this.timerDisplay.textContent = '运行时间: 00:00:00'; this.timerDisplay.style.cssText = ` color: #2ecc71; font-size: 13px; margin-bottom: 12px; `; // 开始按钮 this.startBtn = document.createElement('button'); this.startBtn.className = 'auth-button'; this.startBtn.style.backgroundColor = '#2ecc71'; this.startBtn.innerHTML = ` 开始运行-自动化挂机 `; this.startBtn.onclick = () => this.startAutomation(); // 错误提示 this.errorBox = document.createElement('div'); this.errorBox.className = 'error-message'; // 组装结构 this.controlPanel.append( this.vipBtn, this.timerDisplay, this.startBtn ); this.container.append( title, tip, this.authInput.container, authLink1, authLink2, this.verifyBtn, this.controlPanel, this.errorBox ); document.body.appendChild(this.container); this.initControlBtn() } initControlBtn() { // 创建控制按钮 this.toggleBtn = document.createElement('button'); this.toggleBtn.className = 'window-toggle'; this.toggleBtn.innerHTML = ` 展开面板 `; this.toggleBtn.style.cssText = ` position: fixed; right: 30px; bottom: 30px; padding: 12px 20px; background: #fff; border: none; border-radius: 30px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); cursor: pointer; display: flex; align-items: center; gap: 8px; transition: all 0.3s ease; z-index: 9999999; `; // 添加交互效果 this.toggleBtn.addEventListener('mouseenter', () => { this.toggleBtn.style.transform = 'translateY(-2px)'; this.toggleBtn.style.boxShadow = '0 6px 16px rgba(0,0,0,0.2)'; }); this.toggleBtn.addEventListener('mouseleave', () => { this.toggleBtn.style.transform = 'none'; this.toggleBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; }); // 点击事件处理 this.toggleBtn.onclick = () => { const isVisible = this.container.style.display !== 'none'; this.container.style.display = isVisible ? 'none' : 'block'; // 更新按钮状态 this.toggleBtn.querySelector('.toggle-icon').style.transform = isVisible ? 'rotate(180deg)' : 'none'; this.toggleBtn.querySelector('.toggle-text').textContent = isVisible ? '展开面板' : '收起面板'; // 添加动画效果 if (!isVisible) { this.container.animate([ { opacity: 0, transform: 'translateY(20px)' }, { opacity: 1, transform: 'none' } ], { duration: 300, easing: 'ease-out' }); } }; document.body.appendChild(this.toggleBtn); } startAutomation() { if (!this.isRunning) { this.startTime = Date.now(); this.isRunning = true; this.startBtn.innerHTML = ` 运行中... `; this.startBtn.style.backgroundColor = '#e67e22'; this.startBtn.disabled = true; // 启动计时器 this.timer = setInterval(() => { const elapsed = Date.now() - this.startTime; const hours = Math.floor(elapsed / 3600000); const minutes = Math.floor((elapsed % 3600000) / 60000); const seconds = Math.floor((elapsed % 60000) / 1000); this.timerDisplay.textContent = `运行时间: ${hours.toString().padStart(2, '0')}:` + `${minutes.toString().padStart(2, '0')}:` + `${seconds.toString().padStart(2, '0')}`; }, 1000); // 触发自动化任务 if (this.begin && typeof this.begin === 'function') { this.begin() } } } createInput(labelText, type, id) { const container = document.createElement('div'); container.className = 'input-group'; const label = document.createElement('label'); label.className = 'input-label'; label.textContent = labelText; label.htmlFor = id; const input = document.createElement('input'); input.className = 'input-field'; input.type = type; input.id = id; input.maxLength = 16 container.appendChild(label); container.appendChild(input); return { container, input }; } createLink(id, link, name) { const authLink = document.createElement('a'); authLink.id = id; authLink.className = 'auth-link'; authLink.href = link; authLink.target = '_blank'; authLink.textContent = name; authLink.style.cssText = ` display: block; margin: 12px 0; color: #3498db; text-decoration: none; font-size: 13px; transition: opacity 0.2s; `; authLink.addEventListener('mouseenter', () => { authLink.style.opacity = '0.8'; authLink.style.textDecoration = 'underline'; }); authLink.addEventListener('mouseleave', () => { authLink.style.opacity = '1'; authLink.style.textDecoration = 'none'; }); return authLink } show() { setTimeout(() => { this.container.classList.add('visible'); }, 100); } async handleVerify() { const data = { key: this.authInput.input.value }; console.log(data); if (!data.key || !(/^[A-Z0-9]{16}$/).test(data.key)) { Swal.fire({ title: "授权码不正确,应为16位", text: "请正确输入!", icon: 'info', confirmButtonText: '确定', }); return } // 触发验证回调 if (this.onVerify) { if (await this.onVerify(data)) { GM_setValue(this.storageKey, JSON.stringify(data)) } } } handleVIPClick() { if (this.vipCallback) { this.vipCallback() } else { Swal.fire({ title: "提示", text: "请在视频播放页面使用!", icon: 'info', confirmButtonText: '确定', willClose: () => { console.log(' 用户确认错误,脚本已停止'); } }); } } loadPersistedData() { const saved = GM_getValue(this.storageKey); if (saved) { try { this.authInput.input.value = JSON.parse(saved).key || ''; } catch (e) { console.error('解析本地授权数据失败:', e); GM_deleteValue(this.storageKey); } } } setTip(text) { this.tip.innerText = text } // 验证回调函数 setOnVerifyCallback(callback) { this.onVerify = callback; } setOnBegin(callback) { this.begin = callback; } setOnVIP(callback) { this.vipCallback = callback; } setVIPBtnText(text) { this.vipBtn.innerHTML = ` ${text} `; } } new Runner()