Sleazy Fork is available in English.

Civitai Text Downloader Mod

Downloadボタンをクリックするとファイルのダウンロードと同時にJSONと画像が保存されます。また、Detailsの下にJSONと画像をダウンロードするボタンを追加します。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name            Civitai Text Downloader Mod
// @name:ja         Civitai Text Downloader Mod
// @namespace       http://tampermonkey.net/
// @version         5.3.5
// @description     Click Download button will download the file and save the JSON and images at the same time. Also add a button to download JSON and images under details.
// @description:ja  Downloadボタンをクリックするとファイルのダウンロードと同時にJSONと画像が保存されます。また、Detailsの下にJSONと画像をダウンロードするボタンを追加します。
// @author          Takenoko3333
// @match           https://civitai.com/*
// @icon            https://civitai.com/favicon.ico
// @grant           GM.addStyle
// @grant           GM.xmlHttpRequest
// @connect         civitai.com
// @license         BSD
// ==/UserScript==

(function() {
    'use strict';
    GM.addStyle(`.ctd-button:not([data-disabled]) {color: #ffff00;}`);

    let file_id = null;
    let category = "";
    if (typeof MutationObserver !== 'undefined') {
        let lastUrl = location.href;
        let observerTriggered = false;
        const observer = new MutationObserver((mutations) => {
            if (lastUrl !== location.href) {
                lastUrl = location.href;
                observerTriggered = true;
                const interval = setInterval(function() {
                    downloadFiles();
                    if (file_id || category != "models" ) {
                        clearInterval(interval);
                    }
                }, 200);
            }
        });
        const config = { subtree: true, childList: true };
        observer.observe(document, config);
        // URL change not detected (initial access)
        setTimeout(() => {
            if (!observerTriggered) {
                if (isFirefox()) {
                    runDownloadFilesForFirefox();
                } else {
                    downloadFiles();
                }
            }
        }, 0);
    } else {
        // MutationObserver is not supported
        setInterval(function(){
            downloadFiles();
        }, 500);
    }

    function isFirefox() {
        const isFirefoxBrowser = typeof InstallTrigger !== 'undefined';
        return isFirefoxBrowser;
    }

    function runDownloadFilesForFirefox() {
        let counter = 0;
        const interval = setInterval(function() {
            downloadFiles();
            counter++;
            if (category != "models" || counter >= 3) {
                clearInterval(interval);
            }
        }, 500);
    }

    function createButton() {
        const newElement = document.createElement('a');
        newElement.className = 'mantine-UnstyledButton-root mantine-Button-root mantine-4fe1an json-image-download-button';
        newElement.type = 'button';
        newElement.setAttribute('data-button', 'true');
        newElement.style.marginTop = '8px';

        const innerDiv = document.createElement('div');
        innerDiv.className = 'mantine-3xbgk5 mantine-Button-inner';

        const innerSpan = document.createElement('span');
        innerSpan.className = 'mantine-qo1k2 mantine-Button-label';
        innerSpan.textContent = 'JSON and Image Download';

        innerDiv.appendChild(innerSpan);
        newElement.appendChild(innerDiv);

        const tableElement = document.querySelector('table');
        let currentElement = tableElement;
        while (currentElement && !currentElement.classList.contains('mantine-Accordion-item')) {
            currentElement = currentElement.parentElement;
        }
        if (currentElement) {
            currentElement.insertAdjacentElement('afterend', newElement);
        }
    }

    function downloadFiles() {
        file_id = null
        category = location.pathname.split("/")[1];
        if (category != "models") return;

        const codeElements = document.querySelectorAll('table code');
        codeElements.forEach((element, index) => {
            if (element.textContent === '@') {
                if (codeElements[index + 1]) {
                    file_id = codeElements[index + 1].textContent;
                }
            }
        });

        const jsonImageDownloadButton = document.querySelector('.json-image-download-button');
        if (file_id && !jsonImageDownloadButton) createButton();

        document.querySelectorAll('main a[type^="button"]').forEach(button => {
            if(!button.classList.contains("ctd-done")){
                button.addEventListener("click", function(){
                    const modelName = document.querySelector('main h1').textContent;
                    const spanElements = document.querySelectorAll('table span');
                    let strength = null;
                    let _id = location.pathname.split("/")[2];

                    spanElements.forEach((element, index) => {
                        if (/^Strength:/.test(element.textContent)) {
                            strength = element.textContent.split(":")[1].trim();
                        }
                    });

                    GM.xmlHttpRequest({
                        method: "GET",
                        url: "https://civitai.com/api/v1/models/" + _id,
                        onload: function(response) {
                            let j = JSON.parse(response.responseText);
                            let file = j.modelVersions.find(x => x.id == file_id);
                            let link = document.createElement('a');
                            let text = {
                                "description": "",
                                "model name": modelName,
                                "model url": document.URL,
                                "base model": file.baseModel,
                                "sd version": "Unknown",
                                "activation text": "",
                                "preferred weight": 0,
                                "notes": document.URL + "\nModel name: " + modelName + "\nBase model: " + file.baseModel
                            };

                            if (/^SD 1/.test(file.baseModel)) {
                                text["sd version"] = "SD1";
                            } else if (/^SD 2/.test(file.baseModel)) {
                                text["sd version"] = "SD2";
                            } else if (/^SDXL/.test(file.baseModel) || /^Pony/.test(file.baseModel)) {
                                text["sd version"] = "SDXL";
                            } else if (/^SD 3/.test(file.baseModel)) {
                                text["sd version"] = "SD3";
                            }

                            if (j.description && j.description.textContent) {
                                text.description = j.description.textContent;
                            }
                            if (file.trainedWords) {
                                text["activation text"] = file.trainedWords.join(" ");
                            }
                            if (strength) {
                                text["preferred weight"] = strength;
                            }
                            text = [JSON.stringify(text)];
                            link.href = window.URL.createObjectURL(new Blob(text));
                            let filename = file.files.find(x => x).name || file_id + ".json";
                            filename = filename.replace(/\.[a-z]*$/, ".json")
                            link.download = filename;
                            link.click();

                            let image = file.images.find(x =>x.type === 'image');
                            if (image) {
                                GM.xmlHttpRequest({
                                    method: "GET",
                                    url: image.url,
                                    responseType: "blob",
                                    onload: function (response) {
                                        let dlLink = document.createElement("a");
                                        const dataUrl = URL.createObjectURL(response.response);
                                        dlLink.href = dataUrl;
                                        let suffix = "." + image.url.slice(image.url.lastIndexOf('.') + 1).toLowerCase();
                                        if (suffix === ".jpeg") {
                                            suffix = ".jpg";
                                        }
                                        filename = filename.replace(".json", suffix)
                                        dlLink.download = filename;
                                        document.body.insertAdjacentElement("beforeEnd", dlLink);
                                        dlLink.click();
                                        dlLink.remove();
                                        setTimeout(function () {
                                            window.URL.revokeObjectURL(dataUrl);
                                        }, 1000);
                                    }
                                });
                            }
                        }
                    });
                });
                if (file_id && !jsonImageDownloadButton) button.classList.add("ctd-button");
                button.classList.add("ctd-done");
            }
        });
    }
})();