Clip Studio Reader Downloader

Download books from the browser version of Clip Studio Reader

  1. // ==UserScript==
  2. // @name Clip Studio Reader Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.9
  5. // @description Download books from the browser version of Clip Studio Reader
  6. // @author mrcoconuat
  7. // @supportURL https://github.com/MrCocoNuat/clip-studio-reader-downloader/issues
  8. // @match *://*/*
  9. // @require https://unpkg.com/jszip@3.10.1/dist/jszip.js
  10. // @require https://unpkg.com/file-saver@2.0.5/dist/FileSaver.js
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=mobilebook.jp
  12. // @license MIT
  13. // @grant none
  14. // ==/UserScript==
  15.  
  16. 'use strict';
  17.  
  18. // Site-blind Clip Studio Reader integration support:
  19. //------------------------------
  20.  
  21. const downloadButtonId = "download-button";
  22. const errorMessageId = "error-message";
  23.  
  24. const ELEMENT = {
  25. SCREEN_CONTROLLER: 0, // used to flip pages
  26. CURRENT_PAGE_COUNTER: 1, // duh
  27. TOTAL_PAGE_COUNTER: 2, // duh
  28. LOADER_SPINNER: 3, // used to detect if the reader is loading a page
  29. MENU: 4, // used to detect if the menu must be raised since it contains the page scroller
  30. PAGE_SPREAD: 5, // contains the actual pages, and is checked to see if the reader as a whole has loaded
  31. PAGE_SLIDER: 7 // duh
  32. }
  33.  
  34. const DOWNLOAD_MODE = {
  35. PAGE_BY_PAGE: 0, // each page must be descrambled and rendered individually. Usually these are paid books
  36. DIRECT: 1 // links to each page are accessible right from the start. Usually these are free samples or outright free books
  37. }
  38.  
  39. // Data distribution for site-specific integrations:
  40. //------------------------------
  41.  
  42. const siteSupport = {
  43. "mbj-bs.pf.mobilebook.jp": {
  44. mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
  45. ids: {
  46. [ELEMENT.SCREEN_CONTROLLER]: "screen_surface",
  47. [ELEMENT.CURRENT_PAGE_COUNTER]: "paging_slider_nombre_current",
  48. [ELEMENT.TOTAL_PAGE_COUNTER]: "paging_slider_nombre_total",
  49. [ELEMENT.LOADER_SPINNER]: "loading_spinner_layer",
  50. [ELEMENT.MENU]: "menu_layer",
  51. [ELEMENT.PAGE_SPREAD]: "spread_a",
  52. [ELEMENT.PAGE_SLIDER]: "paging_slider"
  53. },
  54. // sometimes the necessary element does not have an id, which sucks
  55. classes: {}
  56. },
  57. "api.distribution.mediadotech.com": {
  58. mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
  59. ids: {
  60. [ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
  61. [ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
  62. [ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
  63. [ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
  64. [ELEMENT.MENU]: "menu_container",
  65. [ELEMENT.PAGE_SPREAD]: "screen_layer"
  66. },
  67. },
  68. "comic-viewer.iowl.jp": {
  69. mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
  70. ids: {
  71. [ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
  72. [ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
  73. [ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
  74. [ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
  75. [ELEMENT.MENU]: "menu_footer",
  76. [ELEMENT.PAGE_SPREAD]: "screen_layer",
  77. },
  78. },
  79. "comic.pixiv.net": {
  80. // readers can have extra conditions tacked on
  81. condition: () => document.location.pathname.substring(0, 7) === "/viewer",
  82. mode: DOWNLOAD_MODE.DIRECT,
  83. href: (pageNumber) => document.getElementById(`page-${pageNumber}`)?.style["background-image"].match(/url\("(.*)"\)/)?.[1], // TODO: these urls are not all loaded at start. So clicking it too fast will lose some ending pages
  84. classes: {
  85. [ELEMENT.PAGE_SPREAD]: "h-screen",
  86. },
  87. },
  88. "bs.comicdc.jp": {
  89. mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
  90. ids: {
  91. [ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
  92. [ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
  93. [ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
  94. [ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
  95. [ELEMENT.MENU]: "menu_container",
  96. [ELEMENT.PAGE_SPREAD]: "screen_layer"
  97. },
  98. }
  99. }
  100.  
  101. // try by ID if given, then by class
  102. const getCSRElement = (elementEnum) => {
  103. return document.getElementById(siteSupport[window.location.hostname].ids?.[elementEnum])
  104. ?? document.getElementsByClassName(siteSupport[window.location.hostname].classes?.[elementEnum])[0];
  105. }
  106.  
  107. // returns a list of direct image links
  108. function getCSRPageHrefs() {
  109. // start from page 0, and go until null
  110. const result = [];
  111. for (let i = 0; /*blank*/; i++) {
  112. const href = siteSupport[window.location.hostname].href(i);
  113. if (href === undefined) {
  114. break;
  115. }
  116. result.push(href);
  117. }
  118. return result;
  119. }
  120.  
  121. function siteIsSupported() {
  122. return !!siteSupport[document.location.hostname] && (siteSupport[document.location.hostname].condition?.() ?? true);
  123. }
  124.  
  125. function downloadMode() {
  126. return siteSupport[document.location.hostname].mode;
  127. }
  128.  
  129.  
  130. init();
  131.  
  132.  
  133. // SVG Handling
  134. // Convert SVG to image (JPEG, PNG, etc.) in the browser
  135. // Thanks, Thom Kiesewetter
  136. // https://stackoverflow.com/a/58142441
  137. //------------------------------
  138.  
  139. const downloadSvg = `<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  140. <defs>
  141. <path id="download-a" d="M4.29289322,1.70710678 C3.90236893,1.31658249 3.90236893,0.683417511 4.29289322,0.292893219 C4.68341751,-0.0976310729 5.31658249,-0.0976310729 5.70710678,0.292893219 L7.70710678,2.29289322 C8.09763107,2.68341751 8.09763107,3.31658249 7.70710678,3.70710678 C7.31658249,4.09763107 6.68341751,4.09763107 6.29289322,3.70710678 L4.29289322,1.70710678 Z M0,8 L16,8 L16,10 L0,10 L0,8 Z"/>
  142. <path id="download-c" d="M11,9.58578644 L13.2928932,7.29289322 C13.6834175,6.90236893 14.3165825,6.90236893 14.7071068,7.29289322 C15.0976311,7.68341751 15.0976311,8.31658249 14.7071068,8.70710678 L10.7071068,12.7071068 C10.3165825,13.0976311 9.68341751,13.0976311 9.29289322,12.7071068 L5.29289322,8.70710678 C4.90236893,8.31658249 4.90236893,7.68341751 5.29289322,7.29289322 C5.68341751,6.90236893 6.31658249,6.90236893 6.70710678,7.29289322 L9,9.58578644 L9,0.998529185 C9,0.447056744 9.44771525,-7.95978809e-15 10,-7.99360578e-15 C10.5522847,-8.02742346e-15 11,0.447056744 11,0.998529185 L11,9.58578644 Z M18,16 L18,10 C18,9.44771525 18.4477153,9 19,9 C19.5522847,9 20,9.44771525 20,10 L20,17 C20,17.5522847 19.5522847,18 19,18 L1,18 C0.44771525,18 0,17.5522847 0,17 L0,10 C0,9.44771525 0.44771525,9 1,9 C1.55228475,9 2,9.44771525 2,10 L2,16 L18,16 Z"/>
  143. </defs>
  144. <g fill="none" fill-rule="evenodd" transform="translate(2 3)">
  145. <g transform="translate(2 6)">
  146. <mask id="download-b" fill="#ffffff">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAMgCAYAAADbcAZoAAAgAElEQVR4XuzdCbQdVZkv8C8BQggQCFOHhBkEEQEVxLZRZEyQyW5QmWx9igiIgowtSveLovJkFhDDZKutgOKE2BKQEFAGlUEJLSozAcI8hoQQSPJOGPoiJDf7nqGqdtXPtVyre7mr9q7f/mpz/nz33Dso/IcAAQIECBAgQIAAAQIFCQwqaB7TECBAgAABAgQIECBAIAQQRUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAECBAgQIECAQGECAkhh1CYiQIAAAQIECBAgQEAAUQMECBAgQIAAAQIECBQmIIAURm0iAgQIECBAgAABAgQEEDVAgAABAgQIECBAgEBhAgJIYdQmIkCAAAECBAgQIEBAAFEDBAgQIECAAAECBAgUJiCAFEZtIgIECBAgQIAAAQIEBBA1QIAAAQIECBAgQIBAYQICSGHUJiJAgAABAgQIECBAQABRAwQIECBAgAABAgQIFCYggBRGbSICBAgQIECAAAECBAQQNUCAAAEC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swAigPS+ysxAgACB3gkIIL2zdWcCBFoC0y666Iwln356v0Fz5gyuAsicRRed/cyIEceO2HHHf6/CetpZgwAigLRTN64hQIBAVQQEkKrshHUQqJnAfddeu84KDzxwydDp07vy5fJu8zy3zDLXPzFq1PtXede7Hu/2vXt9PwFEAOl1jbk/AQIEeikggPRS170JNFTg0YkT9xzx4INnt75gvmSVCV4cMuTxp0aN+tCKW245qcrrfP3aBBABJKd6tVYCBAi8XkAAURMECHRV4LErrjhyxP33f23wnDmLdPXGPbpZ60eyZj0xcuRBK2677Zk9mqLrtxVABJCuF5UbEiBAoEABAaRAbFMRqLvA07/85b8Pf+yxL+f2nHMXWWTus8st92/Dd9zx+BzWLoAIIDnUqTUSIEBgQQICiNogQKArAo9NnHjscvfd9/lcD5XWX1OPp0aO/OZy22//ma6A9PAmAogA0sPycmsCBAj0XCDXzwo9hzEBAQLpAq3wccByDzxwelV+01X6yv9+5LwQ8vSoUaePGDPms+3eo4jrBBABpIg6MwcBAgR6JSCA9ErWfQk0RODRq6/earm77rq09Z2PxeryyE9WvBMigAggdXnXPAcBAs0UEECaue+emkBXBO69994RK1933d8Wmzlzxa7csCI3qXonRAARQCryqlgGAQIE2hIQQNpicxEBAvMEnvnv/75y6UcffV8dNaocQgQQAaSO75xnIkCgOQICSHP22pMS6KrA1Kuv3n/kHXd8q86HyEshZNVVTxmxzTaHdBWvw5sJIAJIhyXkcgIECJQqUOfPDqXCmpxA3QVmXnDBo4vPnLlC3Z8zBg2a2/qL6d9YfrvtKhNCBBABpPbvnQckQKDWAgJIrbfXwxHojUDr732c0fp7Hwf05u7VvOuTK6982nJjxx5UhdUJIAJIFerQGggQINCugADSrpzrCDRU4Nprr11is7vvfmyRF14Y1iiCCnVCBBABpFHvnoclQKB2AgJI7bbUAxHorcATEyacMeKhhxrV/fhf0YqEEAFEAOntW+7uBAgQ6K2AANJbX3cnUDuBWT/60TOLzZixdO0ebAAP9OwKKxy99E47fXUAl3R1qAAigHS1oNyMAAECBQsIIAWDm45AzgIPT5q030r33js+52foytpL7oQIIAJIV+rYTQgQIFCSgABSErxpCeQoMO3nP79m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
  147. <use xlink:href="#download-a"/>
  148. </mask>
  149. <use fill="#D8D8D8" fill-rule="nonzero" xlink:href="#download-a"/>
  150. <g fill="#FFA0A0" mask="url(#download-b)">
  151. <rect width="24" height="24" transform="translate(-4 -9)"/>
  152. </g>
  153. </g>
  154. <mask id="download-d" fill="#ffffff">
  155. <use xlink:href="#download-c"/>
  156. </mask>
  157. <use fill="#000000" fill-rule="nonzero" xlink:href="#download-c"/>
  158. <g fill="#7600FF" mask="url(#download-d)">
  159. <rect width="24" height="24" transform="translate(-2 -3)"/>
  160. </g>
  161. </g>
  162. </svg>`
  163.  
  164. function svgToPng(svg, callback) {
  165. const url = getSvgUrl(svg);
  166. svgUrlToPng(url, (imgData) => {
  167. callback(imgData);
  168. URL.revokeObjectURL(url);
  169. });
  170. }
  171.  
  172. function getSvgUrl(svg) {
  173. return URL.createObjectURL(new Blob([svg], {type: 'image/svg+xml'}));
  174. }
  175.  
  176. function svgUrlToPng(svgUrl, callback) {
  177. const svgImage = document.createElement('img');
  178. // can't be display none, but also don't take up space
  179. svgImage.style.position = "fixed";
  180. svgImage.style.visibility = "hidden";
  181. document.body.appendChild(svgImage);
  182. svgImage.onload = function () {
  183. const canvas = document.createElement('canvas');
  184. canvas.width = svgImage.clientWidth;
  185. canvas.height = svgImage.clientHeight;
  186. const canvasCtx = canvas.getContext('2d');
  187. canvasCtx.drawImage(svgImage, 0, 0);
  188. const imgData = canvas.toDataURL('image/png');
  189. callback(imgData);
  190. // document.body.removeChild(imgPreview);
  191. };
  192. svgImage.src = svgUrl;
  193. }
  194.  
  195.  
  196. // Utility
  197. //------------------------------
  198.  
  199. function error(str) {
  200. console.error(`[CSRD]: ${str}`);
  201. }
  202.  
  203. function log(str) {
  204. console.log(`[CSRD]: ${str}`);
  205. }
  206.  
  207. function info(str) {
  208. console.info(`[CSRD]: ${str}`);
  209. }
  210.  
  211. function debug(str) {
  212. console.debug(`[CSRD]: ${str}`);
  213. }
  214.  
  215. async function sleep(ms) {
  216. await new Promise(r => setTimeout(r, ms)); // give the browser a break - idle wait
  217. }
  218.  
  219. // How to get the browser viewport dimensions?
  220. // Thanks, ryanve
  221. // https://stackoverflow.com/a/8876069
  222. function viewportX() {
  223. return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
  224. }
  225.  
  226. function viewportY() {
  227. return Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
  228. }
  229.  
  230. function digitCount(num) {
  231. return num.toString().length;
  232. }
  233.  
  234. function dataUrlToData(base64Url) {
  235. return base64Url.substr(base64Url.indexOf(',') + 1);
  236. }
  237.  
  238.  
  239. // Reader Utility
  240. //------------------------------
  241.  
  242. // positive flips forward (increase page number), negative flips backwards (decrease page number), 0 is to open menu
  243. function flipPage(direction) {
  244. const screen = getCSRElement(ELEMENT.SCREEN_CONTROLLER);
  245. // click on the left, middle, or right of the screen depending on arg
  246. const x = (-direction + 1) * viewportX() / 2;
  247. screen.dispatchEvent(new PointerEvent("pointerdown", {buttons: 1, clientX: x, clientY: 100, bubbles: true}));
  248. screen.dispatchEvent(new PointerEvent("pointerup", {buttons: 0, clientX: x, clientY: 100, bubbles: true}));
  249. }
  250.  
  251. async function waitForPageLoad() {
  252. while (isLoadingPage()) {
  253. await sleep(100);
  254. }
  255. }
  256.  
  257. function currentPage() {
  258. return +getCSRElement(ELEMENT.CURRENT_PAGE_COUNTER).textContent;
  259. }
  260.  
  261. function totalPageCount() {
  262. return +getCSRElement(ELEMENT.TOTAL_PAGE_COUNTER).textContent;
  263. }
  264.  
  265. function isLoadingPage() {
  266. return getCSRElement(ELEMENT.LOADER_SPINNER).classList.contains("onstage");
  267. }
  268.  
  269. function isMenuOpen() {
  270. return getCSRElement(ELEMENT.MENU).style.display !== "none" && getCSRElement(ELEMENT.MENU).classList.contains("onstage");
  271. }
  272.  
  273. async function flipToFirstPage() {
  274. const slider = getCSRElement(ELEMENT.PAGE_SLIDER);
  275. if (slider === undefined && currentPage() !== 1) {
  276. throw Error("This reader's automatic page slider is not supported, please move to page 1 manually and click the download button again");
  277. }
  278. if (currentPage() === 1) {
  279. info(`already on first page`);
  280. return;
  281. }
  282.  
  283. info(`flipping to first page`);
  284.  
  285. if (!isMenuOpen()) {
  286. debug("opening menu to load scroller");
  287. flipPage(0); // open menu
  288. await sleep(200);
  289. }
  290.  
  291. // click the very right of the page slider - page 1
  292. slider.dispatchEvent(new PointerEvent("pointerdown", {
  293. buttons: 1,
  294. clientX: viewportX() - 25,
  295. clientY: viewportY() - 70,
  296. bubbles: true
  297. }));
  298. slider.dispatchEvent(new PointerEvent("pointerup", {
  299. buttons: 0,
  300. clientX: viewportX() - 25,
  301. clientY: viewportY() - 70,
  302. bubbles: true
  303. }));
  304. await sleep(100);
  305. await waitForPageLoad();
  306. }
  307.  
  308.  
  309. // Main
  310. //------------------------------
  311.  
  312. async function generatePageByPageZip() {
  313. const jsZip = new JSZip();
  314.  
  315. let totalPages = totalPageCount();
  316. if (totalPages === 0) {
  317. throw Error("The total number of pages reported by the reader is 0, the page slider seems to have been opened incorrectly");
  318. }
  319.  
  320. info(`there are ${totalPages} pages in total to save`);
  321. const zeroPads = digitCount(totalPages);
  322.  
  323. let pageNumber = 1;
  324. let lastReportedReaderPageNumber; // this helps distinguish a 1 page spread on 2 canvases, vs the reader always rendering 1 page only because the viewport is too narrow
  325. // any time the spread is not 2 canvases, this will no longer match pageNumber. But that is ok, we prioritize continuity of pageNumber instead (e.g. page 01, 02, 04 is bad!)
  326. let expectedPagesInSpread = 2; // a 2 page spread is normal. But we have to handle when the viewport is narrow enough that only 1 is shown
  327.  
  328. log("==== downloading pages: ====");
  329.  
  330. while (true) {
  331. const element_spread = getCSRElement(ELEMENT.PAGE_SPREAD);
  332. // Right to left means reverse the array
  333. for (const canvas of [...element_spread.children].toReversed()) {
  334. if (canvas.style.visibility === "hidden" || canvas.style.display === "none") {
  335. debug(`page before ${pageNumber} is a hidden or junk page, skipping it`);
  336. //possibly the unseen half of a 1 page spread on 2 canvases. Need to check after this canvas spread is completed and the page is flipped, what to do
  337. expectedPagesInSpread--;
  338. continue;
  339. }
  340.  
  341. info(`saving page ${pageNumber}`);
  342. jsZip.file(`${pageNumber.toString().padStart(zeroPads, "0")}.png`, dataUrlToData(canvas.toDataURL()), {base64: true});
  343. pageNumber++;
  344. }
  345.  
  346. if (pageNumber > totalPages) {
  347. break;
  348. }
  349.  
  350. // Need to check after this canvas spread is completed and the page is flipped, what to do
  351. lastReportedReaderPageNumber = currentPage();
  352. await sleep(100);
  353. flipPage(1);
  354. await sleep(100);
  355. await waitForPageLoad();
  356.  
  357. debug(`currentPage() - lastReportedReaderPageNumber = ${currentPage()} - ${lastReportedReaderPageNumber}`);
  358. if (currentPage() - lastReportedReaderPageNumber > expectedPagesInSpread) {
  359. // there are less pages than we thought! - the reader reports that currentPage() - lastDownloadedPageNumber passed (usually 2)
  360. // but we only saw expectedPages and incremented pageNumber by that much (usually 1 when this branch is entered)! decrement totalPages so we don't run over the totalPages with pageNumber
  361. totalPages -= (currentPage() - lastReportedReaderPageNumber) - expectedPagesInSpread;
  362. debug(`cutting ${(currentPage() - lastReportedReaderPageNumber) - expectedPagesInSpread} from total pages, now ${totalPages}`)
  363. }
  364. expectedPagesInSpread = 2; // reset this
  365.  
  366. await sleep(100);
  367. }
  368.  
  369. return jsZip;
  370. }
  371.  
  372. async function generateDirectZip() {
  373. const jsZip = new JSZip();
  374. const pageHrefs = getCSRPageHrefs();
  375. const totalPages = pageHrefs.length;
  376. info(`there are ${totalPages} pages in total to save`);
  377. const zeroPads = digitCount(totalPages);
  378. log("==== downloading pages: ====");
  379.  
  380. for (const [i, href] of pageHrefs.entries()) {
  381. jsZip.file(`${i.toString().padStart(zeroPads, "0")}.png`, await (await fetch(href)).blob());
  382. }
  383.  
  384. return jsZip;
  385. }
  386.  
  387. function injectErrorMessage(message) {
  388. if (document.getElementById(errorMessageId) !== null) {
  389. document.getElementById(errorMessageId).remove();
  390. }
  391.  
  392. const div = document.createElement("div");
  393. div.id = errorMessageId;
  394. div.style["position"] = "fixed";
  395. div.style["border-radius"] = "5%";
  396. div.style["z-index"] = 900;
  397. div.style["bottom"] = "150px";
  398. div.style["right"] = "32px";
  399. div.style["background"] = "black";
  400. div.style["border"] = "3px solid purple";
  401. div.style["padding"] = "8px";
  402. div.style["cursor"] = "pointer";
  403. div.style["color"] = "red";
  404. document.body.appendChild(div);
  405. const text = document.createTextNode(message);
  406. div.appendChild(text);
  407. }
  408.  
  409. async function downloadBookAsZip() {
  410. let jsZip;
  411. try {
  412. switch (downloadMode()) {
  413. case DOWNLOAD_MODE.PAGE_BY_PAGE:
  414. if (!isMenuOpen()) {
  415. info("opening menu to load scroller");
  416. flipPage(0); // open menu
  417. await sleep(200);
  418. }
  419. await flipToFirstPage();
  420.  
  421. jsZip = await generatePageByPageZip();
  422. break;
  423. case DOWNLOAD_MODE.DIRECT:
  424. jsZip = await generateDirectZip();
  425. break;
  426. }
  427. } catch (exception) {
  428. injectErrorMessage(exception.message);
  429. throw exception;
  430. }
  431.  
  432. log("generating zip file, rename it however you like - this script cannot figure out the book's name");
  433. await jsZip.generateAsync({type: "blob"}).then(blob => saveAs(blob, "Clip_Studio_Reader_Downloader_RENAME_ME.zip"));
  434. log("==== all done! Enjoy your book ^_^ ====");
  435.  
  436. }
  437.  
  438. function injectDownloadButton() {
  439. log("reader is loaded, download button injected");
  440. const parent = document.body;
  441. const div = document.createElement("div");
  442. parent.appendChild(div);
  443. div.id = downloadButtonId;
  444. // not all sites use tailwind
  445. div.style["position"] = "fixed";
  446. div.style["border-radius"] = "50%";
  447. div.style["z-index"] = 900;
  448. div.style["bottom"] = "32px";
  449. div.style["right"] = "32px";
  450. div.style["background"] = "black";
  451. div.style["border"] = "3px solid purple";
  452. div.style["padding"] = "8px";
  453. div.style["cursor"] = "pointer";
  454. div.addEventListener("pointerdown", downloadBookAsZip);
  455. svgToPng(downloadSvg, (imgData) => {
  456. const image = document.createElement('img');
  457. image.style.height = "64px";
  458. div.appendChild(image);
  459. image.src = imgData;
  460. });
  461. }
  462.  
  463. function checkReaderLoad(observer, timeoutId) {
  464. if (currentPage() !== 0) {
  465. observer.disconnect();
  466. // stop the 30 second timeout
  467. clearTimeout(timeoutId);
  468. injectDownloadButton();
  469. }
  470. }
  471.  
  472. // Userscript to wait for page to load before executing code techniques?
  473. // Thanks, goweon
  474. // https://stackoverflow.com/a/47406751
  475. function checkPageLoad(observer) {
  476. if (getCSRElement(ELEMENT.PAGE_SPREAD)) {
  477. observer.disconnect();
  478. log("==== Clip Studio Reader Downloader ====");
  479. log("https://github.com/MrCocoNuat/clip-studio-reader-downloader");
  480. log("waiting up to 30 seconds for reader to load");
  481.  
  482. switch (downloadMode()) {
  483. case DOWNLOAD_MODE.PAGE_BY_PAGE:
  484. let readerObserver;
  485. const timeoutId = setTimeout(async () => {
  486. error("ERR: reader load timeout. the reader seems to have started incorrectly or the reader may have taken too long to load - do you need to reopen the book?");
  487. readerObserver.disconnect();
  488. }, 30000);
  489. readerObserver = new MutationObserver((changes, innerObserver) => checkReaderLoad(innerObserver, timeoutId));
  490. readerObserver.observe(getCSRElement(ELEMENT.CURRENT_PAGE_COUNTER), {childList: true, subtree: true});
  491. break;
  492. case DOWNLOAD_MODE.DIRECT:
  493. injectDownloadButton(); // not much to wait for
  494. break;
  495. }
  496. }
  497. }
  498.  
  499.  
  500. function init() {
  501. if (siteIsSupported()) {
  502. (new MutationObserver((changes, observer) => checkPageLoad(observer))).observe(document, {
  503. childList: true,
  504. subtree: true
  505. });
  506. } else {
  507. log("No instance of Clip Studio Reader found on the current page");
  508. }
  509. }