BUAA-Thesis-Download

北航论文平台下载工具,请勿传播下载的文件,否则后果自负。

当前为 2022-12-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name BUAA-Thesis-Download
  3. // @namespace https://github.com/xiaotianxt
  4. // @version 0.1.0
  5. // @description 北航论文平台下载工具,请勿传播下载的文件,否则后果自负。
  6. // @author xiaotianxt
  7. // @match https://d.buaa.edu.cn/*/pdfindex.jsp?fid=*
  8. // @match https://copyright.lib.buaa.edu.cn/*/pdfindex.jsp?fid=*
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/notify/0.4.2/notify.min.js
  11. // @license GNU GPLv3
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. "use strict";
  16. const RESOLUTION = "buaa_thesis_download.resolution";
  17. const fid = $("#fid").val();
  18. const totalPage = parseInt($("#totalPages").html().replace(/ \/ /, ""));
  19. const baseUrl = `https://copyright.lib.buaa.edu.cn/jumpServlet?fid=${fid}`;
  20. // const baseUrl = `https://d.buaa.edu.cn/https/77726476706e69737468656265737421f3f8518535396f586a4685a59a1b2120b46fb5d23026dc57e5/jumpServlet?fid=${fid}`;
  21. const msgBox = initUI();
  22. initMonitor();
  23.  
  24. function initUI() {
  25. // 下载按钮
  26. const downloadButton = document.querySelector("#thumbtab").cloneNode(true);
  27. downloadButton.innerHTML = `
  28. <div class="panel-bg" style="background: url(&quot;data:image/png;base64,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&quot;) center center no-repeat;"></div>
  29. <span class="panel-name">下载</span>
  30. `;
  31. document.querySelector("#btnList").appendChild(downloadButton);
  32. downloadButton.addEventListener("click", download);
  33.  
  34. // 清晰度
  35. const resolution = localStorage.getItem(RESOLUTION) || "2f";
  36. const resolutionRadioGroup = document
  37. .querySelector("#thumbtab")
  38. .cloneNode(true);
  39. resolutionRadioGroup.innerHTML = `
  40. <div resolution>
  41. <input type="radio" name="resolution" id="standard" value="2f"> <label for="standard">标清</label>
  42. <input type="radio" name="resolution" id="high" value="3f"> <label for="high">超清</label>
  43. <input type="radio" name="resolution" id="super" value="5f"> <label for="super">巨清</label>
  44. </div>
  45. `;
  46. document.querySelector("#btnList").appendChild(resolutionRadioGroup);
  47. $("input[name='resolution'][value='" + resolution + "']").prop(
  48. "checked",
  49. true
  50. );
  51. $("input[name='resolution']").on("click", (e) => {
  52. localStorage.setItem(RESOLUTION, e.target.value);
  53. $("#jspPane_scroll img").each((i, elem) => {
  54. elem.src = elem.src.replace(/2f|3f|5f/, e.target.value);
  55. });
  56. $.notify("清晰度已调整", "success");
  57. });
  58. $("input[name='resolution'][value='5f']").on("click", (e) => {
  59. $("[resolution]").notify("图片尺寸过大,加载速度会比较缓慢。", "warn");
  60. });
  61.  
  62. // msgBox
  63. const msgBox = downloadButton.querySelector("span");
  64. return msgBox;
  65. }
  66.  
  67. function initMonitor() {
  68. const targetNode = document.getElementById("jspPane_scroll");
  69. const config = { childList: true, subtree: true };
  70. const callback = (mutationList, observer) => {
  71. const resolution = document.querySelector(
  72. 'input[name="resolution"]:checked'
  73. ).value;
  74. for (const mutation of mutationList) {
  75. if (mutation.type === "childList") {
  76. const target = mutation.target.querySelector("img");
  77. if (target) target.src = target.src.replace(/2f$/, resolution);
  78. }
  79. }
  80. };
  81.  
  82. // Create an observer instance linked to the callback function
  83. const observer = new MutationObserver(callback);
  84.  
  85. // Start observing the target node for configured mutations
  86. observer.observe(targetNode, config);
  87. }
  88.  
  89. async function download(e) {
  90. e.preventDefault();
  91. e.target.disabled = true;
  92. await solveSrc().then(solveImg).then(solvePDF);
  93. e.target.disabled = false;
  94. }
  95.  
  96. /**
  97. * 解析pdf图片链接
  98. */
  99. async function solveSrc() {
  100. async function downloadSrcInfo(url) {
  101. return fetch(url)
  102. .then((res) => res.json())
  103. .then((json) => {
  104. finished++;
  105. msgBox.innerHTML = finished + "/" + page;
  106. return json.list;
  107. });
  108. }
  109.  
  110. let urlPromise = [];
  111. let page = 0;
  112. let finished = 0;
  113. for (; page < totalPage; page++) {
  114. const url = baseUrl + "&page=" + page;
  115. urlPromise.push(downloadSrcInfo(url));
  116. msgBox.innerHTML = finished + "/" + page;
  117. }
  118. return Promise.all(urlPromise);
  119. }
  120.  
  121. /**
  122. * 下载图片
  123. */
  124. async function solveImg(urls) {
  125. async function downloadPdf(url, i) {
  126. return fetch(url)
  127. .then((res) => res.blob())
  128. .then((blob) => {
  129. const reader = new FileReader();
  130. reader.readAsDataURL(blob);
  131. return new Promise((resolve) => {
  132. reader.onloadend = () => {
  133. resolve(reader.result);
  134. numFinished++;
  135. msgBox.innerHTML = numFinished + "/" + numTotal;
  136. };
  137. });
  138. });
  139. }
  140.  
  141. // remove duplicated
  142. const map = new Map();
  143. const resolution = localStorage.getItem(RESOLUTION) || "2f";
  144. urls.forEach((triple) => {
  145. triple.forEach((item) => {
  146. map.set(item.id, item.src.replace(/2f$/, resolution));
  147. });
  148. });
  149.  
  150. // sort and clear index
  151. urls = [...map.entries()]
  152. .sort((a, b) => a[0] - b[0])
  153. .map((item) => item[1]);
  154.  
  155. // download images
  156. const base64Promise = [];
  157. let numFinished = 0;
  158. let numTotal = 0;
  159. urls.forEach((url) => {
  160. base64Promise.push(downloadPdf(url));
  161. numTotal++;
  162. msgBox.innerHTML = numFinished + "/" + numTotal;
  163. });
  164.  
  165. return Promise.all(base64Promise);
  166. }
  167.  
  168. /**
  169. * 拼接为pdf
  170. * @param {*} base64s
  171. */
  172. async function solvePDF(base64s) {
  173. msgBox.innerHTML = "拼接中";
  174. const doc = new jspdf.jsPDF();
  175. base64s.forEach((base64, index) => {
  176. doc.addImage(base64, "JPEG", 0, 0, 210, 297);
  177. index + 1 == base64s.length || doc.addPage();
  178. });
  179. msgBox.innerHTML = "保存中";
  180. doc.save(document.title + ".pdf");
  181. msgBox.innerHTML = "完成!";
  182. }
  183. })();