Clip Studio Reader Downloader

Download books from the browser version of Clip Studio Reader

目前為 2024-09-16 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Clip Studio Reader Downloader
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  Download books from the browser version of Clip Studio Reader
// @author       mrcoconuat
// @supportURL   https://github.com/MrCocoNuat/clip-studio-reader-downloader/issues
// @match        *://*/*
// @require      https://unpkg.com/[email protected]/dist/jszip.js
// @require      https://unpkg.com/[email protected]/dist/FileSaver.js
// @icon         https://www.google.com/s2/favicons?sz=64&domain=mobilebook.jp
// @license      MIT
// @grant        none
// ==/UserScript==

'use strict';

// Site-blind Clip Studio Reader integration support:
//------------------------------

const downloadButtonId = "download-button";
const errorMessageId = "error-message";

const ELEMENT = {
    SCREEN_CONTROLLER: 0, // used to flip pages
    CURRENT_PAGE_COUNTER: 1, // duh
    TOTAL_PAGE_COUNTER: 2, // duh
    LOADER_SPINNER: 3, // used to detect if the reader is loading a page
    MENU: 4, // used to detect if the menu must be raised since it contains the page scroller
    PAGE_SPREAD: 5, // contains the actual pages, and is checked to see if the reader as a whole has loaded
    PAGE_SLIDER: 7 // duh
}

const DOWNLOAD_MODE = {
    PAGE_BY_PAGE: 0, // each page must be descrambled and rendered individually. Usually these are paid books
    DIRECT: 1 // links to each page are accessible right from the start. Usually these are free samples or outright free books
}

// Data distribution for site-specific integrations:
//------------------------------

