Kemono Download Tool

一鍵下載圖片 [壓縮下載/單圖下載] , json 數據創建 (還沒添加)

Per 03-08-2023. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Kemono Download Tool
// @version      0.0.3
// @author       HentiSaru
// @description  一鍵下載圖片 [壓縮下載/單圖下載] , json 數據創建 (還沒添加)

// @match        *://kemono.su/*
// @match        *://*.kemono.su/*
// @match        *://kemono.party/*
// @match        *://*.kemono.party/*
// @icon         https://cdn-icons-png.flaticon.com/512/2381/2381981.png

// @license      MIT
// @run-at       document-end

// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_download
// @grant        GM_addElement
// @grant        GM_deleteValue
// @grant        GM_notification
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand

// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @namespace https://greasyfork.org/users/989635
// ==/UserScript==

var CompressMode = GM_getValue("壓縮下載", []), ModeDisplay;
(function() {
    const pattern = /^(https?:\/\/)?(www\.)?kemono\..+\/.+\/user\/.+\/post\/.+$/;
    if (pattern.test(window.location.href)) {setTimeout(ButtonCreation, 300)}
    GM_registerMenuCommand("🔁 切換下載模式", function() {DownloadModeSwitch()}, "C")
    GM_registerMenuCommand("📃 開啟當前頁面所有帖子", function() {OpenData()}, "O")
})();

async function DownloadModeSwitch() {
    if (GM_getValue("壓縮下載", [])){
        GM_setValue("壓縮下載", false);
        GM_notification({
            title: "模式切換",
            text: "單圖下載模式",
            timeout: 2500
        });
    } else {
        GM_setValue("壓縮下載", true);
        GM_notification({
            title: "模式切換",
            text: "壓縮下載模式",
            timeout: 2500
        });
    }
    location.reload();
}

async function ButtonCreation() {
    GM_addStyle(`
        .File_Span {
            padding: 1rem;
            font-size: 20% !important;
        }
        .Download_Button {
            color: hsl(0, 0%, 45%);
            padding: 6px;
            border-radius: 8px;
            border: 2px solid rgba(59, 62, 68, 0.7);
            background-color: rgba(29, 31, 32, 0.8);
            font-family: Arial, sans-serif;
        }
        .Download_Button:hover {
            color: hsl(0, 0%, 95%);
            background-color: hsl(0, 0%, 45%);
            font-family: Arial, sans-serif;
        }
    `);
    let download_button;
    try {
        const Files = document.querySelectorAll("div.post__body h2")
        const spanElement = GM_addElement(Files[Files.length - 1], "span", {class: "File_Span"});
        download_button = GM_addElement(spanElement, "button", {
            class: "Download_Button",
        });
        if (CompressMode) {
            ModeDisplay = "壓縮下載";
        } else {
            ModeDisplay = "單圖下載";
        }
        download_button.textContent = ModeDisplay;
        download_button.addEventListener("click", function() {
            DownloadTrigger(download_button);
        });
    } catch {
        download_button.textContent = "無法下載";
        download_button.disabled = true;
    }
}

function IllegalFilter(Name) {
    return Name.replace(/[\/\?<>\\:\*\|":]/g, '');
}
function Conversion(Name) {
    return Name.replace(/[\[\]]/g, '');
}

async function DownloadTrigger(button) {
    let interval = setInterval(function() {
        let imgdata = document.querySelectorAll("a.fileThumb.image-link");
        let title = document.querySelector("h1.post__title").textContent.trim();
        let user = document.querySelector("a.post__user-name").textContent.trim();
        if (imgdata && title && user) {
            button.textContent = "開始下載";
            if (CompressMode) {
                ZipDownload(`[${user}] ${title}`, imgdata, button);
            } else {
                ImageDownload(`[${user}] ${title}`, imgdata, button)
            }
            clearInterval(interval);
        }
    }, 500);
}

async function ZipDownload(Folder, ImgData, Button) {
    const zip = new JSZip(),
    Data = Object.values(ImgData),
    File = Conversion(Folder),
    Total = Data.length,
    name = IllegalFilter(Folder.split(" ")[1]);
    let pool = [], poolSize = 5, progress = 1, mantissa;
    function createPromise(i) {
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: Data[i].href.split("?f=")[0],
                responseType: "blob",
                onload: response => {
                    if (response.status === 200 && response.response instanceof Blob && response.response.size > 0) {
                        mantissa = progress.toString().padStart(3, '0');
                        zip.file(`${File}/${name}_${mantissa}.png`, response.response);
                        Button.textContent = `下載進度 [${progress}/${Total}]`;
                        progress++;
                    } else {
                        i--;
                    }
                    resolve();
                }
            });

        });
    }
    for (let i = 0; i < Total; i++) {
        let promise = createPromise(i);
        pool.push(promise);
        if (pool.length >= poolSize) {
            await Promise.all(pool);
            pool = [];
        }
    }
    if (pool.length > 0) {await Promise.all(pool)}
    Compression();
    function Compression() {
        Button.textContent = "壓縮封裝中[請稍後]";
        zip.generateAsync({
            type: "blob",
            compression: "DEFLATE",
            compressionOptions: {
                level: 5 // 壓縮級別,範圍從 0(無壓縮)到 9(最大壓縮)
            }
        }).then(zip => {
            Button.textContent = "下載完成";
            saveAs(zip, `${Folder}.zip`);
            setTimeout(() => {Button.textContent = ModeDisplay}, 4000);
        }).catch( result => {
            Button.textContent = "壓縮封裝失敗";
            setTimeout(() => {Button.textContent = ModeDisplay}, 6000);
        });
    }
}

async function ImageDownload(Folder, ImgData, Button) {
    const name = IllegalFilter(Folder.split(" ")[1]),
    Data = Object.values(ImgData),
    Total = Data.length;
    let progress = 1;
    for (let i = 0; i < Total; i++) {
        GM_download({
            url: Data[i].href.split("?f=")[0],
            name: `${name}_${(progress+i).toString().padStart(3, '0')}.png`,
            ontimeout: 5000,
            onload: () => {
                Button.textContent = `下載進度 [${progress}/${Total}]`;
                progress++;
            },
            onerror: () => {
                i--;
            }
        });
    }
}

function OpenData() {
    try {
        let content = document.querySelector('.card-list__items').querySelectorAll('article.post-card');
        content.forEach(function(content) {
            let link = content.querySelector('a').getAttribute('href');
            setTimeout(() => {
                window.open("https://kemono.party" + link , "_blank");
            }, 300);
        });
    } catch {
        alert("這裡沒帖子給你開");
    }
}