一键下载模型、图例、模型说明(附触发词)
// ==UserScript== // @name Civitai Tool // @namespace http://tampermonkey.net/ // @version 1.3 // @description 一键下载模型、图例、模型说明(附触发词) // @author 宇泽同学 // @match https://civitai.com/* // @grant GM_download // @license MIT // ==/UserScript== (function () { 'use strict'; var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = ` .custom-button { background: radial-gradient(circle, #00BFFF,#005ec9); /* 渐变色 */ color: #b3ffe7; width: 100px; height: 35px; padding: 5px 15px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; cursor: pointer; border: none; margin-left: 5px; border-radius: 7px; } .image-count-selector { width: 85px; /* 边框宽度 */ height: 35px; padding: 5px; border: 1px solid #23caff; /* 输入框描边色 */ border-radius: 7px; margin-left: 5px; display: inline-block; } `; document.head.appendChild(style); // 获取当前页面的URL中的模型ID function getModelIdFromUrl() { const urlRegex = /https:\/\/civitai.com\/models\/(\d+)(?:\/|$|\?modelVersionId=\d+)/; const match = window.location.href.match(urlRegex); return match ? match[1] : null; } // 从API获取模型文件名的函数 function fetchModelFileName(modelId, modelVersionId) { let apiUrl = `https://civitai.com/api/v1/models/${modelId}`; if (modelVersionId) { apiUrl += `?modelVersionId=${modelVersionId}`; } return fetch(apiUrl) .then(response => response.json()) .then(data => { if (modelVersionId) { const modelVersion = data.modelVersions.find(version => version.id.toString() === modelVersionId); return modelVersion ? modelVersion.files.find(file => file.primary).name : null; } else { const publishedVersion = data.modelVersions.find(version => version.status === 'Published'); return publishedVersion ? publishedVersion.files.find(file => file.primary).name : null; } }) .catch(error => { console.error('Error fetching model file name:', error); return null; }); } // 下载图例 function downloadImages(modelFileName, callback) { const numberOfImages = document.getElementById('imageCountInput').value; const thumbnailElements = document.querySelectorAll('.mantine-7aj0so'); for (let i = 0; i < Math.min(numberOfImages, thumbnailElements.length); i++) { const thumbnailSrc = thumbnailElements[i].src; const highResSrc = thumbnailSrc.replace('/width=450/', '/original=true/'); const filename = `${modelFileName.split('.')[0]}.preview.png`; GM_download(highResSrc, filename); } // 图例下载完成后执行回调函数 if (typeof callback === 'function') { callback(); } } // 下载说明 function downloadDescription(modelFileName, modelId) { // 使用Model ID构造API URL const apiUrl = `https://civitai.com/api/v1/models/${modelId}`; fetch(apiUrl) .then(response => response.json()) .then(data => { if (data.description) { let descriptionHtml = data.description; // API返回的HTML格式文本 // 将<p>和<br>标签替换为换行符,保留段落间的空行 descriptionHtml = descriptionHtml.replace(/<p>/gi, ""); descriptionHtml = descriptionHtml.replace(/<\/p>/gi, "\n\n"); descriptionHtml = descriptionHtml.replace(/<br\s*[\/]?>/gi, "\n"); // 创建一个DOM元素来解析HTML文本,并提取文本内容 const tempDiv = document.createElement('div'); tempDiv.innerHTML = descriptionHtml; // 获取纯文本内容,去除所有HTML标签 let plainTextDescription = tempDiv.textContent || tempDiv.innerText || ""; // 确保纯文本中的连续换行被保留 plainTextDescription = plainTextDescription.replace(/\n\s*\n/g, "\n\n"); // 获取触发词 const triggerWordElements = document.querySelectorAll('.mantine-Group-root.mantine-i72d0e'); let triggerWords = []; triggerWordElements.forEach(element => { const word = element.textContent.trim(); if (word) { triggerWords.push(word); } }); const triggerWordsText = triggerWords.length > 0 ? triggerWords.join(', ') : '未发现'; // 组合下载内容 const modelName = data.name || '模型名称未知'; const currentUrl = window.location.href; const combinedText = `##触发词: ${triggerWordsText}\n\n${plainTextDescription.trim()}\n\n##本模型地址:${currentUrl}\n\n`; // 触发下载 const blob = new Blob([combinedText], { type: 'text/plain;charset=utf-8' }); const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.download = `${modelFileName.split('.')[0]}.txt`; document.body.appendChild(link); link.click(); document.body.removeChild(link); } else { console.error('获取不到模型说明文本。'); } }).catch(error => { console.error('Error fetching model description:', error); }); } // 模拟原生下载按钮点击 function simulateNativeButtonClick() { const nativeDownloadButton = document.querySelector('.mantine-UnstyledButton-root.mantine-Button-root.mantine-4fe1an'); nativeDownloadButton && nativeDownloadButton.click(); } // 下载全部内容 function downloadAll(modelFileName, modelId) { downloadImages(modelFileName, () => { downloadDescription(modelFileName, modelId); setTimeout(() => { simulateNativeButtonClick(); }, 1000); }); } // 创建下载按钮 function createButton(text, onClick) { const button = document.createElement('button'); button.textContent = text; button.className = 'custom-button'; button.addEventListener('click', onClick); return button; } // 创建数字输入框 function createNumberInput() { const input = document.createElement('input'); input.className = 'image-count-selector'; input.id = 'imageCountInput'; input.type = 'number'; input.value = '1'; input.min = '1'; return input; } // 主函数 function main() { const modelId = getModelIdFromUrl(); const urlParams = new URLSearchParams(window.location.search); const modelVersionId = urlParams.get('modelVersionId'); if (modelId) { fetchModelFileName(modelId, modelVersionId).then(modelFileName => { if (modelFileName) { const imageCountInput = createNumberInput(); const downloadImagesButton = createButton('下载图例', () => downloadImages(modelFileName)); const downloadDescriptionButton = createButton('下载说明', () => downloadDescription(modelFileName, modelId)); // 更新这一行 const downloadAllButton = createButton('我全都要', () => downloadAll(modelFileName, modelId)); const buttonContainer = document.createElement('div'); buttonContainer.className = 'custom-button-container'; buttonContainer.appendChild(downloadImagesButton); buttonContainer.appendChild(imageCountInput); buttonContainer.appendChild(downloadDescriptionButton); buttonContainer.appendChild(downloadAllButton); const targetElement = document.querySelector('.mantine-UnstyledButton-root.mantine-Accordion-control.mantine-17tws88'); if (targetElement && !targetElement.parentNode.querySelector('.custom-button-container')) { targetElement.parentNode.insertBefore(buttonContainer, targetElement); } else { console.error('未找到目标元素,无法插入按钮容器。'); } } }); } } function checkAndInsertButtons() { setTimeout(() => { main(); checkAndInsertButtons(); }, 1000); } window.addEventListener('load', checkAndInsertButtons); // 监听网址变化 function checkUrlChange() { const currentUrl = window.location.href; if (currentUrl !== sessionStorage.getItem('previousUrl')) { sessionStorage.setItem('previousUrl', currentUrl); window.location.reload(); } } window.addEventListener('load', main); setInterval(checkUrlChange, 50); })();