ApabiDownloader

将最高清晰度的图片打包为PDF

目前为 2021-10-13 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name ApabiDownloader
  3. // @namespace https://qinlili.bid/
  4. // @version 0.5
  5. // @description 将最高清晰度的图片打包为PDF
  6. // @author 琴梨梨
  7. // @match *://*/OnLineReader/Default.aspx?*
  8. // @match *://cebxol.apabi.com/*
  9. // @match *://cebxol.apabiedu.com/?metaid=*
  10. // @icon data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAABEkswAlNb8AJTS/AAEAxwADxckAHrB7ACEsMQAYIWcAH/A5AAiMEwAiM/0ADxQdAAaMEQAGihEAEya3ABsv+wAmeH8AITK9AAJEBwAUarsAERuhACc2fwAPny0ACo6QgBDXmkApeH8AKfg9AAkMDwAMFl0AEeGtABOmMwALD1cAHXB9AARG0QAQFh0AJLI3AAoUIQArun8AKTo/AAyRVwAnNr0AHvH9ACS1/QAHjFcAAQFNAAvPkwAf6m8AERkbABvl6wABAIkAA8YLACc5vwABwo1AAcKIQBMp+QABAMUAAsRLAA2SGwAbLr0AGS47AAEAiwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDwzMR8lPDw8PDw8PDw8Lzw8JjwGIzw8MTw8PDw8PBAzMSU3JRgxJSg8PDw8PDw8JiYaLiU1JSI8PDw8PDAzGzElLSUHJQ08FCk8PDw8CyUlJQksPBUqCikhPDw8PDwxFxIxPDw8AzE8PCw8PAwlJSUlGzw8CikFOjc8PDwrOTwxJTUBCCkEPBYTPDw8PDwnGSICHCk4OzI8PDw8PDwsGTQ8ETEPHDQ2ADw8PDw8PDw8PCA8HR48PDw8PDw8PDw8PDw6PCQOPDw8PDw8PDw8PDw8PDw8PDw8PDw8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  11. // @grant none
  12. // @run-at document-idle
  13. // @require https://cdn.jsdelivr.net/npm/jspdf@2.4.0/dist/jspdf.umd.min.js
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18. //公共库SakiProgress
  19. var SakiProgress = {
  20. isLoaded: false,
  21. progres: false,
  22. pgDiv: false,
  23. textSpan: false,
  24. first: false,
  25. alertMode: false,
  26. init: function (color) {
  27. if (!this.isLoaded) {
  28. this.isLoaded = true;
  29. console.info("SakiProgress Initializing!\nVersion:1.0.3\nQinlili Tech:Github@qinlili23333");
  30. this.pgDiv = document.createElement("div");
  31. this.pgDiv.id = "pgdiv";
  32. 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;";
  33. this.pgDiv.style.opacity = 0;
  34. this.first = document.body.firstElementChild;
  35. document.body.insertBefore(this.pgDiv, this.first);
  36. this.first.style.transition = "margin-top 0.5s"
  37. this.progress = document.createElement("div");
  38. this.progress.id = "dlprogress"
  39. 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;"
  40. if (color) {
  41. this.setColor(color);
  42. }
  43. this.pgDiv.appendChild(this.progress);
  44. this.textSpan = document.createElement("span");
  45. this.textSpan.style = "padding-left:4px;font-size:24px;";
  46. this.textSpan.style.display = "inline-block"
  47. this.pgDiv.appendChild(this.textSpan);
  48. var css = ".barBtn:hover{ background-color: #cccccc }.barBtn:active{ background-color: #999999 }";
  49. var style = document.createElement('style');
  50. if (style.styleSheet) {
  51. style.styleSheet.cssText = css;
  52. } else {
  53. style.appendChild(document.createTextNode(css));
  54. }
  55. document.getElementsByTagName('head')[0].appendChild(style);
  56. console.info("SakiProgress Initialized!");
  57. } else {
  58. console.error("Multi Instance Error-SakiProgress Already Loaded!");
  59. }
  60. },
  61. destroy: function () {
  62. if (this.pgDiv) {
  63. document.body.removeChild(this.pgDiv);
  64. this.isLoaded = false;
  65. this.progres = false;
  66. this.pgDiv = false;
  67. this.textSpan = false;
  68. this.first = false;
  69. console.info("SakiProgress Destroyed!You Can Reload Later!");
  70. }
  71. },
  72. setPercent: function (percent) {
  73. if (this.progress) {
  74. this.progress.style.width = percent + "%";
  75. } else {
  76. console.error("Not Initialized Error-Please Call `init` First!");
  77. }
  78. },
  79. clearProgress: function () {
  80. if (this.progress) {
  81. this.progress.style.opacity = 0;
  82. setTimeout(function () { SakiProgress.progress.style.width = "0%"; }, 500);
  83. setTimeout(function () { SakiProgress.progress.style.opacity = 1; }, 750);
  84. } else {
  85. console.error("Not Initialized Error-Please Call `init` First!")
  86. }
  87. },
  88. hideDiv: function () {
  89. if (this.pgDiv) {
  90. if (this.alertMode) {
  91. setTimeout(function () {
  92. SakiProgress.pgDiv.style.opacity = 0;
  93. SakiProgress.first.style.marginTop = "";
  94. setTimeout(function () {
  95. SakiProgress.pgDiv.style.display = "none";
  96. }, 500);
  97. }, 3000);
  98. } else {
  99. this.pgDiv.style.opacity = 0;
  100. this.first.style.marginTop = "";
  101. setTimeout(function () {
  102. SakiProgress.pgDiv.style.display = "none";
  103. }, 500);
  104. }
  105. }
  106. else {
  107. console.error("Not Initialized Error-Please Call `init` First!");
  108. }
  109. },
  110. showDiv: function () {
  111. if (this.pgDiv) {
  112. this.pgDiv.style.display = "";
  113. setTimeout(function () { SakiProgress.pgDiv.style.opacity = 1; }, 10);
  114. this.first.style.marginTop = (this.pgDiv.clientHeight + 8) + "px";
  115. }
  116. else {
  117. console.error("Not Initialized Error-Please Call `init` First!");
  118. }
  119. },
  120. setText: function (text) {
  121. if (this.textSpan) {
  122. if (this.alertMode) {
  123. setTimeout(function () {
  124. if (!SakiProgress.alertMode) {
  125. SakiProgress.textSpan.innerText = text;
  126. }
  127. }, 3000);
  128. } else {
  129. this.textSpan.innerText = text;
  130. }
  131. }
  132. else {
  133. console.error("Not Initialized Error-Please Call `init` First!");
  134. }
  135. },
  136. setTextAlert: function (text) {
  137. if (this.textSpan) {
  138. this.textSpan.innerText = text;
  139. this.alertMode = true;
  140. setTimeout(function () { this.alertMode = false; }, 3000);
  141. }
  142. else {
  143. console.error("Not Initialized Error-Please Call `init` First!");
  144. }
  145. },
  146. setColor: function (color) {
  147. if (this.progress) {
  148. this.progress.style.backgroundColor = color;
  149. }
  150. else {
  151. console.error("Not Initialized Error-Please Call `init` First!");
  152. }
  153. },
  154. addBtn: function (img) {
  155. if (this.pgDiv) {
  156. var btn = document.createElement("img");
  157. btn.style = "display: inline-block;right:0px;float:right;height:32px;width:32px;transition:background-color 0.2s;"
  158. btn.className = "barBtn"
  159. btn.src = img;
  160. this.pgDiv.appendChild(btn);
  161. return btn;
  162. }
  163. else {
  164. console.error("Not Initialized Error-Please Call `init` First!");
  165. }
  166. },
  167. removeBtn: function (btn) {
  168. if (this.pgDiv) {
  169. if (btn) {
  170. this.pgDiv.removeChild(btn);
  171. }
  172. }
  173. else {
  174. console.error("Not Initialized Error-Please Call `init` First!");
  175. }
  176. }
  177. }
  178. SakiProgress.init();
  179. console.log("Initializing Apabi Downloader...Engine:AvA PDF");
  180. var jsPDF=jspdf.jsPDF;
  181. try{
  182. console.log(jsPDF)
  183. console.log("jsPDF Ready!")
  184. }catch{
  185. console.error("jsPDF Not Ready!")
  186. }
  187. //解除右键
  188. document.body.oncontextmenu = ""
  189. var pageTotal = 0;
  190. var picUrl = ""
  191. var pageCurrent = 1;
  192. var donePage=0;
  193. var urlhost = "";
  194. var PDFfile=false;
  195. var imgList=[];
  196. var imgDataList=[];
  197. var imgEle=document.createElement("img");
  198. document.body.appendChild(imgEle);
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205. //Array多线程快速下载
  206. function downloadPicList(list,dataList) {
  207. for(var j=0;list[j];j++){
  208. toDataURL(list[j],j,function(data,page){
  209. dataList[page]=data;
  210. donePage++
  211. SakiProgress.setPercent(donePage/pageTotal*90)
  212. SakiProgress.setText("已下载"+donePage+"页...")
  213. if(donePage==pageTotal){
  214. SakiProgress.setText("准备生成PDF...")
  215. makePDF();
  216. }
  217. })
  218. }
  219.  
  220.  
  221.  
  222.  
  223. //读取图片
  224. function toDataURL(url,page, callback) {
  225. var xhr = new XMLHttpRequest();
  226. xhr.onload = function() {
  227. var reader = new FileReader();
  228. reader.onloadend = function() {
  229. callback(reader.result,page);
  230. }
  231. reader.readAsDataURL(xhr.response);
  232. };
  233. xhr.open('GET', url);
  234. xhr.responseType = 'blob';
  235. xhr.send();
  236. }
  237. }
  238.  
  239.  
  240.  
  241. //制作PDF
  242. function makePDF(){
  243. for(var k=0;imgDataList[k];k++){
  244. imgEle.src=imgDataList[k];
  245. PDFfile.addImage(imgDataList[k],"JPEG",0,0,imgEle.naturalWidth,imgEle.naturalHeight,"Page"+(k+1),"SLOW")
  246. PDFfile.addPage();
  247. SakiProgress.setText("已生成"+k+"页...")
  248. }
  249. SakiProgress.setText("正在制作PDF...")
  250. PDFfile.save("Apabi.pdf",{returnPromise:true}).then(finish => {
  251. SakiProgress.clearProgress;
  252. SakiProgress.hideDiv();
  253. });
  254. }
  255. //批量下载
  256. function batchDownload() {
  257. SakiProgress.showDiv()
  258. SakiProgress.setText("正在读取页面信息...")
  259. //最大化图片尺寸
  260. currentHeight = 9999;
  261. currentWidth = 9999;
  262. pageTotal = document.getElementById("TotalCount").innerText;
  263. console.log("Initializing image list...")
  264. if (document.location.host=="cebxol.apabi.com"){
  265. urlhost="/"
  266. }else{
  267. urlhost="/OnLineReader/"
  268. }
  269. for(var i=1;i<=pageTotal;i++){
  270. imgList[i-1]=window.location.origin + urlhost + encodeURI(getUrl(i));
  271. }
  272. SakiProgress.setText("正在读取参数并建立PDF...")
  273. imgEle.onload=function(){
  274. var ori
  275. if(imgEle.naturalWidth>imgEle.naturalHeight){ori="l"}else{ori="p"}
  276. PDFfile=new jsPDF({
  277. orientation: ori,
  278. unit: 'px',
  279. format: [imgEle.naturalWidth,imgEle.naturalHeight],
  280. putOnlyUsedFonts:true,
  281. });
  282. SakiProgress.setText("正在准备下载页面...")
  283. downloadPicList(imgList,imgDataList)
  284. }
  285. imgEle.src=imgList[0]
  286. }
  287. //创建下载按钮
  288. var downloadBtn = document.createElement("a");
  289. downloadBtn.innerText = "批量下载全书";
  290. downloadBtn.onclick = function () { batchDownload() }
  291. document.querySelector("body > div.page > div.header > ul").appendChild(downloadBtn);
  292.  
  293. })();