// ==UserScript== // @name 🥇良师在线品质课堂|使用说明:http://doc.zhanyc.cn/pages/pzkt/ // @namespace free // @version 1.0 // @description 当前是免费版本,只包含了视频页面自动播放、解除播放暂停限制功能。如需自动下一集、自动换课程、秒过、考试答题、全自动无人值守高级功能可升级付费版本|接各类脚本开发、代挂工作,微信:zhanyc_cn 个人网站:http://doc.zhanyc.cn // @include *://*lsedu.vip* // @include *://*.lsedu.vip/* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_setClipboard // @grant GM_addStyle // @run-at document-end // @require http://libs.baidu.com/jquery/2.0.0/jquery.min.js // @license Creative Commons // ==/UserScript== (function () { let docUrl = "http://doc.zhanyc.cn/pages/pzkt/"; let $jq = $; unsafeWindow.$jq = $; let FREEJS = { pageData: { waitOfIndexArr: [], waitTime: 0, video: { index: null, checkIndex: null, }, }, async init() { console.log("%c FREEJS init", "background:rgb(0,0,0);color:#fff"); FREEJS._stopLeaveTips(); FREEJS._createFloatPanel(); if (top == window) { FREEJS._addFloatMsg("免费版本已启动,仅支持视频自动播放和解除暂停"); } FREEJS._runByUrl(location.href); let lastUrl = location.href; setInterval(async () => { if (lastUrl != location.href) { lastUrl = location.href; FREEJS._runByUrl(location.href); } }, 500); }, _runByUrl(url) { clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; clearTimeout(FREEJS.pageData.video.checkIndex); FREEJS.pageData.video.checkIndex = null; url = url.toLowerCase(); if (url.includes("/course/".toLowerCase())) { FREEJS._page_video(); } }, async _page_video() { console.log("%c _page_video", "background:rgb(0,0,0);color:#fff"); let timeout = 2; let lastTime = null; let checkTimeTimesBak = 60; let checkTimeTimes = checkTimeTimesBak; FREEJS.pageData.waitTime = 0; clearTimeout(FREEJS.pageData.video.checkIndex); FREEJS.pageData.video.checkIndex = setTimeout(async () => { let curTime = FREEJS._getCurTime(); await FREEJS._delay(5000); if (FREEJS._getCurTime() == curTime) { FREEJS._addFloatMsg("⚠️ 检测到视频可能卡住,请手动刷新页面"); } }, 60 * 1000); FREEJS.pageData.video.index = setInterval(async () => { try { if (FREEJS.pageData.waitTime > 0) { FREEJS.pageData.waitTime -= timeout; return; } if (!FREEJS._getVideo()) { console.log("%c no video", "background:rgb(0,0,0);color:#fff"); return; } let curTime = FREEJS._getCurTime(); if (curTime == lastTime) { lastTime = FREEJS._getCurTime(); checkTimeTimes -= timeout; if (checkTimeTimes <= 0) { checkTimeTimes = checkTimeTimesBak; FREEJS._addFloatMsg("⚠️ 视频可能卡死,请检查页面状态"); FREEJS.pageData.waitTime = 10; return; } } else { lastTime = curTime; checkTimeTimes = checkTimeTimesBak; } FREEJS._getVideo().volume = 0; let current = FREEJS._getCurTime().toFixed(0); let total = FREEJS._getTotalTime().toFixed(0); let title = `进度:${current}/${total}`; $("title").text(title); FREEJS._updateFloatProgress(current, total); console.log("%c video running", "background:rgb(255,0,0);color:#fff"); let isPlay = await FREEJS._videoIsPlay(); if (!isPlay) { FREEJS._play(); } } catch (e) { console.error("视频定时器出错", e); } }, timeout * 1000); }, _play() { let video = FREEJS._getVideo(); if (video) { video.volume = 0; setTimeout(() => { video.play(); }, 200); } }, _getVideo() { return $("video")[0]; }, _getCurTime() { try { let video = FREEJS._getVideo(); return video ? video.currentTime : 0; } catch (e) { return 0; } }, _getTotalTime() { try { let video = FREEJS._getVideo(); return video ? video.duration : 0; } catch (e) { return 0; } }, _videoIsPlay() { return new Promise((resolve) => { try { let curTime = FREEJS._getCurTime(); setTimeout(() => { let time1 = FREEJS._getCurTime(); let res = time1 > curTime; if (res) { setTimeout(() => { let time2 = FREEJS._getCurTime(); resolve(time2 > time1); }, 100); } else { resolve(false); } }, 100); } catch (e) { resolve(false); } }); }, _stopLeaveTips() { const win = unsafeWindow; const EventTargetProto = win.EventTarget.prototype; const originalAddEventListener = EventTargetProto.addEventListener; const originalRemoveEventListener = EventTargetProto.removeEventListener; EventTargetProto.addEventListener = function (type, listener, options) { if (type === 'beforeunload') { console.log('[FREEJS] 已阻止 beforeunload 监听注册'); return; } return originalAddEventListener.call(this, type, listener, options); }; EventTargetProto.removeEventListener = function (type, listener, options) { if (type === 'beforeunload') { return; } return originalRemoveEventListener.call(this, type, listener, options); }; win.onbeforeunload = null; Object.defineProperty(win, 'onbeforeunload', { configurable: true, enumerable: true, get: function () { return null; }, set: function (value) { console.log('[FREEJS] 已阻止设置 onbeforeunload'); } }); }, _delay(timeout) { return new Promise((resolve) => { setTimeout(() => resolve(), timeout); }); }, _wait(fun, interval = 1000, timeout = 30) { console.log("%c _wait", "background:rgb(0,0,0);color:#fff"); return new Promise((resolve, reject) => { let _timeOut = timeout * 1000; try { if (fun()) { return resolve(); } } catch (e) { console.error(e); } let index = setInterval(() => { try { if (timeout != -1) { _timeOut -= interval; if (_timeOut < 0) { clearInterval(index); return reject(); } } if (fun()) { clearInterval(index); return resolve(); } } catch (e) { console.error(e); } }, interval); FREEJS.pageData.waitOfIndexArr.push(index); }); }, _clearWait() { FREEJS.pageData.waitOfIndexArr.forEach(index => { clearInterval(index); }); FREEJS.pageData.waitOfIndexArr = []; }, openDoc() { if (docUrl) { window.open(docUrl); } }, 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; }, getElListByText(query, text, mode = "eq", visible = true) { let arr = []; $(query).each((i, el) => { if (visible && !$(el).is(":visible")) { return true; } if (mode == "eq" && $(el).text().trim() == text) { arr.push($(el)); } else if (mode == "startsWith" && $(el).text().trim().startsWith(text)) { arr.push($(el)); } else if (mode == "endsWith" && $(el).text().trim().endsWith(text)) { arr.push($(el)); } else if (mode == "like" && $(el).text().trim().includes(text)) { arr.push($(el)); } }); return arr; }, now() { return new Date().getTime(); }, dateFormat(date = new Date(), fmt = "yyyy-MM-dd HH:mm") { let ret; if (typeof date === "number") date = new Date(date); const opt = { "y+": date.getFullYear().toString(), "M+": (date.getMonth() + 1).toString(), "d+": date.getDate().toString(), "H+": date.getHours().toString(), "m+": date.getMinutes().toString(), "s+": date.getSeconds().toString(), }; for (let k in opt) { ret = new RegExp("(" + k + ")").exec(fmt); if (ret) { fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")); } } return fmt; }, setClip(txt) { GM_setClipboard(txt, "text"); }, // ---- 悬浮面板相关 ---- _createFloatPanel() { if ($("#freejs-float-panel").length > 0) return; GM_addStyle(` #freejs-float-panel { position: fixed; bottom: 20px; left: 20px; z-index: 2147483647; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4); padding: 16px 20px; min-width: 220px; max-width: 320px; color: #fff; font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif; font-size: 14px; line-height: 1.6; user-select: none; } #freejs-float-panel .freejs-close { position: absolute; top: 6px; right: 10px; cursor: pointer; font-size: 20px; color: rgba(255,255,255,0.7); background: none; border: none; padding: 0; line-height: 1; } #freejs-float-panel .freejs-close:hover { color: #fff; } #freejs-float-panel .freejs-title { font-size: 15px; font-weight: bold; margin-bottom: 8px; padding-right: 20px; } #freejs-float-panel .freejs-body { margin-bottom: 8px; } #freejs-float-panel .freejs-body a { color: #ffd700; text-decoration: underline; cursor: pointer; } #freejs-float-panel .freejs-body a:hover { color: #ffec80; } #freejs-float-panel .freejs-progress { background: rgba(255,255,255,0.2); border-radius: 10px; padding: 8px 10px; margin-bottom: 8px; display: none; font-size: 13px; } #freejs-float-panel .freejs-progress .freejs-progress-bar-bg { background: rgba(255,255,255,0.3); border-radius: 6px; height: 8px; margin-top: 4px; overflow: hidden; } #freejs-float-panel .freejs-progress .freejs-progress-bar { background: #ffd700; height: 100%; border-radius: 6px; width: 0%; transition: width 0.5s ease; } #freejs-float-panel .freejs-footer { font-size: 12px; color: rgba(255,255,255,0.8); border-top: 1px solid rgba(255,255,255,0.2); padding-top: 6px; margin-top: 4px; } #freejs-float-panel .freejs-msg { background: rgba(255,255,255,0.15); border-radius: 6px; padding: 6px 10px; margin-bottom: 6px; font-size: 13px; display: none; } `); let html = `
`; $("body").append(html); $("#freejs-float-panel .freejs-close").on("click", function () { $("#freejs-float-panel").remove(); }); }, _removeFloatPanel() { $("#freejs-float-panel").remove(); }, _addFloatMsg(msg) { let $area = $("#freejs-msg-area"); if ($area.length == 0) return; $area.text(msg).show(); }, _updateFloatProgress(current, total) { let $area = $("#freejs-progress-area"); let $bar = $("#freejs-progress-bar"); let $text = $("#freejs-progress-text"); if ($area.length == 0) return; $area.show(); let pct = 0; if (total > 0) { pct = Math.min(100, (current / total) * 100); } $bar.css("width", pct + "%"); $text.text(`${current} / ${total}`); }, }; if (!unsafeWindow.FREEJS) unsafeWindow.FREEJS = FREEJS; setTimeout(() => { debugger if (typeof (zfk) == 'undefined') { FREEJS.init(); } else { console.log('skip init'); } }, 3000); })();