Advertisement
Guest User

Untitled

a guest
May 30th, 2025
33
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.68 KB | None | 0 0
  1. // ==UserScript==
  2. // @name CivitAI Gallery Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Download gallery images when downloading CivitAI model
  6. // @author You
  7. // @match https://civitai.com/models/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. let downloadStopped = false;
  15.  
  16. // Функция для скачивания файла
  17. async function downloadFile(url, filename) {
  18. try {
  19. const response = await fetch(url);
  20. if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  21. const blob = await response.blob();
  22.  
  23. const link = document.createElement('a');
  24. link.href = URL.createObjectURL(blob);
  25. link.download = filename;
  26. document.body.appendChild(link);
  27. link.click();
  28. document.body.removeChild(link);
  29. URL.revokeObjectURL(link.href);
  30.  
  31. } catch (error) {
  32. console.error(`Error downloading ${filename}:`, error);
  33. }
  34. }
  35.  
  36. // Функция для генерации случайного имени файла
  37. function generateRandomFilename() {
  38. const timestamp = Date.now();
  39. const random = Math.floor(Math.random() * 1000);
  40. return `${timestamp}_${random}.jpg`;
  41. }
  42.  
  43. // Функция для получения текущих изображений из галереи
  44. function getCurrentGalleryImages() {
  45. const images = [];
  46. const galleryContainer = document.querySelector('div.overflow-hidden > div.-ml-3.flex');
  47.  
  48. if (!galleryContainer) {
  49. return images;
  50. }
  51.  
  52. const imageLinks = galleryContainer.querySelectorAll('a[href^="/images/"]');
  53.  
  54. imageLinks.forEach((link) => {
  55. const img = link.querySelector('.EdgeImage_image__iH4_q');
  56. if (img && img.src && img.src.includes('image.civitai.com')) {
  57. const imageId = link.href.split('/images/')[1];
  58. const originalUrl = img.src.replace(/\/anim=false,width=\d+/, '');
  59. const filename = generateRandomFilename();
  60.  
  61. images.push({
  62. url: originalUrl,
  63. filename: filename,
  64. imageId: imageId
  65. });
  66. }
  67. });
  68.  
  69. return images;
  70. }
  71.  
  72. // Функция для поиска кнопки "следующий"
  73. function findNextButton() {
  74. // Ищем кнопку по классу tabler-icon-chevron-right
  75. const chevronRight = document.querySelector('svg.tabler-icon-chevron-right');
  76. if (chevronRight) {
  77. const button = chevronRight.closest('button');
  78. if (button && !button.hasAttribute('data-disabled') && button.style.opacity !== '0.25') {
  79. return button;
  80. }
  81. }
  82.  
  83. // Альтернативный поиск по структуре
  84. const buttons = document.querySelectorAll('button[class*="absolute"][class*="right-3"]');
  85. for (const button of buttons) {
  86. const svg = button.querySelector('svg.tabler-icon-chevron-right');
  87. if (svg && !button.hasAttribute('data-disabled') && button.style.opacity !== '0.25') {
  88. return button;
  89. }
  90. }
  91.  
  92. return null;
  93. }
  94.  
  95. // Функция для пролистывания галереи и сбора всех изображений
  96. async function collectAllGalleryImages() {
  97. const allImages = new Map(); // Используем Map для избежания дубликатов по imageId
  98. let attempts = 0;
  99. const maxAttempts = 100; // Максимальное количество попыток
  100. let noNewImagesCount = 0;
  101.  
  102. showProgress('Сканирование галереи...', false);
  103.  
  104. while (attempts < maxAttempts && noNewImagesCount < 5 && !downloadStopped) {
  105. const currentImages = getCurrentGalleryImages();
  106. let newImagesFound = false;
  107.  
  108. // Добавляем новые изображения
  109. currentImages.forEach(image => {
  110. if (!allImages.has(image.imageId)) {
  111. allImages.set(image.imageId, image);
  112. newImagesFound = true;
  113. }
  114. });
  115.  
  116. if (newImagesFound) {
  117. noNewImagesCount = 0;
  118. showProgress(`Найдено изображений: ${allImages.size}`, false);
  119. } else {
  120. noNewImagesCount++;
  121. }
  122.  
  123. // Ищем кнопку "следующий"
  124. const nextButton = findNextButton();
  125.  
  126. if (nextButton) {
  127. console.log(`Clicking next button, attempt ${attempts + 1}`);
  128. nextButton.click();
  129. await new Promise(resolve => setTimeout(resolve, 300)); // Ждем загрузки
  130. } else {
  131. console.log('Next button not found or disabled');
  132. break; // Больше нет кнопки "следующий"
  133. }
  134.  
  135. attempts++;
  136. }
  137.  
  138. console.log(`Collected ${allImages.size} unique images after ${attempts} attempts`);
  139. return Array.from(allImages.values());
  140. }
  141.  
  142. // Функция для показа прогресса
  143. function showProgress(message, showStopButton = true) {
  144. let progressDiv = document.getElementById('civitai-gallery-progress');
  145.  
  146. if (!progressDiv) {
  147. progressDiv = document.createElement('div');
  148. progressDiv.id = 'civitai-gallery-progress';
  149. progressDiv.style.cssText = `
  150. position: fixed;
  151. top: 80px;
  152. right: 20px;
  153. background: #333;
  154. color: white;
  155. padding: 15px;
  156. border-radius: 8px;
  157. z-index: 10000;
  158. font-family: Arial, sans-serif;
  159. font-size: 14px;
  160. max-width: 320px;
  161. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  162. `;
  163. document.body.appendChild(progressDiv);
  164. }
  165.  
  166. if (showStopButton) {
  167. progressDiv.innerHTML = `
  168. <div style="margin-bottom: 10px;">${message}</div>
  169. <button id="stop-download-btn" style="
  170. background: #dc3545;
  171. color: white;
  172. border: none;
  173. padding: 8px 16px;
  174. border-radius: 4px;
  175. cursor: pointer;
  176. font-size: 12px;
  177. width: 100%;
  178. ">Остановить скачивание</button>
  179. `;
  180.  
  181. const stopBtn = document.getElementById('stop-download-btn');
  182. if (stopBtn) {
  183. stopBtn.addEventListener('click', () => {
  184. downloadStopped = true;
  185. showProgress('❌ Скачивание остановлено', false);
  186. hideProgress(2000);
  187. });
  188. }
  189. } else {
  190. progressDiv.innerHTML = `<div>${message}</div>`;
  191. }
  192. }
  193.  
  194. // Функция для скрытия прогресса
  195. function hideProgress(delay = 3000) {
  196. setTimeout(() => {
  197. const progressDiv = document.getElementById('civitai-gallery-progress');
  198. if (progressDiv) {
  199. progressDiv.remove();
  200. }
  201. }, delay);
  202. }
  203.  
  204. // Функция для скачивания галереи
  205. async function downloadGallery() {
  206. try {
  207. downloadStopped = false;
  208.  
  209. // Сначала собираем все изображения, пролистывая галерею
  210. const allImages = await collectAllGalleryImages();
  211.  
  212. if (downloadStopped) {
  213. return;
  214. }
  215.  
  216. if (allImages.length === 0) {
  217. showProgress('Галерея модели не найдена', false);
  218. hideProgress(2000);
  219. return;
  220. }
  221.  
  222. showProgress(`Скачивание ${allImages.length} изображений...`, true);
  223.  
  224. // Скачиваем все собранные изображения
  225. for (let i = 0; i < allImages.length && !downloadStopped; i++) {
  226. const image = allImages[i];
  227. showProgress(`Скачивание ${i + 1}/${allImages.length}<br>Файл: ${image.filename}`, true);
  228.  
  229. await downloadFile(image.url, image.filename);
  230.  
  231. // Задержка между скачиваниями
  232. await new Promise(resolve => setTimeout(resolve, 600));
  233. }
  234.  
  235. if (!downloadStopped) {
  236. showProgress('✓ Галерея модели скачана!', false);
  237. hideProgress();
  238. }
  239.  
  240. } catch (error) {
  241. console.error('Error downloading gallery:', error);
  242. showProgress('Ошибка при скачивании галереи', false);
  243. hideProgress(5000);
  244. }
  245. }
  246.  
  247. // Функция для перехвата кликов по кнопке скачивания
  248. function interceptDownloadButtons() {
  249. const selectors = [
  250. 'a[data-tour="model:download"]',
  251. 'a[href*="/api/download/models/"]'
  252. ];
  253.  
  254. selectors.forEach(selector => {
  255. const buttons = document.querySelectorAll(selector);
  256. buttons.forEach(button => {
  257. if (!button.hasAttribute('data-gallery-intercepted')) {
  258. button.setAttribute('data-gallery-intercepted', 'true');
  259.  
  260. button.addEventListener('click', () => {
  261. console.log('Download button clicked, starting gallery download...');
  262. // Запускаем скачивание галереи через небольшую задержку
  263. setTimeout(downloadGallery, 1500);
  264. });
  265. }
  266. });
  267. });
  268. }
  269.  
  270. // Запускаем при загрузке страницы
  271. setTimeout(interceptDownloadButtons, 2000);
  272.  
  273. // Наблюдаем за изменениями DOM
  274. const observer = new MutationObserver(() => {
  275. interceptDownloadButtons();
  276. });
  277.  
  278. observer.observe(document.body, {
  279. childList: true,
  280. subtree: true
  281. });
  282.  
  283. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement