Kemono Download Tool

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

Устаревшая версия за 03.08.2023. Перейдите к последней версии.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==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("這裡沒帖子給你開");
    }
}