Created
September 28, 2022 12:05
-
-
Save qinlili23333/d971800dbf2c9a4e0a4728825a3684fa to your computer and use it in GitHub Desktop.
yunpub
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name yunpub | |
// @namespace https://qinlili.bid | |
// @version 0.1 | |
// @description 轻松秒杀 | |
// @author 琴梨梨 | |
// @icon https://v.yunpub.cn/static/img/favicon.png | |
// @match https://v.yunpub.cn/reader/ebook/* | |
// @require https://lib.baomitu.com/crypto-js/4.1.1/crypto-js.min.js#sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA== | |
// @require https://lib.baomitu.com/jszip/3.10.1/jszip.min.js#sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg== | |
// @grant none | |
// ==/UserScript== | |
(async function() { | |
'use strict'; | |
//key明文写进js真是小天才呢 | |
const key="15a8d98e081cc12520c2392fb2bf35a2"; | |
//初始化依赖 | |
if (!Array.prototype.last){ | |
Array.prototype.last = function(){ | |
return this[this.length - 1]; | |
}; | |
}; | |
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.4\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 sleep = delay => new Promise(resolve => setTimeout(resolve, delay)); | |
const dlFile = (link, name) => { | |
let eleLink = document.createElement('a'); | |
eleLink.download = name; | |
eleLink.style.display = 'none'; | |
eleLink.href = link; | |
document.body.appendChild(eleLink); | |
eleLink.click(); | |
document.body.removeChild(eleLink); | |
}; | |
await sleep(2000); | |
SakiProgress.init(); | |
const btnBack = document.querySelector(".fav-btn"); | |
let inDownload = false; | |
btnBack.addEventListener("click", async (e) => { | |
e.stopPropagation(); | |
e.preventDefault(); | |
if (!inDownload) { | |
//开始下载 | |
let fileList = []; | |
inDownload = true; | |
SakiProgress.showDiv(); | |
SakiProgress.setText("正在准备下载..."); | |
console.log("Igniting Niya Core ..."); | |
let bookid=location.pathname.split("/").last(); | |
let pkey=CryptoJS.enc.Utf8.parse(key) | |
const encrypt=data=>{ | |
return CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(data), pkey, { | |
iv: pkey, | |
mode: CryptoJS.mode.CBC, | |
padding: CryptoJS.pad.Pkcs7 | |
}).toString(); | |
}; | |
const decrypt=data=>{ | |
let n = CryptoJS.AES.decrypt(data, pkey, { | |
iv: pkey, | |
mode: CryptoJS.mode.CBC, | |
padding: CryptoJS.pad.Pkcs7 | |
}) | |
return CryptoJS.enc.Utf8.stringify(n); | |
}; | |
const request="query chapterList($goodsNum: String) {\n userGood(goodsNum: $goodsNum) {\n hasRight\n hasPackageRight\n isFavorite\n studyProgress{\n chapterId\n chapterName\n isEnd\n percent\n currentPercent\n }\n good {\n id\n goodsNum\n isFree\n name\n freeChapter\n isStared\n price{\n selling\n userSellingWithRequiredExtraGoods\n }\n hasOtherRelatedGoods\n ebook {\n id\n ebookChapterList {\n id\n name\n epubPath\n }\n }\n }\n }\n }"; | |
let info=await (await fetch("https://v.yunpub.cn/api/graphql", { | |
"headers": { | |
"accept": "application/json, text/plain, */*", | |
"content-type": "application/json;charset=UTF-8" | |
}, | |
"referrer": location.href, | |
"referrerPolicy": "strict-origin-when-cross-origin", | |
"body": "{\"aQuery\":\""+encrypt(request)+"\",\"aVariables\":\""+encrypt(JSON.stringify({ | |
goodsNum:bookid, | |
goodsId:null | |
}))+"\"}", | |
"method": "POST", | |
"mode": "cors", | |
})).json(); | |
let decryptInfo=JSON.parse(decrypt(info.data)); | |
console.log(decryptInfo); | |
const basePath = "https://v.yunpub.cn/api/epub/ebook/chapter/{id}/"+decryptInfo.userGood.good.ebook.ebookChapterList[0].epubPath.split("/")[0]+"/" | |
const defaultid=decryptInfo.userGood.good.ebook.ebookChapterList[0].id; | |
await sleep(100); | |
SakiProgress.setPercent(1); | |
SakiProgress.setText("正在读取EPUB信息..."); | |
let container = await (await fetch(basePath.replace("{id}",defaultid) + "META-INF/container.xml")).blob(); | |
fileList.push({ file: container, path: "META-INF/container.xml" }); | |
let containerParsed = new DOMParser().parseFromString(await container.text(), "text/xml"); | |
console.log(containerParsed); | |
await sleep(100); | |
SakiProgress.setPercent(3); | |
SakiProgress.setText("正在读取文件清单..."); | |
let rootfile = containerParsed.getElementsByTagName("rootfile")[0]; | |
if (rootfile.getAttribute("media-type") == "application/oebps-package+xml" || confirm("该书的信息似乎不符合标准,继续吗?")) { | |
//OEBPS/content.opf | |
let itemPath = rootfile.getAttribute("full-path"); | |
console.log(basePath.replace("{id}",defaultid) + itemPath); | |
let opf = await (await fetch(basePath.replace("{id}",defaultid) + itemPath)).blob(); | |
fileList.push({ file: opf, path: itemPath }); | |
let opfParsed = new DOMParser().parseFromString(await opf.text(), "text/xml"); | |
console.log(opfParsed); | |
await sleep(100); | |
SakiProgress.setPercent(5); | |
SakiProgress.setText("正在索引文件..."); | |
let itemList = opfParsed.getElementsByTagName("item"); | |
let baseItemPath = basePath + itemPath.substring(0, itemPath.lastIndexOf("/")) + "/"; | |
let zipPath = itemPath.substring(0, itemPath.lastIndexOf("/")) + "/" | |
for (let i = 0; itemList[i]; i++) { | |
SakiProgress.setPercent(5 + i / itemList.length * 80); | |
SakiProgress.setText("正在下载文件[" + i + "/" + itemList.length + "]..."); | |
let item = itemList[i]; | |
const process=(base,file)=>{ | |
if(file.endsWith(".xhtml")){ | |
let id=defaultid; | |
decryptInfo.userGood.good.ebook.ebookChapterList.forEach(obj=>{ | |
if(obj.epubPath.indexOf(file)>0){ | |
id=obj.id; | |
} | |
}) | |
return base.replace("{id}",id) | |
}else{ | |
return base.replace("{id}",defaultid) | |
} | |
} | |
if (item.getAttribute("href")) { | |
let itemBlob = await (await fetch(process(baseItemPath,item.getAttribute("href")) + item.getAttribute("href"))).blob(); | |
let path = zipPath + item.getAttribute("href"); | |
fileList.push({ file: itemBlob, path: path }); | |
} | |
}; | |
console.log(fileList); | |
SakiProgress.setPercent(85); | |
SakiProgress.setText("正在生成文件结构..."); | |
let zip = new JSZip(); | |
fileList.forEach(file => zip.file(file.path, file.file, { binary: true })); | |
await sleep(100); | |
SakiProgress.setPercent(90); | |
SakiProgress.setText("正在打包..."); | |
let zipFile = await zip.generateAsync({ | |
type: "blob", | |
compression: "DEFLATE", | |
compressionOptions: { | |
level: 9 | |
} | |
}); | |
SakiProgress.setPercent(97); | |
SakiProgress.setText("正在导出..."); | |
await sleep(2000); | |
dlFile(URL.createObjectURL(zipFile), document.title + ".epub"); | |
SakiProgress.clearProgress(); | |
SakiProgress.setText("下载成功!"); | |
await sleep(3000); | |
inDownload = false; | |
SakiProgress.hideDiv(); | |
} else { | |
SakiProgress.clearProgress(); | |
SakiProgress.hideDiv(); | |
console.log("User Cancel."); | |
inDownload = false; | |
} | |
} | |
},true) | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment