// ==UserScript== // @name 【免费】新版中国教育干部网络学院+解除暂停限制+自动签到|如需免播放加速学习等高级功能见收费版本:http://doc.zhanyc.cn/pages/zgjygbwlxy/ // @namespace free // @version 1.0 // @description 当前是免费版本,只包含了视频页面自动播放、解除暂停限制+自动签到功能。如需免播放加速学习等高级功能可升级付费版本|接各类脚本开发、代挂工作,微信:zhanyc_cn 个人网站:http://doc.zhanyc.cn // @include *://estudy.enaea.edu.cn/* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_addStyle // @grant GM_xmlhttpRequest // @run-at document-end // @require http://libs.baidu.com/jquery/2.0.0/jquery.min.js // @antifeature payment // @license Creative Commons // ==/UserScript== (function () { let $jq = $; let baseConfig = { pageData: { videoTimer: null, stuckTimer: null, waitTime: 0, }, }; let docUrl = "http://doc.zhanyc.cn/pages/zgjygbwlxy/"; let fk = Object.assign(baseConfig, { // ========== 工具方法 ========== now() { return new Date().getTime(); }, getGMData(item, def) { return GM_getValue(item, def); }, setGMData(item, val) { return GM_setValue(item, val); }, delGMData(item) { return GM_deleteValue(item); }, timeFmt(sec) { sec = Math.floor(Math.abs(sec)); let h = Math.floor(sec / 3600); let m = Math.floor((sec % 3600) / 60); let s = sec % 60; return (h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m) + ":" + (s < 10 ? "0" + s : s); }, waitTimeout(timeout) { return new Promise((resolve) => { setTimeout(resolve, timeout); }); }, waitEl(fun, interval = 1000, timeout = 60) { return new Promise((resolve, reject) => { let _timeOut = timeout * 1000; try { if (fun()) return resolve(); } catch (e) { } let index = setInterval(() => { _timeOut -= interval; if (_timeOut < 0) { clearInterval(index); return reject(); } try { if (fun()) { clearInterval(index); return resolve(); } } catch (e) { } }, interval); }); }, createWorker(f) { var blob = new Blob(["(" + f + ")()"]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); return worker; }, createIntervalWorker(callback, time) { var pollingWorker = fk.createWorker(`async function (e) { setInterval(async function () { this.postMessage(null) }, ${time}) }`); pollingWorker.onmessage = callback; return pollingWorker; }, stopWorker(vm) { try { vm && vm.terminate(); } catch (e) { } }, // ========== 悬浮面板 ========== addGlobalStyle() { GM_addStyle(` .fk-float-panel{position:fixed;left:10px;top:10px;z-index:2147483647;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.3);padding:0;min-width:320px;max-width:400px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;color:#fff;overflow:hidden} .fk-float-panel .fk-panel-header{background:rgba(0,0,0,0.2);padding:10px 15px;display:flex;justify-content:space-between;align-items:center;cursor:move} .fk-float-panel .fk-panel-header .fk-title{font-size:15px;font-weight:bold} .fk-float-panel .fk-panel-close{background:none;border:none;cursor:pointer;font-size:22px;color:#fff;padding:0 4px;line-height:1;opacity:0.8} .fk-float-panel .fk-panel-close:hover{opacity:1} .fk-float-panel .fk-panel-body{padding:12px 15px} .fk-float-panel .fk-video-status{background:#00b894;border-radius:8px;padding:12px;margin-bottom:10px;text-align:center} .fk-float-panel .fk-video-status .fk-status-text{font-size:20px;font-weight:bold;margin-bottom:6px;letter-spacing:2px} .fk-float-panel .fk-video-status .fk-progress-text{font-size:14px;margin-bottom:8px} .fk-float-panel .fk-progress-bar{width:100%;height:8px;background:rgba(255,255,255,0.3);border-radius:4px;overflow:hidden} .fk-float-panel .fk-progress-fill{height:100%;background:linear-gradient(90deg,#ffeaa7,#fdcb6e);border-radius:4px;transition:width 0.5s ease;width:0%} .fk-float-panel .fk-notice{background:rgba(255,77,79,0.9);border-radius:6px;padding:8px 10px;margin-bottom:10px;font-size:12px;text-align:center;line-height:1.6} .fk-float-panel .fk-promo{background:rgba(0,0,0,0.15);border-radius:6px;padding:10px;font-size:12px;line-height:1.8} .fk-float-panel .fk-promo .fk-promo-title{font-size:13px;font-weight:bold;color:#ffeaa7;margin-bottom:4px} .fk-float-panel .fk-promo a{color:#ffeaa7;text-decoration:underline} .fk-float-panel .fk-signin-status{background:rgba(255,255,255,0.15);border-radius:6px;padding:6px 10px;margin-bottom:8px;font-size:12px;text-align:center;display:none} `); }, showFloatPanel() { $jq(".fk-float-panel").remove(); let panelHtml = `
脚本助手 - 免费版
等待视频加载...
--:--:-- / --:--:--
一次付费,永久使用,免费维护!
如需免播放加速学习等高级功能等高级功能,请升级付费版本
如需使用全功能版本,请通过地址安装付费版本购买授权后可使用
付费版地址:${docUrl}
接各类脚本开发、代挂、网站小程序开发工作
微信:zhanyc_cn
`; $jq("body").append(panelHtml); $jq(".fk-panel-close").on("click", function () { $jq(".fk-float-panel").remove(); }); }, updatePanelStatus(statusText, curTime, totalTime) { let $statusText = $jq("#fkVideoStatusText"); let $progressText = $jq("#fkVideoProgressText"); let $progressFill = $jq("#fkProgressFill"); let $statusBox = $jq("#fkVideoStatusBox"); if ($statusText.length > 0) { $statusText.text(statusText); } if ($progressText.length > 0 && totalTime > 0) { $progressText.text(`${this.timeFmt(curTime)} / ${this.timeFmt(totalTime)}`); } if ($progressFill.length > 0 && totalTime > 0) { let pct = Math.min(100, (curTime / totalTime * 100)).toFixed(1); $progressFill.css("width", pct + "%"); } if (statusText === "播放中" || statusText.startsWith("播放中")) { $statusBox.css("background", "#00b894"); } else if (statusText === "视频已完成") { $statusBox.css("background", "#6c5ce7"); } else { $statusBox.css("background", "#636e72"); } }, removeFloatPanel() { $jq(".fk-float-panel").remove(); }, showFloatTip(textArr) { $jq(".fk-float-tip-box").remove(); let listHtml = textArr.map(item => { return `
${$jq('').text(item).html()}
`; }).join(''); let boxHtml = `
${listHtml}
`; $jq("body").append(boxHtml); $jq(".fk-float-tip-close").off("click").on("click", function () { $jq(".fk-float-tip-box").remove(); }); }, // ========== 视频播放方法 ========== playVideo() { let vid = this.getVideoEl(); if (!vid) return; vid.volume = 0; setTimeout(() => { vid.play(); }, 200); }, getVideoEl() { return $jq("video")[0]; }, isVideoDone() { try { return this.getVideoTotal() > 0 && this.getVideoCur() + 5 >= this.getVideoTotal(); } catch (e) { return false; } }, getVideoCur() { try { return this.getVideoEl().currentTime || 0; } catch (e) { return 0; } }, getVideoTotal() { try { return this.getVideoEl().duration || 0; } catch (e) { return 0; } }, checkVideoPlaying() { return new Promise((resolve) => { try { let t1 = this.getVideoCur(); setTimeout(() => { let t2 = this.getVideoCur(); resolve(t2 > t1); }, 300); } catch (e) { resolve(false); } }); }, // ========== 视频页面监控 ========== async pageVideoMonitor() { console.log("%c pageVideoMonitor", "background:rgb(0,0,0);color:#fff"); if (fk.pageData.videoTimer != null) return; this.showFloatPanel(); this.updatePanelStatus("等待视频加载..."); // 等待video元素出现 await this.waitEl(() => { try { return this.getVideoEl() != null; } catch (e) { return false; } }, 1000, 120); // 检查是否在iframe中,尝试获取iframe内的video await this.waitTimeout(2000); let vid = this.getVideoEl(); if (!vid) { let $iframe = $jq("iframe"); if ($iframe.length > 0) { try { let iframeDoc = $iframe[0].contentDocument || $iframe[0].contentWindow.document; vid = $jq(iframeDoc).find("video")[0]; } catch (e) { console.log("无法访问iframe内容"); } } } let lastTime = -1; let stuckCount = 0; let stuckMaxCount = 30; // 30次 * 2秒 = 60秒无进度则刷新 fk.pageData.videoTimer = setInterval(async () => { try { if (fk.pageData.waitTime > 0) { fk.pageData.waitTime -= 2; return; } let video = fk.getVideoEl(); if (!video) { fk.updatePanelStatus("等待视频加载..."); return; } // 解除播放限制:静音 video.volume = 0; let curTime = fk.getVideoCur(); let totalTime = fk.getVideoTotal(); // 更新进度显示 fk.updatePanelStatus("播放中", curTime, totalTime); // 更新页面标题显示进度 let titleText = `进度:${fk.timeFmt(curTime)}/${fk.timeFmt(totalTime)}`; $jq("title").text(titleText); // 检测是否卡住 if (curTime == lastTime && curTime > 0) { stuckCount++; if (stuckCount >= stuckMaxCount) { console.log("视频疑似卡住,刷新页面"); location.reload(); return; } } else { stuckCount = 0; } lastTime = curTime; // 检测是否播放完成 if (fk.isVideoDone()) { fk.updatePanelStatus("视频已完成", curTime, totalTime); clearInterval(fk.pageData.videoTimer); fk.pageData.videoTimer = null; return; } // 检测是否暂停,自动播放 let isPlaying = await fk.checkVideoPlaying(); if (!isPlaying) { fk.playVideo(); } } catch (e) { console.error("视频监控出错", e); } }, 2000); }, // ========== 自动签到(占位,后续添加) ========== // TODO: 自动签到功能 - 在此处添加自动签到相关方法 // async autoSignIn() { // // 自动签到逻辑 // }, // ========== 路由 ========== async runByUrl(url) { // 清理之前的定时器 if (fk.pageData.videoTimer != null) { clearInterval(fk.pageData.videoTimer); fk.pageData.videoTimer = null; } if (fk.pageData.stuckTimer != null) { clearTimeout(fk.pageData.stuckTimer); fk.pageData.stuckTimer = null; } $jq(".fk-float-tip-box").remove(); $jq(".fk-float-panel").remove(); url = url.toLowerCase(); if (url.includes("/web/player/project")) { // 视频播放页面 - 启动视频监控 fk.pageVideoMonitor(); } else if (url.includes("/web/") && url.includes("/home")) { // 项目首页 fk.showFloatTip(["请从【项目首页】进入视频播放页面,脚本将自动解除播放限制并监控进度"]); } else { fk.showFloatTip(["请从【项目首页】进入视频播放页面"]); } }, async firstRun() { fk.addGlobalStyle(); let lastUrl = location.href; setInterval(async () => { if (lastUrl != location.href) { lastUrl = location.href; fk.runByUrl(location.href); } }, 500); fk.runByUrl(location.href); }, }); fk.firstRun(); if (!unsafeWindow.fk) unsafeWindow.fk = fk; })();