// ==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 = `
等待视频加载...
--:--:-- / --:--:--
自动签到:等待中
一次付费,永久使用,免费维护!
如需免播放加速学习等高级功能等高级功能,请升级付费版本
`;
$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;
})();