// ==UserScript== // @name 昊誉信息|昊誉教师研训平台|免费版|使用说明:http://doc.zhanyc.cn/pages/hyxx/ // @namespace free // @version 1.0 // @description 当前是免费版本,只包含了视频页面自动播放、解除视频暂停限制以及视频进度采集功能。如需自动下一集、自动换课程、考试答题、全自动无人值守高级功能可升级付费版本|接各类脚本开发、代挂任务,微信:zhanyc_cn 个人网站:http://doc.zhanyc.cn // @include *://*.haoyuinfo.com/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_deleteValue // @run-at document-end // @require http://libs.baidu.com/jquery/2.0.0/jquery.min.js // @license Creative Commons // ==/UserScript== (function () { let $jq = $; unsafeWindow.$jq = $; let docUrl = "http://doc.zhanyc.cn/pages/hyxx/"; let FREEJS = { pageData: { waitOfIndexArr: [], video: { index: null, }, progressData: null, collectProgressFlag: false, }, // ==================== 工具方法 ==================== _waitTimeout(timeout) { return new Promise((resolve, reject) => { setTimeout(() => { return resolve(); }, timeout); }); }, _waitOf(fun, interval = 1000, timeout = 180) { return new Promise((resolve, reject) => { let _timeOut = timeout * 1000; if (fun()) { return resolve(); } let index = setInterval(() => { if (timeout != -1) { _timeOut -= interval; if (_timeOut < 0) { clearInterval(index); return reject(); } } if (fun()) { clearInterval(index); return resolve(); } }, interval); FREEJS.pageData.waitOfIndexArr.push(index); }); }, _abortWaitOf() { FREEJS.pageData.waitOfIndexArr.forEach((index) => { clearInterval(index); }); FREEJS.pageData.waitOfIndexArr = []; }, // ==================== 视频相关方法 ==================== getVideo() { return $("video")[0]; }, play() { let video = FREEJS.getVideo(); if (video) { video.volume = 0; setTimeout(() => { video.play(); }, 200); } }, setVideoVolume() { try { let video = FREEJS.getVideo(); if (video && video.volume != 0) { video.volume = 0; } } catch (e) { console.error(e); } }, setStep(time) { let video = FREEJS.getVideo(); if (video) { if (time >= 0) { video.currentTime = time; } else { video.currentTime = video.duration + time; } } }, isPlayFinish() { try { let video = FREEJS.getVideo(); return video && video.duration > 0 && video.currentTime + 5 >= video.duration; } catch (e) { return false; } }, async videoIsPlay() { return new Promise((resolve) => { try { let video = FREEJS.getVideo(); if (!video) return resolve(false); let curTime = video.currentTime; setTimeout(() => { let time1 = video.currentTime; let res = time1 > curTime; if (res) { setTimeout(() => { let time2 = video.currentTime; resolve(time2 > time1); }, 100); } else { return resolve(false); } }, 100); } catch (e) { resolve(false); } }); }, // ==================== 进度采集 ==================== async page_videoProgesss() { FREEJS._abortWaitOf(); await FREEJS._waitOf((a) => $("#scoreTb tbody tr").length > 0); FREEJS.floaterShowTips("正在采集视频进度..."); await FREEJS._waitTimeout(1000); let arr = []; $("#scoreTb tbody tr").each((i, el) => { let title = $(el).find("a:first").text().trim(); let isFinish = !$(el).find("td:last").text().includes("未完成"); arr.push({ title, isFinish }); }); let courseTitle = $("h3.title").text().trim(); let progressData = { title: courseTitle, video: arr }; // 持久化到 GM 存储,防止页面切换后数据丢失导致重复采集 FREEJS.setGMData("progressCache", progressData); FREEJS.pageData.progressData = progressData; FREEJS.floaterShowProgress(progressData); FREEJS.floaterShowTips("视频进度采集完成!"); // 返回到课程学习页面 FREEJS.getElByText($(".m-sxtstudy-nav a"), "课程学习").click(); }, // ==================== 视频页面处理 ==================== async page_video() { clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; FREEJS._abortWaitOf(); await FREEJS._waitOf((a) => FREEJS.getElByText($("a"), "学习进度") != null); let title = $("h3.title").text().trim(); // 先从 GM 存储读取缓存的进度数据 if (!FREEJS.pageData.progressData) { FREEJS.pageData.progressData = FREEJS.getGMData("progressCache", null); } // 检查是否已有进度数据,没有则去采集(仅采集一次,采集后持久化到 GM 存储) if (!FREEJS.pageData.progressData || FREEJS.pageData.progressData.title != title) { FREEJS.pageData.collectProgressFlag = true; await FREEJS._waitTimeout(500); FREEJS.floaterShowTips("正在采集视频进度..."); FREEJS.getElByText($("a"), "学习进度")[0].click(); return; } // 在视频页面加载章节进度显示 FREEJS.floaterShowProgress(FREEJS.pageData.progressData); if (FREEJS.pageData.video.index != null) return; FREEJS.pageData.video.index = setInterval(async () => { try { // 解锁播放限制:处理暂停的视频 let video = FREEJS.getVideo(); if (!video) return; // 如果视频被暂停,自动播放 if (video.paused) { video.play(); FREEJS.floaterShowTips("检测到视频暂停,已自动恢复播放"); } // 静音 video.volume = 0; // 更新进度显示 let curTime = video.currentTime; let totalTime = video.duration; if (totalTime > 0) { let title = `进度:${curTime.toFixed(0)}/${totalTime.toFixed(0)}`; $("title").text(title); FREEJS.floaterUpdateProgress(curTime, totalTime); } // 更新当前播放的章节高亮 FREEJS.floaterUpdateCurrentChapter(); // 检测视频是否播放完成 if (FREEJS.isPlayFinish()) { FREEJS.floaterShowTips("当前视频播放完成"); clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; return; } // 检查是否弹出完成提示 if ($(".g-study-prompt").text().includes("您已完成观看")) { FREEJS.floaterShowTips("您已完成观看!"); clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; return; } } catch (e) { console.error("视频页面定时器出错", e); } }, 2000); }, // ==================== 页面路由 ==================== async runByUrl(url) { clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; url = url.toLowerCase(); if (url.includes("/study/course/progess".toLowerCase())) { FREEJS.page_videoProgesss(); } else if (url.includes("/study/course/".toLowerCase())) { FREEJS.page_video(); } }, // ==================== 悬浮引流面板 ==================== addStyle() { GM_addStyle(` #FREEJS_floater { position: fixed; left: 10px; top: 10px; z-index: 999999; width: 320px; background: #fff; border: 2px solid #f1a325; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); font-family: "Microsoft YaHei", sans-serif; font-size: 13px; color: #333; line-height: 1.6; } #FREEJS_floater .floater-header { background: linear-gradient(135deg, #f1a325, #f7c948); color: #fff; padding: 8px 12px; border-radius: 6px 6px 0 0; font-weight: bold; font-size: 14px; display: flex; justify-content: space-between; align-items: center; } #FREEJS_floater .floater-header .floater-close { cursor: pointer; background: rgba(255,255,255,0.3); border: none; color: #fff; border-radius: 4px; padding: 0 8px; font-size: 16px; line-height: 22px; } #FREEJS_floater .floater-header .floater-close:hover { background: rgba(255,255,255,0.5); } #FREEJS_floater .floater-body { padding: 10px 12px; } #FREEJS_floater .floater-section { margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid #eee; } #FREEJS_floater .floater-section:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; } #FREEJS_floater .floater-status { display: flex; gap: 10px; flex-wrap: wrap; } #FREEJS_floater .floater-status-item { background: #e8f5e9; padding: 3px 8px; border-radius: 4px; font-size: 12px; color: #2e7d32; } #FREEJS_floater .floater-progress-bar { width: 100%; height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden; margin: 4px 0; } #FREEJS_floater .floater-progress-bar .floater-progress-fill { height: 100%; background: linear-gradient(90deg, #4caf50, #8bc34a); border-radius: 4px; transition: width 0.5s; } #FREEJS_floater .floater-progress-text { font-size: 12px; color: #666; text-align: center; } #FREEJS_floater .floater-progress-list { max-height: 150px; overflow-y: auto; font-size: 12px; } #FREEJS_floater .floater-progress-list .progress-item { padding: 2px 4px; border-bottom: 1px dashed #eee; } #FREEJS_floater .floater-progress-list .progress-item.finish { color: #4caf50; } #FREEJS_floater .floater-progress-list .progress-item.unfinish { color: #ff9800; } #FREEJS_floater .floater-tips { background: #fff3e0; padding: 8px; border-radius: 4px; font-size: 12px; color: #e65100; } #FREEJS_floater .floater-tips a { color: #1565c0; text-decoration: underline; cursor: pointer; } #FREEJS_floater .floater-tips a:hover { color: #0d47a1; } #FREEJS_floater .floater-contact { font-size: 11px; color: #888; text-align: center; padding-top: 4px; } #FREEJS_floater .floater-running { background: #e3f2fd; border: 1px solid #90caf9; padding: 4px 8px; border-radius: 4px; font-size: 12px; color: #1565c0; text-align: center; font-weight: bold; } `); }, createFloater() { if (document.getElementById("FREEJS_floater")) return; let html = `
🎯 昊誉教师研训平台 - 免费版
✅ 视频播放限制已解除 📊 进度采集:就绪
💡 免费版功能:解除视频暂停限制 / 视频进度采集

