Another cmoa.jp Downloader

用于网站cmoa.jp的下载。在顶部菜单选项右侧点击download按钮开始下载。按钮没有显示的话尝试重新打开菜单栏。图片每50页分为一部分进行压缩下载,压缩需要40-60秒,请不要关闭或刷新页面。

  1. // ==UserScript==
  2. // @name Another cmoa.jp Downloader
  3. // @namespace Ziran
  4. // @version 2.3.0
  5. // @description 用于网站cmoa.jp的下载。在顶部菜单选项右侧点击download按钮开始下载。按钮没有显示的话尝试重新打开菜单栏。图片每50页分为一部分进行压缩下载,压缩需要40-60秒,请不要关闭或刷新页面。
  6. // @author Ziran
  7. // @match https://www.cmoa.jp/bib/*
  8. // @match https://yomiho.cmoa.jp/bib/*
  9. // @require https://cdn.jsdelivr.net/npm/file-saver@2.0.2/dist/FileSaver.min.js
  10. // @require https://cdn.jsdelivr.net/npm/jszip@3.2.0/dist/jszip.min.js
  11. // @grant none
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. "use strict";
  17.  
  18. function addSpeedreaderCanvas() {
  19. let canvas = document.createElement("canvas");
  20. canvas.id = "temp-canvas";
  21. canvas.style.cssText = `position: fixed;
  22. top: 15%;
  23. left: 5%;
  24. height: 268px;
  25. width: 187px;
  26. background-color: pink;
  27. z-index: 19;
  28. visibility:hidden;`;
  29.  
  30. document.body.appendChild(canvas);
  31. }
  32.  
  33. function addSpeedreaderRadio() {
  34. let downloadingMessage = document.createElement("span");
  35. downloadingMessage.id = "downloading-message";
  36. downloadingMessage.innerText = "downloading……";
  37. downloadingMessage.style.cssText = `position: fixed;
  38. top: 10px;
  39. right: 80px;
  40. font-size: 16px;
  41. font-weight: 500;
  42. visibility:hidden;`;
  43.  
  44. let menu_header = document.getElementById("menu_header");
  45. menu_header.appendChild(downloadingMessage);
  46. }
  47.  
  48. function addSpeedreaderButton() {
  49. let button = document.createElement("button");
  50. button.id = "download-button";
  51. button.innerHTML = "download";
  52. button.style.cssText = `position: fixed;
  53. top: 11px;
  54. right: 80px;
  55. font-size:16px;
  56. border-style: none;
  57. text-align:center;
  58. vertical-align:baseline;
  59. cursor:pointer`;
  60.  
  61. button.onclick = async function () {
  62. changeDownloadStatus("on");
  63. let zip = new JSZip();
  64. let folder = zip.folder(__sreaderFunc__.contentInfo.items[0].SubTitle);
  65. let partCounter = 0;
  66. let partPages = 50;
  67. for (let i = 0; i < __sreaderFunc__.currentPageInfo.endPageNumber; i++) {
  68. partCounter = i + 1;
  69. if (partCounter % partPages == 0) {
  70. await zip.generateAsync({ type: "blob" })
  71. .then(function (content) {
  72. saveAs(content, __sreaderFunc__.contentInfo.items[0].SubTitle + "_part" + Math.floor(partCounter / partPages).toString());
  73. });
  74. zip = new JSZip();
  75. folder = zip.folder(__sreaderFunc__.contentInfo.items[0].SubTitle);
  76. }
  77. __sreaderFunc__.moveTo(i, !1);
  78. let content = document.getElementById("content-p" + (i + 1).toString());
  79. while (!content.hasChildNodes()) {
  80. await sleep(200);
  81. }
  82. let ptimg = content.firstElementChild;
  83. while (!ptimg.hasChildNodes()) {
  84. await sleep(200);
  85. ptimg = content.firstElementChild;
  86. }
  87.  
  88. let canvas = document.getElementById("temp-canvas");
  89. let ctx = canvas.getContext('2d');
  90.  
  91. let imgPartParent = ptimg.firstElementChild;
  92. let imgPart = imgPartParent.firstElementChild;
  93. canvas.height = imgPart.naturalHeight * ptimg.offsetHeight / imgPartParent.offsetHeight;
  94. canvas.width = canvas.height * ptimg.offsetWidth / ptimg.offsetHeight;
  95.  
  96. for (let i = 0; i < 3; i++) {
  97. let imgPart = imgPartParent.firstElementChild;
  98. while (!imgPart.complete) {
  99. await sleep(200);
  100. }
  101.  
  102. let insetArr = imgPartParent.style.inset.split('%');
  103. for (let j = 0; j < 4 && j < insetArr.length; j++) {
  104. insetArr[j] = insetArr[j].trim();
  105. insetArr[j] = parseFloat(insetArr[j]) / 100.0;
  106. }
  107. let imgHeight = canvas.height * (1 - insetArr[0] - insetArr[2]);
  108. let imgWidth = imgHeight * imgPart.naturalWidth / imgPart.naturalHeight;
  109. let imgY = canvas.height * insetArr[0];
  110. ctx.drawImage(imgPart, 0, imgY, imgWidth, imgHeight);
  111.  
  112. imgPartParent = imgPartParent.nextElementSibling;
  113. }
  114. folder.file((i + 1).toString() + ".png", canvas.toDataURL().split(',')[1], { base64: true });
  115. }
  116. if (partCounter % partPages != 0) {
  117. await zip.generateAsync({ type: "blob" })
  118. .then(function (content) {
  119. saveAs(content, __sreaderFunc__.contentInfo.items[0].SubTitle + "_part" + Math.ceil(partCounter / partPages).toString());
  120. });
  121. }
  122. changeDownloadStatus("off");
  123. };
  124.  
  125. let menu_header = document.getElementById("menu_header");
  126. menu_header.appendChild(button);
  127. }
  128.  
  129. function sleep(time) {
  130. return new Promise((resolve) => setTimeout(resolve, time));
  131. }
  132.  
  133. function changeDownloadStatus(status) {
  134. let button = document.getElementById("download-button");
  135. let downloadingMessage = document.getElementById("downloading-message");
  136. if (status == "on") {
  137. alert("开始下载,请稍候");
  138. button.style.visibility = "hidden";
  139. downloadingMessage.style.visibility = "visible";
  140. }
  141. else if (status == "off") {
  142. button.style.visibility = "visible";
  143. downloadingMessage.style.visibility = "hidden";
  144. }
  145. return
  146. }
  147.  
  148. function addReaderButton() {
  149. let button = document.createElement("button");
  150. button.id = "download-button";
  151. button.innerHTML = "download";
  152. button.style.cssText = `position: fixed;
  153. top: 11px;
  154. right: 80px;
  155. font-size:16px;
  156. border-style: none;
  157. text-align:center;
  158. vertical-align:baseline;
  159. cursor:pointer`;
  160.  
  161. button.onclick = async function () {
  162. alert("开始下载,请稍候");
  163. let text = ZOM0KK.ZZN0AE.value;
  164. let title = '';
  165. title = text.match(/(?<=<title>).*(?=<\/title>)/)[0];
  166. text = text.replace(/<head>.*?<\/head>/s, '');
  167. text = text.replace(/<t-param indent="0".*?>/g, '\n');
  168. text = text.replace(/<br.*?>/g, '\n');
  169. text = text.replace(/<.*?>/g, '');
  170.  
  171. let sss = text.match(/&\$.*?;/g, '');
  172. for (let i = 0; i < sss.length; i++) {
  173. text = text.replace(sss[i], theZ2Q0F5(sss[i].slice(2, sss[i].length - 1)));
  174. }
  175.  
  176. while (text[0] === '\n') {
  177. text = text.slice(1, text.length);
  178. }
  179.  
  180. let blob = new Blob([text], { type: "text/plain;charset=utf-8" });
  181. saveAs(blob, title + ".txt");
  182. };
  183.  
  184. let menu_header = document.getElementById("ctmble_menu_upper_holder");
  185. menu_header.appendChild(button);
  186. }
  187.  
  188. function theZ2Q0F5(ss) {
  189. let vs = Z2Q0F5(ss);
  190. let sb;
  191. let sp;
  192. let cd1;
  193. let cd2;
  194. ss = "";
  195. if (vs < 256) {
  196. sb = vs % 256;
  197. ss = String.fromCharCode(sb)
  198. } else if (vs >= 65536) {
  199. sp = vs - 65536;
  200. cd1 = 55296 | sp >> 10;
  201. cd2 = 56320 | sp & 1023;
  202. ss = String.fromCharCode(cd1, cd2)
  203. } else {
  204. ss = String.fromCharCode(vs)
  205. }
  206. return ss;
  207. }
  208.  
  209. // Run script
  210. let isButtonCreated = false;
  211. document.onclick = function (e) {
  212. if (document.documentURI.indexOf('cmoa.jp/bib/reader/') != -1) {
  213. if (!isButtonCreated && document.getElementById("ctmble_menu_upper_holder") != null) {
  214. addReaderButton();
  215. isButtonCreated = true;
  216. }
  217. }
  218. if (document.documentURI.indexOf('cmoa.jp/bib/speedreader/') != -1) {
  219. if (!isButtonCreated && document.getElementById("menu_header") != null) {
  220. addSpeedreaderButton();
  221. addSpeedreaderCanvas();
  222. addSpeedreaderRadio();
  223. isButtonCreated = true;
  224. }
  225. }
  226. }
  227. })();

QingJ © 2025

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