const siteSupport = {
    "mbj-bs.pf.mobilebook.jp": {
        mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
        ids: {
            [ELEMENT.SCREEN_CONTROLLER]: "screen_surface",
            [ELEMENT.CURRENT_PAGE_COUNTER]: "paging_slider_nombre_current",
            [ELEMENT.TOTAL_PAGE_COUNTER]: "paging_slider_nombre_total",
            [ELEMENT.LOADER_SPINNER]: "loading_spinner_layer",
            [ELEMENT.MENU]: "menu_layer",
            [ELEMENT.PAGE_SPREAD]: "spread_a",
            [ELEMENT.PAGE_SLIDER]: "paging_slider"
        },
        // sometimes the necessary element does not have an id, which sucks
        classes: {}
    },
    "api.distribution.mediadotech.com": {
        mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
        ids: {
            [ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
            [ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
            [ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
            [ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
            [ELEMENT.MENU]: "menu_container",
            [ELEMENT.PAGE_SPREAD]: "screen_layer"
        },
    },
    "comic-viewer.iowl.jp": {
        mode: DOWNLOAD_MODE.PAGE_BY_PAGE,
        ids: {
            [ELEMENT.SCREEN_CONTROLLER]: "screen_control_pad",
            [ELEMENT.CURRENT_PAGE_COUNTER]: "menu_nombre_current",
            [ELEMENT.TOTAL_PAGE_COUNTER]: "menu_nombre_total",
            [ELEMENT.LOADER_SPINNER]: "screen_loading_spinner_layer",
            [ELEMENT.MENU]: "menu_footer",
            [ELEMENT.PAGE_SPREAD]: "screen_layer",
        },
    },
    "comic.pixiv.net": {
        // readers can have extra conditions tacked on
        condition: () => document.location.pathname.substring(0, 7) === "/viewer",
        mode: DOWNLOAD_MODE.DIRECT,
        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
        classes: {
            [ELEMENT.PAGE_SPREAD]: "h-screen",
        },
    }
}

// try by ID if given, then by class
const getCSRElement = (elementEnum) => {
    return document.getElementById(siteSupport[window.location.hostname].ids?.[elementEnum])
        ?? document.getElementsByClassName(siteSupport[window.location.hostname].classes?.[elementEnum])[0];
}

// returns a list of direct image links
function getCSRPageHrefs() {
    // start from page 0, and go until null
    const result = [];
    for (let i = 0; /*blank*/; i++) {
        const href = siteSupport[window.location.hostname].href(i);
        if (href === undefined) {
            break;
        }
        result.push(href);
    }
    return result;
}

function siteIsSupported() {
    return !!siteSupport[document.location.hostname] && (siteSupport[document.location.hostname].condition?.() ?? true);
}

function downloadMode() {
    return siteSupport[document.location.hostname].mode;
}


init();


// SVG Handling
// Convert SVG to image (JPEG, PNG, etc.) in the browser
// Thanks, Thom Kiesewetter
// https://stackoverflow.com/a/58142441
//------------------------------

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">
  <defs>
    <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"/>
    <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"/>
  </defs>
  <g fill="none" fill-rule="evenodd" transform="translate(2 3)">
    <g transform="translate(2 6)">
      <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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
        <use xlink:href="#download-a"/>
      </mask>
      <use fill="#D8D8D8" fill-rule="nonzero" xlink:href="#download-a"/>
      <g fill="#FFA0A0" mask="url(#download-b)">
        <rect width="24" height="24" transform="translate(-4 -9)"/>
      </g>
    </g>
    <mask id="download-d" fill="#ffffff">
      <use xlink:href="#download-c"/>
    </mask>
    <use fill="#000000" fill-rule="nonzero" xlink:href="#download-c"/>
    <g fill="#7600FF" mask="url(#download-d)">
      <rect width="24" height="24" transform="translate(-2 -3)"/>
    </g>
  </g>
</svg>`

function svgToPng(svg, callback) {
    const url = getSvgUrl(svg);
    svgUrlToPng(url, (imgData) => {
        callback(imgData);
        URL.revokeObjectURL(url);
    });
}

function getSvgUrl(svg) {
    return URL.createObjectURL(new Blob([svg], {type: 'image/svg+xml'}));
}

function svgUrlToPng(svgUrl, callback) {
    const svgImage = document.createElement('img');
    // can't be display none, but also don't take up space
    svgImage.style.position = "fixed";
    svgImage.style.visibility = "hidden";
    document.body.appendChild(svgImage);
    svgImage.onload = function () {
        const canvas = document.createElement('canvas');
        canvas.width = svgImage.clientWidth;
        canvas.height = svgImage.clientHeight;
        const canvasCtx = canvas.getContext('2d');
        canvasCtx.drawImage(svgImage, 0, 0);
        const imgData = canvas.toDataURL('image/png');
        callback(imgData);
        // document.body.removeChild(imgPreview);
    };
    svgImage.src = svgUrl;
}


// Utility
//------------------------------

function error(str) {
    console.error(`[CSRD]: ${str}`);
}

function log(str) {
    console.log(`[CSRD]: ${str}`);
}

function info(str) {
    console.info(`[CSRD]: ${str}`);
}

function debug(str) {
    console.debug(`[CSRD]: ${str}`);
}

async function sleep(ms) {
    await new Promise(r => setTimeout(r, ms)); // give the browser a break - idle wait
}

// How to get the browser viewport dimensions?
// Thanks, ryanve
// https://stackoverflow.com/a/8876069
function viewportX() {
    return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
}

function viewportY() {
    return Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
}

function digitCount(num) {
    return num.toString().length;
}

function dataUrlToData(base64Url) {
    return base64Url.substr(base64Url.indexOf(',') + 1);
}


// Reader Utility
//------------------------------

// positive flips forward (increase page number), negative flips backwards (decrease page number), 0 is to open menu
function flipPage(direction) {
    const screen = getCSRElement(ELEMENT.SCREEN_CONTROLLER);
    // click on the left, middle, or right of the screen depending on arg
    const x = (-direction + 1) * viewportX() / 2;
    screen.dispatchEvent(new PointerEvent("pointerdown", {buttons: 1, clientX: x, clientY: 100, bubbles: true}));
    screen.dispatchEvent(new PointerEvent("pointerup", {buttons: 0, clientX: x, clientY: 100, bubbles: true}));
}

async function waitForPageLoad() {
    while (isLoadingPage()) {
        await sleep(100);
    }
}

function currentPage() {
    return +getCSRElement(ELEMENT.CURRENT_PAGE_COUNTER).textContent;
}

function totalPageCount() {
    return +getCSRElement(ELEMENT.TOTAL_PAGE_COUNTER).textContent;
}

function isLoadingPage() {
    return getCSRElement(ELEMENT.LOADER_SPINNER).classList.contains("onstage");
}

function isMenuOpen() {
    return getCSRElement(ELEMENT.MENU).style.display !== "none" && getCSRElement(ELEMENT.MENU).classList.contains("onstage");
}

async function flipToFirstPage() {
    const slider = getCSRElement(ELEMENT.PAGE_SLIDER);
    if (slider === undefined && currentPage() !== 1) {
        throw Error("This reader's automatic page slider is not supported, please move to page 1 manually and click the download button again");
    }
    if (currentPage() === 1) {
        info(`already on first page`);
        return;
    }

    info(`flipping to first page`);

    if (!isMenuOpen()) {
        debug("opening menu to load scroller");
        flipPage(0); // open menu
        await sleep(200);
    }

    // click the very right of the page slider - page 1
    slider.dispatchEvent(new PointerEvent("pointerdown", {
        buttons: 1,
        clientX: viewportX() - 25,
        clientY: viewportY() - 70,
        bubbles: true
    }));
    slider.dispatchEvent(new PointerEvent("pointerup", {
        buttons: 0,
        clientX: viewportX() - 25,
        clientY: viewportY() - 70,
        bubbles: true
    }));
    await sleep(100);
    await waitForPageLoad();
}


// Main
//------------------------------

async function generatePageByPageZip() {
    const jsZip = new JSZip();

    let totalPages = totalPageCount();
    if (totalPages === 0) {
        throw Error("The total number of pages reported by the reader is 0, the page slider seems to have been opened incorrectly");
    }

    info(`there are ${totalPages} pages in total to save`);
    const zeroPads = digitCount(totalPages);

    let pageNumber = 1;
    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
    // 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!)
    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

    log("==== downloading pages: ====");

    while (true) {
        const element_spread = getCSRElement(ELEMENT.PAGE_SPREAD);
        // Right to left means reverse the array
        for (const canvas of [...element_spread.children].toReversed()) {
            if (canvas.style.visibility === "hidden" || canvas.style.display === "none") {
                debug(`page before ${pageNumber} is a hidden or junk page, skipping it`);
                //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
                expectedPagesInSpread--;
                continue;
            }

            info(`saving page ${pageNumber}`);
            jsZip.file(`${pageNumber.toString().padStart(zeroPads, "0")}.png`, dataUrlToData(canvas.toDataURL()), {base64: true});
            pageNumber++;
        }

        if (pageNumber > totalPages) {
            break;
        }

        // Need to check after this canvas spread is completed and the page is flipped, what to do
        lastReportedReaderPageNumber = currentPage();
        await sleep(100);
        flipPage(1);
        await sleep(100);
        await waitForPageLoad();

        debug(`currentPage() - lastReportedReaderPageNumber = ${currentPage()} - ${lastReportedReaderPageNumber}`);
        if (currentPage() - lastReportedReaderPageNumber > expectedPagesInSpread) {
            // there are less pages than we thought! - the reader reports that currentPage() - lastDownloadedPageNumber passed (usually 2)
            // 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
            totalPages -= (currentPage() - lastReportedReaderPageNumber) - expectedPagesInSpread;
            debug(`cutting ${(currentPage() - lastReportedReaderPageNumber) - expectedPagesInSpread} from total pages, now ${totalPages}`)
        }
        expectedPagesInSpread = 2; // reset this

        await sleep(100);
    }

    return jsZip;
}

async function generateDirectZip() {
    const jsZip = new JSZip();
    const pageHrefs = getCSRPageHrefs();
    const totalPages = pageHrefs.length;
    info(`there are ${totalPages} pages in total to save`);
    const zeroPads = digitCount(totalPages);
    log("==== downloading pages: ====");

    for (const [i, href] of pageHrefs.entries()) {
        jsZip.file(`${i.toString().padStart(zeroPads, "0")}.png`, await (await fetch(href)).blob());
    }

    return jsZip;
}

function injectErrorMessage(message) {
    if (document.getElementById(errorMessageId) !== null) {
        document.getElementById(errorMessageId).remove();
    }

    const div = document.createElement("div");
    div.id = errorMessageId;
    div.style["position"] = "fixed";
    div.style["border-radius"] = "5%";
    div.style["z-index"] = 900;
    div.style["bottom"] = "150px";
    div.style["right"] = "32px";
    div.style["background"] = "black";
    div.style["border"] = "3px solid purple";
    div.style["padding"] = "8px";
    div.style["cursor"] = "pointer";
    div.style["color"] = "red";
    document.body.appendChild(div);
    const text = document.createTextNode(message);
    div.appendChild(text);
}

async function downloadBookAsZip() {
    let jsZip;
    try {
        switch (downloadMode()) {
            case DOWNLOAD_MODE.PAGE_BY_PAGE:
                if (!isMenuOpen()) {
                    info("opening menu to load scroller");
                    flipPage(0); // open menu
                    await sleep(200);
                }
                await flipToFirstPage();

                jsZip = await generatePageByPageZip();
                break;
            case DOWNLOAD_MODE.DIRECT:
                jsZip = await generateDirectZip();
                break;
        }
    } catch (exception) {
        injectErrorMessage(exception.message);
        throw exception;
    }

    log("generating zip file, rename it however you like - this script cannot figure out the book's name");
    await jsZip.generateAsync({type: "blob"}).then(blob => saveAs(blob, "Clip_Studio_Reader_Downloader_RENAME_ME.zip"));
    log("==== all done! Enjoy your book ^_^ ====");

}

function injectDownloadButton() {
    log("reader is loaded, download button injected");
    const parent = document.body;
    const div = document.createElement("div");
    parent.appendChild(div);
    div.id = downloadButtonId;
    // not all sites use tailwind
    div.style["position"] = "fixed";
    div.style["border-radius"] = "50%";
    div.style["z-index"] = 900;
    div.style["bottom"] = "32px";
    div.style["right"] = "32px";
    div.style["background"] = "black";
    div.style["border"] = "3px solid purple";
    div.style["padding"] = "8px";
    div.style["cursor"] = "pointer";
    div.addEventListener("pointerdown", downloadBookAsZip);
    svgToPng(downloadSvg, (imgData) => {
        const image = document.createElement('img');
        image.style.height = "64px";
        div.appendChild(image);
        image.src = imgData;
    });
}

function checkReaderLoad(observer, timeoutId) {
    if (currentPage() !== 0) {
        observer.disconnect();
        // stop the 30 second timeout
        clearTimeout(timeoutId);
        injectDownloadButton();
    }
}

// Userscript to wait for page to load before executing code techniques?
// Thanks, goweon
// https://stackoverflow.com/a/47406751
function checkPageLoad(observer) {
    if (getCSRElement(ELEMENT.PAGE_SPREAD)) {
        observer.disconnect();
        log("==== Clip Studio Reader Downloader ====");
        log("https://github.com/MrCocoNuat/clip-studio-reader-downloader");
        log("waiting up to 30 seconds for reader to load");

        switch (downloadMode()) {
            case DOWNLOAD_MODE.PAGE_BY_PAGE:
                let readerObserver;
                const timeoutId = setTimeout(async () => {
                    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?");
                    readerObserver.disconnect();
                }, 30000);
                readerObserver = new MutationObserver((changes, innerObserver) => checkReaderLoad(innerObserver, timeoutId));
                readerObserver.observe(getCSRElement(ELEMENT.CURRENT_PAGE_COUNTER), {childList: true, subtree: true});
                break;
            case DOWNLOAD_MODE.DIRECT:
                injectDownloadButton(); // not much to wait for
                break;
        }
    }
}


function init() {
    if (siteIsSupported()) {
        (new MutationObserver((changes, observer) => checkPageLoad(observer))).observe(document, {
            childList: true,
            subtree: true
        });
    } else {
        log("No instance of Clip Studio Reader found on the current page");
    }
}