如需使用全功能版本(自动下一集、自动换课程、考试答题等高级功能),请通过地址安装付费版本购买授权后使用:
${docUrl}
接各类脚本开发、代挂、网站小程序开发工作,微信:zhanyc_cn
`; $("body").append(html); }, closeFloater() { $("#FREEJS_floater").remove(); }, floaterShowTips(msg) { $("#floaterStatusProgress").text("📊 " + msg); }, floaterUpdateProgress(curTime, totalTime) { let $section = $("#floaterVideoSection"); if ($section.is(":hidden")) $section.show(); let pct = totalTime > 0 ? (curTime / totalTime * 100) : 0; $("#floaterProgressFill").css("width", pct + "%"); $("#floaterProgressText").text( FREEJS._timeFormat(curTime) + " / " + FREEJS._timeFormat(totalTime) ); }, floaterShowProgress(data) { let $section = $("#floaterProgressSection"); if ($section.is(":hidden")) $section.show(); let html = `
📚 课程:${data.title}
`; data.video.forEach((item) => { html += `
${item.isFinish ? "✅" : "⏳"} ${item.title}
`; }); $("#floaterProgressList").html(html); }, floaterUpdateCurrentChapter() { let $currentLink = $("a.z-crt.section"); if ($currentLink.length == 0) return; let currentTitle = $currentLink.text().trim(); if (!currentTitle) return; // 清除所有高亮 $("#floaterProgressList .progress-item").css({ "background": "", "font-weight": "", "border-radius": "" }); // 高亮当前章节 $("#floaterProgressList .progress-item").each(function () { if ($(this).attr("data-ftitle") == currentTitle) { $(this).css({ "background": "#fff3e0", "font-weight": "bold", "border-radius": "3px" }); return false; } }); }, _timeFormat(seconds) { if (!seconds || isNaN(seconds) || seconds < 0) return "00:00"; seconds = Math.floor(seconds); let m = Math.floor(seconds / 60); let s = seconds % 60; return (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s; }, // ==================== 工具方法 ==================== getGMData(item, def) { return GM_getValue(item, def); }, setGMData(item, val) { return GM_setValue(item, val); }, delGMData(item) { return GM_deleteValue(item); }, now() { return new Date().getTime(); }, getElByText(query, text, mode = "eq", visible = true) { let $el = null; text = text.trim(); $(query).each((i, el) => { if (visible && !$(el).is(":visible")) return true; if (mode == "eq" && $(el).text().trim() == text) { $el = $(el); return false; } else if (mode == "startsWith" && $(el).text().trim().startsWith(text)) { $el = $(el); return false; } else if (mode == "endsWith" && $(el).text().trim().endsWith(text)) { $el = $(el); return false; } else if (mode == "like" && $(el).text().trim().includes(text)) { $el = $(el); return false; } }); return $el; }, async init() { FREEJS.addStyle(); FREEJS.createFloater(); // 监听页面变化 let lastUrl = location.href; setInterval(async () => { if (lastUrl != location.href) { lastUrl = location.href; FREEJS.runByUrl(location.href); } }, 500); FREEJS.runByUrl(location.href); }, }; unsafeWindow.FREEJS = FREEJS; // 3秒后初始化,如果检测到付费版本(zfk)已运行,则跳过初始化 setTimeout(() => { if (typeof unsafeWindow.zfk === "undefined") { FREEJS.init(); } else { console.log("检测到付费版本已运行,免费版本跳过初始化"); } }, 3000); })();