东立漫画下载

自动下载电子书城的漫画

  1. // ==UserScript==
  2. // @name 东立漫画下载
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.6
  5. // @description 自动下载电子书城的漫画
  6. // @author shadows
  7. // @license MIT License
  8. // @match https://ebook.tongli.com.tw/reader/FireBase3.html?bookID=*
  9. // @icon https://ebook.tongli.com.tw/images/logo_small.jpg
  10. // @grant none
  11. // @require https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.6.61/dist/zip.min.js
  12. // @run-at document-end
  13. // ==/UserScript==
  14. "use strict";
  15. const buttonCSS = `display: inline-block;
  16. background: linear-gradient(135deg, #6e8efb, #a777e3);
  17. color: white;
  18. padding: 3px 3px;
  19. margin: 4px 0px;
  20. text-align: center;
  21. border-radius: 3px;
  22. border-width: 0px;
  23. position: fixed;
  24. right: 0px;
  25. bottom: 0px;
  26. z-index: 99`;
  27.  
  28. addButton()
  29.  
  30. function addButton() {
  31. let button = document.createElement("button");
  32. button.classList.add("download-button");
  33. button.textContent = "Download";
  34. button.style.cssText = buttonCSS;
  35. button.onclick = clickButton;
  36. document.body.prepend(button);
  37. }
  38.  
  39. async function clickButton(event) {
  40. event.stopPropagation();
  41. event.preventDefault();
  42. const params = (new URL(document.location)).searchParams
  43. const bookID = params.get("bookID");
  44. const token = getCookie("token");
  45. let rawData = await fetch(`https://api.tongli.tw/Comic/sas/${bookID}`,{
  46. headers:{ 'Authorization': "Bearer " + token},
  47. }).then(res => res.json());
  48. const pages = rawData["Pages"];
  49. const name = rawData["Title"];
  50. const targetLength = pages.length.toString().length;
  51. let imagesData = [];
  52. //let zip = new JSZip();
  53. const blobWriter = new zip.BlobWriter("application/zip");
  54. const zipWriter = new zip.ZipWriter(blobWriter);
  55. for (let i of pages) {
  56. imagesData.push({
  57. filename: `${i.PageNumber.toString().padStart(targetLength,'0')}.jpg`,
  58. url: i.ImageURL,
  59. //zip: zip,
  60. zipWriter: zipWriter,
  61. })
  62. }
  63. await asyncPool(10, imagesData, imageWorker);
  64. //zip.generateAsync({ type: "blob", base64: true }).then(content => saveAs(content, `${name}.zip`));
  65. const zipFile = await zipWriter.close();
  66. saveBlob(zipFile , `${name}.zip`);
  67. }
  68.  
  69. async function imageWorker(item) {
  70. let image = await fetch(item.url).then(res => res.blob());
  71. console.log(`${item.filename} have downloaded.`);
  72. //item.zip.file(item.filename, image);
  73. await item.zipWriter.add(item.filename, new zip.BlobReader(image));
  74. }
  75.  
  76. /**
  77. * @param poolLimit 并发控制数 (>= 1)
  78. * @param array 参数数组
  79. * @param iteratorFn 异步任务,返回 promise 或是 async 方法
  80. * https://www.luanzhuxian.com/post/60c2c548.html
  81. */
  82. function asyncPool(poolLimit, array, iteratorFn) {
  83. let i = 0
  84. const ret = [] // Promise.all(ret) 的数组
  85. const executing = []
  86. const enqueue = function() {
  87. // array 遍历完,进入 Promise.all 流程
  88. if (i === array.length) {
  89. return Promise.resolve()
  90. }
  91.  
  92. // 每调用一次 enqueue,就初始化一个 promise,并放入 ret 队列
  93. const item = array[i++]
  94. const p = Promise.resolve().then(() => iteratorFn(item, array))
  95. ret.push(p)
  96.  
  97. // 插入 executing 队列,即正在执行的 promise 队列,并且 promise 执行完毕后,会从 executing 队列中移除
  98. const e = p.then(() => executing.splice(executing.indexOf(e), 1))
  99. executing.push(e)
  100.  
  101. // 每当 executing 数组中 promise 数量达到 poolLimit 时,就利用 Promise.race 控制并发数,完成的 promise 会从 executing 队列中移除,并触发 Promise.race 也就是 r 的回调,继续递归调用 enqueue,继续 加入新的 promise 任务至 executing 队列
  102. let r = Promise.resolve()
  103. if (executing.length >= poolLimit) {
  104. r = Promise.race(executing)
  105. }
  106.  
  107. // 递归,链式调用,直到遍历完 array
  108. return r.then(() => enqueue())
  109. }
  110.  
  111. return enqueue().then(() => Promise.all(ret))
  112. }
  113.  
  114. function saveBlob(content,name) {
  115. const fileUrl = window.URL.createObjectURL(content);
  116. const anchorElement = document.createElement('a');
  117. anchorElement.href = fileUrl;
  118. anchorElement.download = name;
  119. anchorElement.style.display = 'none';
  120. document.body.appendChild(anchorElement);
  121. anchorElement.click();
  122. anchorElement.remove();
  123. window.URL.revokeObjectURL(fileUrl);
  124. }
  125.  
  126. function getCookie(name) {
  127. let cookie = {};
  128. document.cookie.split(';').forEach(function(el) {
  129. let [k,v] = el.split('=');
  130. cookie[k.trim()] = v;
  131. })
  132. return cookie[name];
  133. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址