Greasy Fork is available in English.
页面内登录/搜索+视频/音频/电子书一键批量下载+拦截Log请求+电子书去水印
// ==UserScript==// @name 学习强国梨酱小帮手// @namespace https://qinlili.bid/// @version 1.1.8// @description 页面内登录/搜索+视频/音频/电子书一键批量下载+拦截Log请求+电子书去水印// @author 琴梨梨// @match *://www.xuexi.cn/*// @match *://boot-source.xuexi.cn/newmoocdown?*// @match *://boot-source.xuexi.cn/audiodown?*// @match *://preview-pdf.xuexi.cn/*// @match *://article.xuexi.cn/*// @match https://login.xuexi.cn/login/xuexiWeb?*// @match https://static.xuexi.cn/search/*// @icon https://www.xuexi.cn/favicon.ico// @homepage https://github.com/qinlili23333/XXQG-DL// @supportURL https://github.com/qinlili23333/XXQG-DL// @grant none// @run-at document-end// @require https://lib.baomitu.com/jspdf/2.5.1/jspdf.umd.min.js// @license Anti996License// ==/UserScript==(async function () {'use strict';//既然连喜欢的人都没能力留住,那还是把更多时间投入到写代码吧--记于与悦悦子分手的7天之后(2022.1.11)//这些内容给本项目开发提供了帮助,感谢//https://stackoverflow.com/a/60644673//https://stackoverflow.com/a/55165133//也感谢每一位相信琴梨梨的用户//真的有人会看琴梨梨写的注释吗?在看的话MUA你一下~//是否开启跨源服务器//开启跨源服务器可以下载部分本来会出错的电子书//请访问此地址获取跨源服务器https://github.com/Rob--W/cors-anywhere//Clone到本地后运行npm install,然后运行node server.js,即可运行在默认地址和端口上var corsServer = "http://localhost:8080/";if (document.location.host == "preview-pdf.xuexi.cn" && (document.location.search.indexOf("boot-video.xuexi.cn") > 1) && (window.self === window.top) && confirm("该地址可能需要跨源服务器下载。启用跨源服务器吗?请在确认跨源服务器已启动之后点击确定。\n不知道跨源服务器是什么的话打开脚本源码看注释")) {var valueProp = Object.getOwnPropertyDescriptor(Image.prototype, 'src');Object.defineProperty(Image.prototype, 'src', {set: function(newimgValue){if (!newimgValue.startsWith("data:")) {newimgValue = corsServer + newimgValue;}this.crossOrigin = "anonymous"valueProp.set.call(this, newimgValue);}});}//iframe页面处理if (!(window.self === window.top)) {var transparentStyle = "background:none transparent !important;";document.documentElement.style = transparentStyle;document.body.style = transparentStyle;if (document.location.href.indexOf("login.xuexi.cn/login/xuexiWeb?") > 1) {document.getElementsByClassName("login_content")[0].style.background = "none"};if (document.location.href.indexOf("static.xuexi.cn/search/online/index.html") > 1) {document.getElementById("root").style.background = transparentStyle;if ((window.self.innerWidth > window.self.innerHeight) && window.self.innerWidth > 1000) {document.getElementsByClassName("search-content")[0].style = "padding-left:20px;padding-right:20px;"}};}//干掉日志(open=> {XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {if (!(async === false)) {async = true;};if (url.startsWith("https://iflow-api.xuexi.cn/logflow/api/v1/pclog") || url.startsWith("https://arms-retcode.aliyuncs.com/r.png")) {console.log("Rejected Log XHR! " + url + " -Qinlili")url = "data:text,null"}open.call(this, method, url, async, user, pass);};})(XMLHttpRequest.prototype.open);var originFetch = fetch;window.fetch = (url, options) => {if (url.startsWith("https://iflow-api.xuexi.cn/logflow/api/v1/pclog") || url.startsWith("https://arms-retcode.aliyuncs.com/r.png")) {console.log("Rejected Log Fetch! " + url + " -Qinlili")url = "data:text,null"}return originFetch(url, options);}//干掉PDF水印if (document.location.host == "preview-pdf.xuexi.cn") {CanvasRenderingContext2D.prototype.fillText = () => { }}//共享库const SakiProgress = {isLoaded: false,progres: false,pgDiv: false,textSpan: false,first: false,alertMode: false,init: function (color) {if (!this.isLoaded) {this.isLoaded = true;console.info("SakiProgress Initializing!\nVersion:1.0.3\nQinlili Tech:Github@qinlili23333");this.pgDiv = document.createElement("div");this.pgDiv.id = "pgdiv";this.pgDiv.style = "z-index:9999;position:fixed;background-color:white;min-height:32px;width:auto;height:32px;left:0px;right:0px;top:0px;box-shadow:0px 2px 2px 1px rgba(0, 0, 0, 0.5);transition:opacity 0.5s;display:none;";this.pgDiv.style.opacity = 0;this.first = document.body.firstElementChild;document.body.insertBefore(this.pgDiv, this.first);this.first.style.transition = "margin-top 0.5s"this.progress = document.createElement("div");this.progress.id = "dlprogress"this.progress.style = "position: absolute;top: 0;bottom: 0;left: 0;background-color: #F17C67;z-index: -1;width:0%;transition: width 0.25s ease-in-out,opacity 0.25s,background-color 1s;"if (color) {this.setColor(color);}this.pgDiv.appendChild(this.progress);this.textSpan = document.createElement("span");this.textSpan.style = "padding-left:4px;font-size:24px;";this.textSpan.style.display = "inline-block"this.pgDiv.appendChild(this.textSpan);var css = ".barBtn:hover{ background-color: #cccccc }.barBtn:active{ background-color: #999999 }";var style = document.createElement('style');if (style.styleSheet) {style.styleSheet.cssText = css;} else {style.appendChild(document.createTextNode(css));}document.getElementsByTagName('head')[0].appendChild(style);console.info("SakiProgress Initialized!");} else {console.error("Multi Instance Error-SakiProgress Already Loaded!");}},destroy: function () {if (this.pgDiv) {document.body.removeChild(this.pgDiv);this.isLoaded = false;this.progres = false;this.pgDiv = false;this.textSpan = false;this.first = false;console.info("SakiProgress Destroyed!You Can Reload Later!");}},setPercent: function (percent) {if (this.progress) {this.progress.style.width = percent + "%";} else {console.error("Not Initialized Error-Please Call `init` First!");}},clearProgress: function () {if (this.progress) {this.progress.style.opacity = 0;setTimeout(function () { SakiProgress.progress.style.width = "0%"; }, 500);setTimeout(function () { SakiProgress.progress.style.opacity = 1; }, 750);} else {console.error("Not Initialized Error-Please Call `init` First!")}},hideDiv: function () {if (this.pgDiv) {if (this.alertMode) {setTimeout(function () {SakiProgress.pgDiv.style.opacity = 0;SakiProgress.first.style.marginTop = "";setTimeout(function () {SakiProgress.pgDiv.style.display = "none";}, 500);}, 3000);} else {this.pgDiv.style.opacity = 0;this.first.style.marginTop = "";setTimeout(function () {SakiProgress.pgDiv.style.display = "none";}, 500);}}else {console.error("Not Initialized Error-Please Call `init` First!");}},showDiv: function () {if (this.pgDiv) {this.pgDiv.style.display = "";setTimeout(function () { SakiProgress.pgDiv.style.opacity = 1; }, 10);this.first.style.marginTop = (this.pgDiv.clientHeight + 8) + "px";}else {console.error("Not Initialized Error-Please Call `init` First!");}},setText: function (text) {if (this.textSpan) {if (this.alertMode) {setTimeout(function () {if (!SakiProgress.alertMode) {SakiProgress.textSpan.innerText = text;}}, 3000);} else {this.textSpan.innerText = text;}}else {console.error("Not Initialized Error-Please Call `init` First!");}},setTextAlert: function (text) {if (this.textSpan) {this.textSpan.innerText = text;this.alertMode = true;setTimeout(function () { this.alertMode = false; }, 3000);}else {console.error("Not Initialized Error-Please Call `init` First!");}},setColor: function (color) {if (this.progress) {this.progress.style.backgroundColor = color;}else {console.error("Not Initialized Error-Please Call `init` First!");}},addBtn: function (img) {if (this.pgDiv) {var btn = document.createElement("img");btn.style = "display: inline-block;right:0px;float:right;height:32px;width:32px;transition:background-color 0.2s;"btn.className = "barBtn"btn.src = img;this.pgDiv.appendChild(btn);return btn;}else {console.error("Not Initialized Error-Please Call `init` First!");}},removeBtn: function (btn) {if (this.pgDiv) {if (btn) {this.pgDiv.removeChild(btn);}}else {console.error("Not Initialized Error-Please Call `init` First!");}}}const XHRDL = {isLoaded: false,dlList: [],listBtn: false,listDiv: false,listBar: false,clsBtn: false,init: function () {if (!this.isLoaded) {console.info("WebXHRDL Initializing!\nVersion:Preview0.1.0\nQinlili Tech:Github@qinlili23333")try {SakiProgress.init();} catch {console.error("Initialize Failed!Is SakiProgress Loaded?")return false;}this.isLoaded = true;//this.listBtn = SakiProgress.addBtn("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjQ4cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4cHgiIGZpbGw9IiMwMDAwMDAiPjxnPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjwvZz48Zz48Zz48cGF0aCBkPSJNMTguMzIsNC4yNkMxNi44NCwzLjA1LDE1LjAxLDIuMjUsMTMsMi4wNXYyLjAyYzEuNDYsMC4xOCwyLjc5LDAuNzYsMy45LDEuNjJMMTguMzIsNC4yNnogTTE5LjkzLDExaDIuMDIgYy0wLjItMi4wMS0xLTMuODQtMi4yMS01LjMyTDE4LjMxLDcuMUMxOS4xNyw4LjIxLDE5Ljc1LDkuNTQsMTkuOTMsMTF6IE0xOC4zMSwxNi45bDEuNDMsMS40M2MxLjIxLTEuNDgsMi4wMS0zLjMyLDIuMjEtNS4zMiBoLTIuMDJDMTkuNzUsMTQuNDYsMTkuMTcsMTUuNzksMTguMzEsMTYuOXogTTEzLDE5LjkzdjIuMDJjMi4wMS0wLjIsMy44NC0xLDUuMzItMi4yMWwtMS40My0xLjQzIEMxNS43OSwxOS4xNywxNC40NiwxOS43NSwxMywxOS45M3ogTTEzLDEyVjdoLTJ2NUg3bDUsNWw1LTVIMTN6IE0xMSwxOS45M3YyLjAyYy01LjA1LTAuNS05LTQuNzYtOS05Ljk1czMuOTUtOS40NSw5LTkuOTV2Mi4wMiBDNy4wNSw0LjU2LDQsNy45Miw0LDEyUzcuMDUsMTkuNDQsMTEsMTkuOTN6Ii8+PC9nPjwvZz48L3N2Zz4=");//this.listBtn.onclick = XHRDL.showList;SakiProgress.showDiv();SakiProgress.setText("初始化下载器...");SakiProgress.setPercent(20);this.listDiv = document.createElement("div");this.listDiv.style = "z-index:9999;position:fixed;background-color:white;width:auto;margin-top:32px;height:100%;left:0px;right:0px;top:0px;transition:opacity 0.5s;display:none;";this.listDiv.style.opacity = 0;this.listBar = document.createElement("div");this.listBar.style = "z-index:10000;position:fixed;background-color:white;min-height:32px;margin-top:32px;width:auto;height:32px;left:0px;right:0px;top:0px;box-shadow:0px 2px 2px 1px rgba(0, 0, 0, 0.5);";this.listDiv.appendChild(this.listBar);document.body.appendChild(this.listDiv);var btn = document.createElement("img");btn.style = "display: inline-block;right:0px;float:right;height:32px;width:32px;transition:background-color 0.2s;"btn.className = "barBtn"btn.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjQ4cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4cHgiIGZpbGw9IiMwMDAwMDAiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxwYXRoIGQ9Ik0yMiwzLjQxbC01LjI5LDUuMjlMMjAsMTJoLThWNGwzLjI5LDMuMjlMMjAuNTksMkwyMiwzLjQxeiBNMy40MSwyMmw1LjI5LTUuMjlMMTIsMjB2LThINGwzLjI5LDMuMjlMMiwyMC41OUwzLjQxLDIyeiIvPjwvc3ZnPg==";this.listBar.appendChild(btn);btn.onclick = function () {XHRDL.hideList();}this.clsBtn = btn;SakiProgress.setPercent(100);SakiProgress.setText("下载器已加载!");setTimeout(function () { SakiProgress.clearProgress(); SakiProgress.hideDiv(); }, 1000);console.info("WebXHRDL Initialized!");} else {console.error("Multi Instance Error-WebXHRDL Already Loaded!")}},destroy: function (saki) {if (this.isLoaded) {if (saki) {SakiProgress.destroy();}this.isLoaded = false;this.dlList = [];this.listBtn = false;this.listDiv = false;this.listBar = false;this.clsBtn = false;console.info("WebXHRDL Destroyed!You Can Reload Later!");}},showList: function () {if (XHRDL.isLoaded) {XHRDL.listDiv.style.display = "";setTimeout(function () { XHRDL.listDiv.style.opacity = 1; }, 10);} else {console.error("Not Initialized Error-Please Call `init` First!")}},hideList: function () {if (XHRDL.isLoaded) {XHRDL.listDiv.style.opacity = 0;setTimeout(function () { XHRDL.listDiv.style.display = "none"; }, 500);} else {console.error("Not Initialized Error-Please Call `init` First!")}},saveTaskList: function () {if (XHRDL.isLoaded) {var storage = window.localStorage;storage.setItem("XHRDL_List", JSON.stringify(this.dlList));} else {console.error("Not Initialized Error-Please Call `init` First!")}},loadTaskList: function () {if (XHRDL.isLoaded) {var storage = window.localStorage;this.dlList = JSON.parse(storage.getItem("XHRDL_List"));} else {console.error("Not Initialized Error-Please Call `init` First!")}},newTask: function (url, name) {if (this.isLoaded) {var list = this.dlList;list[list.length] = {taskUrl: url,fileName: name}SakiProgress.showDiv();SakiProgress.setText("已添加新任务:" + name);if (!this.DLEngine.isWorking) {this.DLEngine.start();}} else {console.error("Not Initialized Error-Please Call `init` First!")}},DLEngine: {isWorking: false,start: function () {if (!this.isWorking) {console.info("Start WebXHRDL Engine...\nChecking Tasks...");this.isWorking = true;SakiProgress.showDiv();this.dlFirstFile();} else {console.error("WebXHRDL Engine Already Started!");}},stop: function () {this.isWorking = false;SakiProgress.hideDiv();SakiProgress.setText("");if (XHRDL.dlList[0]) {console.info("All Tasks Done!WebXHRDL Engine Stopped!");} else {console.info("WebXHRDL Engine Stopped!Tasks Paused!");}},dlFirstFile: function () {var taskInfo = XHRDL.dlList[0];SakiProgress.showDiv();SakiProgress.setPercent(0);SakiProgress.setText("正在下载" + taskInfo.fileName);var xhr = new XMLHttpRequest();xhr.responseType = "blob";xhr.onprogress = event => {if (event.loaded && event.total) {var percent = String(Number(event.loaded) / Number(event.total) * 100).substring(0, 4);SakiProgress.setText(taskInfo.fileName + "已下载" + percent + "%");SakiProgress.setPercent(percent)}};xhr.onload = event => {if (xhr.readyState === 4) {if (xhr.status === 200) {var bloburl = URL.createObjectURL(xhr.response);SakiProgress.setText("正在写出" + taskInfo.fileName);var a = document.createElement('a');var filename = taskInfo.fileName;a.href = bloburl;a.download = filename;a.click();window.URL.revokeObjectURL(bloburl);SakiProgress.clearProgress();XHRDL.dlList.splice(0, 1);XHRDL.DLEngine.checkNext();} else {//TODO:支持更多特殊状态处理SakiProgress.setTextAlert(taskInfo.fileName + "暂不支持下载,跳过");XHRDL.dlList.splice(0, 1);XHRDL.DLEngine.checkNext();}}}xhr.onerror = function (e) {//TODO:支持处理不同类别出错if (!taskInfo.errorRetry) {SakiProgress.setTextAlert(taskInfo.fileName + "下载失败,置入列尾等待重试");taskInfo.errorRetry = true;var list = XHRDL.dlList;list[list.length] = taskInfo;} else {SakiProgress.setTextAlert(taskInfo.fileName + "下载又失败了,放弃");}XHRDL.dlList.splice(0, 1);XHRDL.DLEngine.checkNext();}xhr.open('GET', taskInfo.taskUrl, true)xhr.send()},checkNext: function () {if (XHRDL.dlList[0]) {this.dlFirstFile();} else {this.stop();}}}}const sleep = delay => new Promise(resolve => setTimeout(resolve, delay));const openFrame=url=>{var searchFrame = document.createElement("iframe");searchFrame.frameBorder = 0;searchFrame.style = "padding:100%;z-index:9999;position:fixed;backdrop-filter: blur(10px) brightness(100%);background-color: rgba(255, 255, 255, .6);width:100%;margin-top:0px;height:100%;left:0px;right:0px;top:0px;";document.body.appendChild(searchFrame);searchFrame.src = url;var clsBtn = document.createElement("img");clsBtn.style = "z-index:10000;position:fixed;display: inline-block;right:0px;top:0px;float:right;height:32px;width:32px;transition:background-color 0.2s;"clsBtn.className = "barBtn"clsBtn.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjQ4cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4cHgiIGZpbGw9IiMwMDAwMDAiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxwYXRoIGQ9Ik0yMiwzLjQxbC01LjI5LDUuMjlMMjAsMTJoLThWNGwzLjI5LDMuMjlMMjAuNTksMkwyMiwzLjQxeiBNMy40MSwyMmw1LjI5LTUuMjlMMTIsMjB2LThINGwzLjI5LDMuMjlMMiwyMC41OUwzLjQxLDIyeiIvPjwvc3ZnPg==";document.body.appendChild(clsBtn);searchFrame.addEventListener("load", async () => { await sleep(150); searchFrame.style.padding = "0px"; });clsBtn.onclick = () => {document.body.removeChild(searchFrame);document.body.removeChild(clsBtn);}};//主站检测if (document.location.host == "www.xuexi.cn" || document.location.host == "preview-pdf.xuexi.cn") {console.log("JS Loaded,Sleep 3 Sec-Qinlili");await sleep(3000)if (window.self === window.top) {//初始化下载工具条XHRDL.init();var dlPannel = document.createElement("div");var downloadBtn = document.createElement("button");downloadBtn.innerText = "下载本页内容";downloadBtn.style.display = "inline-block";dlPannel.appendChild(downloadBtn);var dlText = document.createElement("p");dlText.style.display = "inline-block";dlText.innerText = "等待检测页面类型";dlPannel.appendChild(dlText);var first = document.body.firstChild;document.body.insertBefore(dlPannel, first);//接管搜索if (document.getElementsByClassName("icon search-icon")[0]) {var scBtn = document.getElementsByClassName("icon search-icon")[0];var scPrt = scBtn.parentElement;scPrt.removeChild(scBtn);scBtn = document.createElement("a");scBtn.className = "icon search-icon";scPrt.appendChild(scBtn);scBtn.addEventListener("click", async e => {var searchFrame = document.createElement("iframe");searchFrame.frameBorder = 0;searchFrame.style = "padding:100%;z-index:9999;position:fixed;backdrop-filter: blur(10px) brightness(100%);background-color: rgba(255, 255, 255, .6);width:100%;margin-top:0px;height:100%;left:0px;right:0px;top:0px;";document.body.appendChild(searchFrame);searchFrame.src = "https://static.xuexi.cn/search/online/index.html";var clsBtn = document.createElement("img");clsBtn.style = "z-index:10000;position:fixed;display: inline-block;right:0px;top:0px;float:right;height:32px;width:32px;transition:background-color 0.2s;"clsBtn.className = "barBtn"clsBtn.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjQ4cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4cHgiIGZpbGw9IiMwMDAwMDAiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxwYXRoIGQ9Ik0yMiwzLjQxbC01LjI5LDUuMjlMMjAsMTJoLThWNGwzLjI5LDMuMjlMMjAuNTksMkwyMiwzLjQxeiBNMy40MSwyMmw1LjI5LTUuMjlMMTIsMjB2LThINGwzLjI5LDMuMjlMMiwyMC41OUwzLjQxLDIyeiIvPjwvc3ZnPg==";document.body.appendChild(clsBtn);searchFrame.addEventListener("load", async () => { await sleep(150); searchFrame.style.padding = "0px"; });clsBtn.onclick = () => {document.body.removeChild(searchFrame);document.body.removeChild(clsBtn);window.removeEventListener("message", msg, false);}function msg(e) {//抄的官方JS改出来的,我对于这种非要传值到上层窗口的做法完全无法理解,但既然能跑,管他呢console.log('e:', e)console.log('e.data:', e.data)try {var params = JSON.parse(e.data);if (params.type) {console.log('params.type:', params.type);console.log('params.data:', params.data);switch (params.type) {case 'search':var useQuestionMark = false;var targetUrl = 'https://static.xuexi.cn/search/online/index.html'for (var key in params.data) {var value = params.data[key];var op = '&'if (!useQuestionMark) {op = '?';useQuestionMark = true;}targetUrl += op + key + '=' + value;}searchFrame.style.padding = "100%";searchFrame.src = targetUrl;break;default:break;}}} catch (error) {console.log(error)}}window.addEventListener("message", msg, false);})}//接管登录if (document.getElementsByClassName("icon login-icon")[0]) {var dlBtn = document.getElementsByClassName("icon login-icon")[0];var dlPrt = dlBtn.parentElement;dlPrt.removeChild(dlBtn);dlBtn = document.createElement("a");dlBtn.className = "icon login-icon";dlPrt.appendChild(dlBtn);dlBtn.addEventListener("click", async e => {e.preventDefault();SakiProgress.showDiv();SakiProgress.setPercent(5);SakiProgress.setText("正在准备登录...")console.log("Login Hooked!-Qinlili");var closeBtn = SakiProgress.addBtn("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjQ4cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4cHgiIGZpbGw9IiMwMDAwMDAiPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIvPjxwYXRoIGQ9Ik0yMiwzLjQxbC01LjI5LDUuMjlMMjAsMTJoLThWNGwzLjI5LDMuMjlMMjAuNTksMkwyMiwzLjQxeiBNMy40MSwyMmw1LjI5LTUuMjlMMTIsMjB2LThINGwzLjI5LDMuMjlMMiwyMC41OUwzLjQxLDIyeiIvPjwvc3ZnPg==")var loginFrame = document.createElement("iframe");loginFrame.frameBorder = 0;loginFrame.scrolling = "no";loginFrame.style = "padding:100%;z-index:9999;position:fixed;backdrop-filter: blur(10px) brightness(100%);background-color: rgba(255, 255, 255, .6);width:100%;margin-top:32px;height:100%;left:0px;right:0px;top:0px;";document.body.appendChild(loginFrame);loginFrame.addEventListener("load", async function () { await sleep(250); loginFrame.style.padding = "0px"; });var refreshBtn = SakiProgress.addBtn("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDhweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iNDhweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPjxwYXRoIGQ9Ik0xNy42NSA2LjM1QzE2LjIgNC45IDE0LjIxIDQgMTIgNGMtNC40MiAwLTcuOTkgMy41OC03Ljk5IDhzMy41NyA4IDcuOTkgOGMzLjczIDAgNi44NC0yLjU1IDcuNzMtNmgtMi4wOGMtLjgyIDIuMzMtMy4wNCA0LTUuNjUgNC0zLjMxIDAtNi0yLjY5LTYtNnMyLjY5LTYgNi02YzEuNjYgMCAzLjE0LjY5IDQuMjIgMS43OEwxMyAxMWg3VjRsLTIuMzUgMi4zNXoiLz48L3N2Zz4=")refreshBtn.onclick = () => {scanLogin();}closeBtn.onclick = () => {document.body.removeChild(loginFrame);SakiProgress.removeBtn(closeBtn);SakiProgress.removeBtn(refreshBtn);closeBtn = false;SakiProgress.clearProgress();SakiProgress.hideDiv();}async function scanLogin() {await sleep(100);SakiProgress.setPercent(10);SakiProgress.setText("正在获取登录口令...");let token;let tokenText;try {token = await fetch("https://pc-api.xuexi.cn/open/api/sns/sign")tokenText = JSON.parse(await token.text())} catch (e) {SakiProgress.setPercent(100);SakiProgress.setText("出错了:网络连接中断! " + e.message);return;}if (tokenText.code = "200") {token = tokenText.data.sign;SakiProgress.setPercent(40);SakiProgress.setText("口令获取成功,加载登录页面...");loginFrame.onload = () => {SakiProgress.setPercent(65);SakiProgress.setText("等待扫码...");}loginFrame.style.padding = "100%";loginFrame.src = "https://login.xuexi.cn/login/xuexiWeb?appid=dingoankubyrfkttorhpou&goto=https%3A%2F%2Foa.xuexi.cn&type=1&state=" + token + "&check_login=https%3A%2F%2Fpc-api.xuexi.cn"window.addEventListener("message", event => {event.preventDefault();console.log(event);if (event.data.success == true) {SakiProgress.setPercent(100);SakiProgress.setText("登录成功,正在刷新页面...");document.location.reload();} else {SakiProgress.setPercent(100);SakiProgress.setText("出错了:扫码登录失败,错误码为" + event.data.errorCode);}}, false);} else {SakiProgress.setPercent(100);SakiProgress.setText("出错了:获取口令失败!");}}scanLogin();})}//检测爬取页面类型console.log("Detecting Page " + document.location.pathname + "-Qinlili");var detected = false;//旧慕课列表if ((document.body.innerText.indexOf("课程介绍") >= 1) || (document.body.innerText.indexOf("课程详情") >= 1)) {console.log("Old Mooc List Detected " + document.location.pathname + "-Qinlili");detected = true;dlText.innerText = "页面类型:旧慕课列表,支持全部批量下载,请开启网站自动下载权限";downloadBtn.onclick = () => {OldMoocListDL();}}//页面有播放器if (window.Aliplayer) {//旧慕课、电视剧播放单页if (document.getElementsByClassName("radio-inline")[0]) {console.log("Old Video Player Detected " + document.location.pathname + "-Qinlili");detected = true;dlText.innerText = "页面类型:旧视频播放单页,支持全部批量下载,请开启网站自动下载权限";downloadBtn.onclick = () => {OldMoocVideoDL();}}//新慕课、影视总页if (document.getElementsByClassName("video-article-content")[0] || document.getElementsByClassName("videoSet-article-wrap")[0]) {console.log("New Video Player Detected " + document.location.pathname + "-Qinlili");detected = true;dlText.innerText = "页面类型:新视频总,支持全部批量下载最高清晰度,需要打开新标签页下载";downloadBtn.onclick = () => {NewMoocPageDL();}}}//音频专题if (document.getElementsByClassName("album-play-btn")[0]) {console.log("Audio Detected " + document.location.pathname + "-Qinlili");detected = true;dlText.innerText = "页面类型:音频,支持全部批量下载,需要打开新标签页下载";downloadBtn.onclick = () => {AudioDL();}}//页面上就一个音频//解锁音频播放器下载按钮if (document.getElementsByTagName("audio").length) {detected = true;dlText.innerText = "页面类型:单个音频,已经解锁播放器下载能力,点击播放器右侧菜单下载";downloadBtn.style.display = "none";for (var la = 0; document.getElementsByTagName("audio")[la]; la++) {document.getElementsByTagName("audio")[la].removeAttribute("controlslist");}}//电子书下载if (document.location.host == "preview-pdf.xuexi.cn") {detected = true;dlText.innerText = "页面类型:电子书,支持打包下载";downloadBtn.onclick = () => {PDFDL();}}if (!detected) {console.log("Unsupported Page " + document.location.pathname + "-Qinlili");dlText.innerText = "本页面不支持下载";downloadBtn.innerText = "暂不支持";}//下载器部分//旧慕课列表function OldMoocListDL() {//读取全部视频列表var videoList = globalCache[Object.keys(globalCache)[0]];console.log("Found " + videoList.length + " Videos-Qinlili")for (var i = 0; videoList[i]; i++) {console.log("Try Analysis " + i + " Video-Qinlili")getInfoAndDL(pagetoinfourl(videoList[i].static_page_url));}}//旧慕课播放单页function OldMoocVideoDL() {console.log("Analysis Page Info-Qinlili")getInfoAndDL(pagetoinfourl(document.location.href))}function NewMoocPageDL() {console.log("Open DL Page-Qinlili");var searchParams = new URLSearchParams(document.location.search);var dlurl = "https://boot-source.xuexi.cn/newmoocdown?id=" + searchParams.get("id");openFrame(dlurl);}function AudioDL() {console.log("Open DL Page-Qinlili");var searchParams = new URLSearchParams(document.location.search);var dlurl = "https://boot-source.xuexi.cn/audiodown?id=" + searchParams.get("id");openFrame(dlurl);}async function PDFDL() {//webp压缩用处和顶碗人一样大,所以换成灰度压缩let enableGreyCompress = confirm("是否启用灰度压缩?\n适合保存以黑白文本内容的书籍或用于Kindle等墨水屏阅读,可大幅削减文件体积,需额外消耗压缩时间。\n根据琴梨梨自己的测试可削减大约44%大小,可用于解决Chrome无法爬512M以上书的问题。")let lowQuality = confirm("是否启用低质量模式?\n轻微降低画质换取显著的文件缩小");SakiProgress.showDiv();SakiProgress.setText("正在加载依赖...");await sleep(100)console.log("Preparing jsPDF Library...-Qinlili");var jsPDF = jspdf.jsPDF;try {console.log(jsPDF)console.log("jsPDF Ready!")} catch {console.error("jsPDF Not Ready!")alert("jsPDF加载失败,请检查网络并尝试重新安装脚本!")}SakiProgress.setText("正在调整尺寸...");SakiProgress.setPercent(2);await sleep(100)if(confirm("是否开启高清模式?请观察原书清晰度,若原书清晰度较高建议开启。\n对超过300页的书开启可能导致无法生成文件。")){//按钮循环点击得稍微延迟一点否则可能卡死//放大到最大保障清晰度for (; document.getElementsByClassName("ctrl-icon")[0].className.animVal.indexOf("disabled") < 0;) {document.getElementsByClassName("ctrl-icon")[0].parentElement.click()await sleep(50)}}SakiProgress.setText("正在回到第一页...");SakiProgress.setPercent(4);await sleep(100)//回到第一页for (; document.getElementsByClassName("ctrl-icon")[2].className.animVal.indexOf("disabled") < 0;) {document.getElementsByClassName("ctrl-icon")[2].parentElement.click()await sleep(50)}//创建文件SakiProgress.setText("正在创建文件...");SakiProgress.setPercent(6);await sleep(100)var samplePage = document.getElementsByTagName("canvas")[0]var ori;let wP = samplePage.width;let hP = samplePage.height;if (wP > hP) { ori = "l" } else { ori = "p" }var PDFfile = new jsPDF({orientation: ori,unit: 'px',format: [wP, hP],putOnlyUsedFonts: true,});console.log("Preparing PDF File...-Qinlili");console.log(PDFfile);//监听函数SakiProgress.setText("正在设置监听函数...");SakiProgress.setPercent(8);await sleep(100)var onPageChange = function () { };var val = document.getElementsByTagName("input")[0];function waitPageChange() {return new Promise(resolve => {onPageChange = () => {resolve();}});}//加载完成后页码显示才会变化,监听页码显示来等待加载Object.defineProperty(val, 'value', {set: newValue => {var valueProp = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');valueProp.set.call(val, newValue);onPageChange();}});//循环保存var page = 1;var totalPage = parseInt(document.getElementsByClassName("total")[0].innerText.substr(1));const saveCurrent=()=>{//不管有几页,把当前全部canvas保存再说for (var i = 0; document.getElementsByTagName("canvas")[i]; i++) {if (enableGreyCompress) {//灰度压缩let cnv = document.getElementsByTagName("canvas")[i];let cnx = cnv.getContext('2d');let width = cnv.width;let height = cnv.height;var imgPixels = cnx.getImageData(0, 0, width, height);for (var y = 0; y < height; y++) {for (var x = 0; x < width; x++) {let i = (y * 4) * width + x * 4;var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;imgPixels.data[i] = avg;imgPixels.data[i + 1] = avg;imgPixels.data[i + 2] = avg;}}cnx.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);}if(lowQuality){PDFfile.addImage(document.getElementsByTagName("canvas")[i].toDataURL("image/webp",0.75),"WEBP",0,0,wP,hP,null,"SLOW");}else{PDFfile.addImage(document.getElementsByTagName("canvas")[i], "WEBP", 0, 0, wP, hP, null, "SLOW");}PDFfile.addPage();page++console.log("Saved One Page!-Qinlili");}}for (; document.getElementsByClassName("ctrl-icon")[3].className.animVal.indexOf("disabled") < 0;) {SakiProgress.setText("正在保存第" + page + "页...");SakiProgress.setPercent(10 + 80 * (page / totalPage));console.log("Work Current Page:" + page + "...-Qinlili");await sleep(100)saveCurrent();//虽然不知道为什么加了延迟半秒就不会卡住,但既然能用管他为什么呢setTimeout(() => { document.getElementsByClassName("ctrl-icon")[3].parentElement.click(); }, 500)//显示的正在加载的页面可能比实际加载页面小一页,但估计1919810个用户里也没一个意识到,不影响保存效果这种细节就不管了,问就是爷懒的写SakiProgress.setText("正在等待加载第" + (page + 1) + "页...");console.log("Waiting For Loading...-Qinlili");await waitPageChange();};saveCurrent();PDFfile.setFontSize(40);PDFfile.text('Powered By Qinlili',35, 65);PDFfile.textWithLink('https://greasyfork.org/zh-CN/scripts/429991',35, 25,{align: 'center', url: 'https://greasyfork.org/zh-CN/scripts/429991'});//生成文件导出SakiProgress.setText("正在导出文件...");SakiProgress.setPercent(90);PDFfile.save("学习强国电子书导出.pdf", { returnPromise: true }).then(finish => {SakiProgress.clearProgress;SakiProgress.hideDiv();});}} else {console.log("Iframe Page " + document.location.pathname + "\nSkip Detect-Qinlili");}}//全局共享函数//读取视频信息并下载function getInfoAndDL(infourl) {console.log("Get Video Info:" + infourl + "\n-Qinlili")var xhr = new XMLHttpRequest();xhr.onload = event => {console.log("Success Get Video Info:" + infourl + "\n-Qinlili")if (xhr.readyState === 4 && xhr.status === 200) {var videoInfo = JSON.parse(xhr.response.replace("globalCache = ", "").replace(";", ""));stringToObject(videoInfo);//判断慕课if (videoInfo[Object.keys(videoInfo)[0]].info) {videoInfo = videoInfo[Object.keys(videoInfo)[0]].info;for (var vi = 0; videoInfo.ossUrl[vi]; vi++) {console.log("Video Name:" + videoInfo.frst_name + "-" + (vi + 1) + "\nChapter Name:" + videoInfo.mooc_class + "\nMooc Name:" + videoInfo.mooc + "\nVideo Url:" + videoInfo.ossUrl[vi] + "\n-Qinlili");var filename = videoInfo.frst_name + "-" + (vi + 1) + "-" + videoInfo.mooc_class + "-" + videoInfo.mooc + ".mp4"console.log("File Name:" + filename + "\nPrepare Download-Qinlili");downloadFile(videoInfo.ossUrl[vi], filename);}}//判断电视剧if (videoInfo[Object.keys(videoInfo)[0]].list) {videoInfo = videoInfo[Object.keys(videoInfo)[0]].list;for (var vii = 0; videoInfo[vii]; vii++) {console.log("Video Name:" + videoInfo[vii].frst_name + "-" + (vi + 1) + "\nList Name:" + videoInfo[vii].title + "\nVideo Url:" + videoInfo[vii].ossUrl + "\n-Qinlili");var filename2 = videoInfo[vii].frst_name + "-" + (vi + 1) + "-" + videoInfo[vii].title + ".mp4"console.log("File Name:" + filename2 + "\nPrepare Download-Qinlili");downloadFile(videoInfo[vii].ossUrl, filename2);}}}xhr.onerror = () => {console.log("Fail Get Video Info:" + infourl + "\n-Qinlili")}}xhr.open('GET', infourl, false);xhr.send();//平整化Array工具,从学习强国本身的js里抄过来的,大概原理就是尝试把值作为json解析,解析成功就把解析结果替换回去,总之我大受震撼function stringToObject(params) {for (var key in params) {var value = params[key];if (isString(value)) {try {if (typeof JSON.parse(value) == 'object') {params[key] = JSON.parse(value);}} catch (e) { }} else if (isArray(value)) {try {for (var index = 0; index < value.length; index++) {stringToObject(value[index]);}} catch (e) { }} else if (isObject(value)) {try {stringToObject(params[key])} catch (e) { }}}}function isArray(o) {return Object.prototype.toString.call(o) === '[object Array]';}function isString(o) {return Object.prototype.toString.call(o) === '[object String]';}function isObject(o) {return Object.prototype.toString.call(o) === '[object Object]';}}//跳转避免跨域问题if (document.location.host == "article.xuexi.cn") {//检测是不是PDF页面,移动端分享文章也是这个域名,之前没发现var obj = document.getElementsByTagName("link");var isPDF = false;for (var pdfJS = 0; obj[pdfJS]; pdfJS++) {if (obj[pdfJS].href.indexOf("js/pdf") > 1) {isPDF = true;}}if (isPDF) {console.log("JS Loaded,Sleep 5 Sec-Qinlili");var tip = document.createElement("H1");tip.innerText = "即将跳转页面,请等待五秒";tip.style = "z-index:9999;position:fixed;backdrop-filter: blur(10px) brightness(100%);background-color: rgba(255, 255, 255, .6);width:100%;margin-top:0px;height:100%;left:0px;right:0px;top:0px;";document.body.appendChild(tip);await sleep(5000)document.location.href = document.getElementsByClassName("pdf-iframe")[0].src;}}//404注入页面避免corsif (document.location.host == "boot-source.xuexi.cn") {console.log("JS Domain Detected, Prepare Inject-Qinlili");document.querySelector("body").innerHTML = "<H2>本页面仅用于CORS注入,分享网址没有用,请允许下载多个文件</H2><H4 id=\"logcat\"></H4>"XHRDL.init();logcat("Initializing Downloader...");await sleep(3000)var searchParams = new URLSearchParams(document.location.search);var vid = searchParams.get("id");document.title = "下载:" + vid;logcat("Found ID:" + vid);logcat("Get Video Info:" + vid)var xhr = new XMLHttpRequest();xhr.onload = event => {if (xhr.readyState === 4 && xhr.status === 200) {logcat("Success Get JSON Info:" + vid)var videoInfo = JSON.parse(xhr.response.replace("callback(", "").replace("})", "}"));console.log(videoInfo);logcat("List Name:" + videoInfo.normalized_title)logcat("List Origin:" + videoInfo.show_source)//文件名后缀var filenamesource = "-" + videoInfo.normalized_title + "-" + videoInfo.show_source;//视频下载模式if (document.location.pathname == "/newmoocdown") {//检测是否为多视频if (videoInfo.sub_items) {logcat("Found " + videoInfo.sub_items.length + " Videos");//循环解析并下载视频for (var vi = 0; videoInfo.sub_items[vi]; vi++) {logcat("Analysis Video " + (vi + 1));//currentVideo,缩写为cV看起来清爽点var cV = videoInfo.sub_items[vi];var vName = cV.title;logcat("Video Name:" + vName);//检测多个清晰度var vurl = getHighest(cV.videos[0].video_storage_info);logcat("Video Url:" + vurl);var fName = vName + filenamesource + ".mp4";logcat("File Name:" + fName);logcat("Call Downloader, Downloader Log Output In F12");downloadFile(vurl, fName);}} else {//单个视频var singlevurl = getHighest(videoInfo.videos[0].video_storage_info);logcat("Video Url:" + singlevurl);vName = videoInfo.title;var sfName = vName + filenamesource + ".mp4";logcat("File Name:" + sfName);logcat("Call Downloader, Downloader Log Output In F12");downloadFile(singlevurl, sfName);}}//音频下载模式if (document.location.pathname == "/audiodown") {if (videoInfo.sub_items) {logcat("Found " + videoInfo.sub_items.length + " Audios");//循环解析并下载音频for (var ai = 0; videoInfo.sub_items[ai]; ai++) {logcat("Analysis Video " + (ai + 1));//currentAudio,缩写为cA看起来清爽点var cA = videoInfo.sub_items[ai];var aName = cA.title;logcat("Audio Name:" + aName);//音频不区分清晰度var aurl = cA.audios[0].audio_storage_info[0].url;logcat("Audio Url:" + aurl);var afName = aName + filenamesource + ".mp3";logcat("File Name:" + fName);logcat("Call Downloader, Downloader Log Output In F12");downloadFile(aurl, afName);}}}}}xhr.onerror = () => {logcat("Fail Get Json Info:" + vid)}xhr.open('GET', "https://boot-source.xuexi.cn/data/app/" + vid + ".js?callback=callback&_st=" + Date.now());xhr.send();//打印日志方法,空页面就不用console.log了function logcat(text) {//获取时间参考https://www.jianshu.com/p/067469a4eed8,稍微整合了一下document.getElementById("logcat").innerText = new Date().toTimeString().substring(0, 8) + " " + text + "\n" + document.getElementById("logcat").innerText;}//分析最高清晰度function getHighest(vObj) {var maxHeight = 1;var maxId = 0;for (var vii = 0; vObj[vii]; vii++) {if (!(vObj[vii].format == "m3u8")) {if (vObj[vii].height > maxHeight) {maxHeight = vObj[vii].height;maxId = vii;}}}logcat("Max Vide Height:" + maxHeight);return vObj[maxId].normal}}//地址转换函数function pagetoinfourl(pageurl) {var tempurl = pageurl.replace(".html", ".js");tempurl = insertStr(tempurl, tempurl.indexOf("/", 21) + 1, "data");return tempurl;}//插入function insertStr(soure, start, newStr) {return soure.slice(0, start) + newStr + soure.slice(start);}//下载function downloadFile(url, name) {XHRDL.newTask(url, name);}})();