diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf6dd4465..f790956f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Change Log for SD.Next
-## Update for 2025-08-02
+## Update for 2025-08-04
- **Models**
- [FLUX.1-Krea-Dev](https://www.krea.ai/blog/flux-krea-open-source-release)
@@ -14,15 +14,21 @@
- new embedded docs/wiki search!
**Docs** search: fully-local and works in real-time on all document pages
**Wiki** search: uses github api to search online wiki pages
- - quicksettings reset button to restore all quicksettings to default values
- because things do sometimes get wrong...
- updated real-time hints, thanks @CalamitousFelicitousness
- - updated *models -> current* tab
+ - rewritten **CivitAI downloader**
+ in *models -> civitai*
+ - updated *models -> current* tab
- updated *models -> list models* tab
- updated *models -> metadata* tab
- - more css optimizations and styling
+ - quicksettings reset button to restore all quicksettings to default values
+ because things do sometimes get wrong...
+ - redesign *settings -> user interface*
- gallery bypass browser cache for thumbnails
- gallery safer delete operation
+ - more css optimizations and styling
+ - *hint*: card layout
+ card layout is used by networks, gallery, civitai search, etc.
+ you can change card size in *settings -> user interface*
- **Offloading**
- changed **default** values for offloading based on detected gpu memory
see [offloading docs](https://vladmandic.github.io/sdnext-docs/Offload/) for details
diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui
index 540353613..89c232814 160000
--- a/extensions-builtin/sdnext-modernui
+++ b/extensions-builtin/sdnext-modernui
@@ -1 +1 @@
-Subproject commit 540353613400c419f8f0d92d6569e1e695e04f03
+Subproject commit 89c232814a3a65f00783af962975ffb31ce336db
diff --git a/javascript/civitai.js b/javascript/civitai.js
index e61980770..417c08584 100644
--- a/javascript/civitai.js
+++ b/javascript/civitai.js
@@ -1,31 +1,76 @@
-// hack to get pythons str.format in js
-String.prototype.format = function (arguments) { // eslint-disable-line no-extend-native, func-names
+String.prototype.format = function (args) { // eslint-disable-line no-extend-native, func-names
let thisString = '';
for (let charPos = 0; charPos < this.length; charPos++) thisString += this[charPos];
- for (const key in arguments) { // eslint-disable-line guard-for-in
- error(key, arguments[key]);
+ for (const key in args) { // eslint-disable-line guard-for-in
const stringKey = `{${key}}`;
- thisString = thisString.replace(new RegExp(stringKey, 'g'), arguments[key]);
+ thisString = thisString.replace(new RegExp(stringKey, 'g'), args[key]);
}
return thisString;
};
+let selectedURL = '';
+let selectedName = '';
+let selectedType = '';
+
+function clearModelDetails() {
+ const el = gradioApp().getElementById('model-details') || gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
+ if (!el) return;
+ el.innerHTML = '';
+}
+
const modelDetailsHTML = `
-
-
{name}
-
Type: {type}
-
Tags: {tags}
-
NSFW: {nsfw}/{level}
-
Availability: {availability}
-
Downloads: {downloads}
-
Author: {creator}
-
{versions}
+
+

+
+
+ | Name | {name} |
+ | Type | {type} |
+ | Tags | {tags} |
+ | NSFW | {nsfw} | {level} |
+ | Availability | {availability} |
+ | Downloads | {downloads} |
+ | Author | {creator} |
+ | Description | {desc} |
+
+
+
+
+
+ | |
+ Version |
+ Type |
+ Base |
+ File |
+ Updated |
+ Size |
+ Availability |
+ Description |
+
+
+
+ {versions}
+
+
`;
+const modelVersionsHTML = `
+
+ | {url} |
+ {name} |
+ {type} |
+ {base} |
+ {file} |
+ {mtime} |
+ {size} |
+ {availability} |
+ {desc} |
+
+`;
+
async function modelCardClick(id) {
log('modelCardClick id', id);
- const el = gradioApp().getElementById('model-details');
+ const el = gradioApp().getElementById('model-details') || gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
if (!el) return;
const res = await fetch(`${window.api}/civitai?model_id=${encodeURI(id)}`);
if (!res || res.status !== 200) {
@@ -36,229 +81,49 @@ async function modelCardClick(id) {
log('modelCardClick data', data);
if (!data || data.length === 0) return;
data = data[0]; // assuming the first item is the one we want
- const obj = {
- name: data.name || 'unknown',
+
+ const versionsHTML = data.versions.map((v) => modelVersionsHTML.format({
+ url: `
\udb80\uddda
`,
+ name: v.name || 'unknown',
+ type: v.files[0]?.type || 'unknown',
+ base: v.base || 'unknown',
+ mtime: (new Date(v.mtime)).toLocaleDateString(),
+ availability: v.availability || 'unknown',
+ size: v.files[0]?.size ? `${(v.files[0].size / 1024 / 1024).toFixed(2)} MB` : 'unknown',
+ file: `
${v.files[0]?.name || 'unknown'}`,
+ desc: v.desc || 'no description available',
+ })).join('');
+ const url = `
${data.name || 'unknown'}`;
+ const creator = `
${data.creator || 'unknown'}`;
+ const images = data.versions.map((v) => v.images).flat().map((i) => i.url); // TODO image gallery
+ const modelHTML = modelDetailsHTML.format({
+ name: url,
type: data.type || 'unknown',
tags: data.tags?.join(', ') || '',
nsfw: data.nsfw ? 'yes' : 'no',
level: data.level?.toString() || '',
availability: data.availability || 'unknown',
downloads: data.downloads?.toString() || '',
- creator: data.creator || 'unknown',
- versions: JSON.stringify(data.versions) || '[]',
- };
- log(obj);
- el.innerHTML = modelDetailsHTML.format({
- name: data.name || 'unknown',
- type: data.type || 'unknown',
- tags: data.tags?.join(', ') || '',
- nsfw: data.nsfw ? 'yes' : 'no',
- level: data.level?.toString() || '',
- availability: data.availability || 'unknown',
- downloads: data.downloads?.toString() || '',
- creator: data.creator || 'unknown',
- versions: JSON.stringify(data.versions) || '[]',
+ creator,
+ desc: data.desc || 'no description available',
+ image: images.length > 0 ? images[0] : './sd_extra_networks/thumb?filename=html/card-no-preview.png',
+ versions: versionsHTML || '',
});
+ el.innerHTML = modelHTML;
}
-const example = {
- id: 1157409,
- url: 'https://civitai.com/models/1157409',
- type: 'Checkpoint',
- name: 'Tempest-by-Vlad',
- html: '',
- desc: 'Base versionFlexible SDXL model with custom encoder and finetuned for larger landscape resolutions with high details and high contrast.Recommended to use medium-low...',
- tags: [
- 'base model',
- ],
- nsfw: false,
- level: 15,
- availability: 'Public',
- downloads: 407,
- creator: 'vmandic',
- versions: [
- {
- id: 1301775,
- name: 'Base v0.1',
- base: 'SDXL 1.0',
- mtime: '2025-01-19T02:53:53.903Z',
- downloads: 346,
- availability: 'Public',
- html: '',
- desc: 'Initial release',
- files: [
- {
- id: 1206102,
- size: 6938089790,
- name: 'tempestByVlad_baseV01.safetensors',
- type: 'Model',
- hashes: [
- '79CB1E32',
- '8BFAD17222',
- '8BFAD1722243955B3F94103C69079C280D348B14729251E86824972C1063B616',
- '43E5E3BB',
- 'DE83D56256411853AB6595CC3D8E865D5310D4A58D49A839DDC104C7F3429D4A',
- '4E933E1EBE61',
- ],
- url: 'https://civitai.com/api/download/models/1301775',
- dct: {},
- },
- ],
- images: [
- {
- id: 52503951,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/18c749f2-42ec-4024-9d20-0b1202b6bacc/width=1024/52503951.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 52508539,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/634d0ea8-ecdb-4ca6-a4ff-145319bc3fd3/width=1024/52508539.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 52508563,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/be529820-a89e-458f-8a3d-86cb43b154ac/width=1024/52508563.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 52508588,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/b2eb456a-a664-4de8-8c3e-6ecd1c4acb38/width=1024/52508588.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 52508654,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/f7b09e3f-4a48-459b-9b32-fa207904f74c/width=1024/52508654.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 52508659,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/fff89f18-628a-43b3-b9f5-44951dc078f7/width=1024/52508659.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 52508671,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/fdd3f774-ce2e-4ea7-82a0-bdb523fb86f6/width=1024/52508671.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 52512251,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/f26971c8-1123-45e3-a85b-2b97c6334b85/width=1024/52512251.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- ],
- dct: {},
- },
- {
- id: 1343512,
- name: 'Hyper v0.1',
- base: 'SDXL 1.0',
- mtime: '2025-01-28T22:54:12.734Z',
- downloads: 61,
- availability: 'Public',
- html: '',
- desc: 'Time-distilled version',
- files: [
- {
- id: 1246991,
- size: 6938085702,
- name: 'tempestByVlad_hyperV01.safetensors',
- type: 'Model',
- hashes: [
- '15943FD9',
- '4104FC6601',
- '4104FC6601F71C4C7A770AD422483FD700C8ECF72D06FCD8C4E8CD4B2D1C7DBB',
- '9F87BCEA',
- 'CB52894625E9C13331285E4435799D707C4EAEF464974159C8B4B217EA32298E',
- 'A0EE15E503DD',
- ],
- url: 'https://civitai.com/api/download/models/1343512',
- dct: {},
- },
- ],
- images: [
- {
- id: 54462987,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/1dd020c8-8a9a-4eb3-afec-fe83613217c5/width=1024/54462987.jpeg',
- width: 1024,
- height: 768,
- type: 'image',
- dct: {},
- },
- {
- id: 54462992,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/89edf233-939f-4b2e-97c8-175498704362/width=1536/54462992.jpeg',
- width: 1536,
- height: 640,
- type: 'image',
- dct: {},
- },
- {
- id: 54463002,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/87aea8fb-7687-48d0-98e6-27555b2ff87f/width=768/54463002.jpeg',
- width: 768,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 54463010,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/f1ee57cc-8920-4b8d-853a-ad1cfc7d9a5a/width=1024/54463010.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 54463011,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/dda6411b-fd36-484f-a9f0-0db847463128/width=1024/54463011.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 54463016,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/5f266a8c-f215-4e0f-8a64-aacc97f81d70/width=1024/54463016.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- {
- id: 54463019,
- url: 'https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/0ad04bdc-41bd-433c-9c25-f5365fbe082c/width=1024/54463019.jpeg',
- width: 1024,
- height: 1024,
- type: 'image',
- dct: {},
- },
- ],
- dct: {},
- },
- ],
- dct: {},
-};
+function startCivitDownload(url, name, type) {
+ log('startCivitDownload', { url, name, type });
+ selectedURL = url;
+ selectedName = name;
+ selectedType = type;
+ const civitDownloadBtn = gradioApp().getElementById('civitai_download_btn');
+ if (civitDownloadBtn) civitDownloadBtn.click();
+}
+
+function downloadCivitModel(modelUrl, modelName, modelType, modelPath, civitToken, innerHTML) {
+ log('downloadCivitModel', { modelUrl, modelName, modelType, modelPath, civitToken });
+ const el = gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
+ const currentHTML = el?.innerHTML || '';
+ return [selectedURL, selectedName, selectedType, modelPath, civitToken, currentHTML];
+}
diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js
index 1a9ae4fe6..a93cff798 100644
--- a/javascript/extraNetworks.js
+++ b/javascript/extraNetworks.js
@@ -110,8 +110,8 @@ function readCardDescription(page, item) {
function getCardsForActivePage() {
const pagename = getENActivePage();
if (!pagename) return [];
- const allCards = Array.from(gradioApp().querySelectorAll('.extra-network-cards > .card'));
- const cards = allCards.filter((el) => el.dataset.page.toLowerCase().includes(pagename.toLowerCase()));
+ let allCards = Array.from(gradioApp().querySelectorAll('.extra-network-cards > .card'));
+ allCards = allCards.filter((el) => el.dataset.page?.toLowerCase().includes(pagename.toLowerCase()));
// log('getCardsForActivePage', pagename, cards.length);
return allCards;
}
diff --git a/javascript/sdnext.css b/javascript/sdnext.css
index 2013cabdb..7c3874a30 100644
--- a/javascript/sdnext.css
+++ b/javascript/sdnext.css
@@ -66,6 +66,10 @@ button {
min-width: unset !important;
}
+h4 {
+ margin: 0.2em 0em 0.2em 0em;
+}
+
input[type='color'] {
height: 32px;
width: 64px;
@@ -122,6 +126,17 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
overflow: auto;
}
+.link {
+ background-color: var(--background-fill-primary);
+ cursor: pointer;
+ border-radius: var(--input-radius);
+ width: 2em;
+}
+
+.link:hover {
+ background-color: var(--button-primary-background-fill);
+}
+
.gradio-dropdown, .block.gradio-slider, .block.gradio-checkbox, .block.gradio-textbox, .block.gradio-radio, .block.gradio-checkboxgroup, .block.gradio-number, .block.gradio-colorpicker {
border-width: 0 !important;
box-shadow: none !important;
@@ -1226,11 +1241,13 @@ table.settings-value-table td {
}
.extra-network-cards .card {
- height: fit-content;
- margin: 0 0 0.5em 0.5em;
- position: relative;
- scroll-margin-top: 0;
- scroll-snap-align: start;
+ margin: 0 0 0.5em 0.5em;
+ position: relative;
+ scroll-margin-top: 0;
+ scroll-snap-align: start;
+ height: var(--card-size);
+ width: var(--card-size);
+ contain: strict;
}
.extra-network-cards .card .overlay {
@@ -1923,10 +1940,6 @@ div:has(>#tab-gallery-folders) {
padding: 0.2em;
}
-.docs-results {
- background-color: var(--sd-group-background-color);
-}
-
.docs-card {
margin: 1em 0;
background-color: var(--background-fill-primary);
@@ -1972,6 +1985,13 @@ div:has(>#tab-gallery-folders) {
overflow: auto;
}
+.model-config {
+ font-size: 0.8em !important;
+ opacity: 0.8;
+ max-height: 6em;
+ overflow-y: auto;
+}
+
.simple-table tr {
vertical-align: baseline;
}
@@ -1980,13 +2000,36 @@ div:has(>#tab-gallery-folders) {
padding: 0.2em !important;
}
-.model-config {
- font-size: 0.8em !important;
- opacity: 0.8;
- max-height: 6em;
+.simple-table tr {
+ vertical-align: baseline;
+}
+
+.simple-table thead tr {
+ background-color: var(--button-primary-border-color) !important;
+}
+
+.simple-table tr:nth-child(odd) {
+ background-color: var(--neutral-900);
+}
+
+.simple-table td {
+ padding: 0.2em !important;
+ white-space: pre-wrap;
+}
+
+.simple-table td div {
+ padding: 0.2em !important;
+ white-space: pre-wrap;
+ max-height: 7em;
+ overflow-x: hidden;
overflow-y: auto;
}
+.simple-table td:nth-child(1) {
+ color: var(--button-primary-border-color);
+ font-weight: bold;
+}
+
@keyframes move {
from {
background-position-x: 0, -40px;
diff --git a/modules/civitai/download_civitai.py b/modules/civitai/download_civitai.py
index e69de29bb..7e013454c 100644
--- a/modules/civitai/download_civitai.py
+++ b/modules/civitai/download_civitai.py
@@ -0,0 +1,191 @@
+import os
+import json
+import rich.progress as p
+from PIL import Image
+from modules import shared, errors, paths
+
+
+pbar = None
+
+
+def save_video_frame(filepath: str):
+ from modules import video
+ try:
+ frames, fps, duration, w, h, codec, frame = video.get_video_params(filepath, capture=True)
+ except Exception as e:
+ shared.log.error(f'Video: file={filepath} {e}')
+ return None
+ if frame is not None:
+ basename = os.path.splitext(filepath)
+ thumb = f'{basename[0]}.thumb.jpg'
+ shared.log.debug(f'Video: file={filepath} frames={frames} fps={fps} size={w}x{h} codec={codec} duration={duration} thumb={thumb}')
+ frame.save(thumb)
+ else:
+ shared.log.error(f'Video: file={filepath} no frames found')
+ return frame
+
+
+def download_civit_meta(model_path: str, model_id):
+ fn = os.path.splitext(model_path)[0] + '.json'
+ url = f'https://civitai.com/api/v1/models/{model_id}'
+ r = shared.req(url)
+ if r.status_code == 200:
+ try:
+ data = r.json()
+ shared.writefile(data, filename=fn, mode='w', silent=True)
+ shared.log.info(f'CivitAI download: id={model_id} url={url} file="{fn}"')
+ return r.status_code, len(data), '' # code/size/note
+ except Exception as e:
+ errors.display(e, 'civitai meta')
+ shared.log.error(f'CivitAI meta: id={model_id} url={url} file="{fn}" {e}')
+ return r.status_code, '', str(e)
+ return r.status_code, '', ''
+
+
+def download_civit_preview(model_path: str, preview_url: str):
+ global pbar # pylint: disable=global-statement
+ if model_path is None:
+ pbar = None
+ return 500, '', ''
+ ext = os.path.splitext(preview_url)[1]
+ preview_file = os.path.splitext(model_path)[0] + ext
+ is_video = preview_file.lower().endswith('.mp4')
+ is_json = preview_file.lower().endswith('.json')
+ if is_json:
+ shared.log.warning(f'CivitAI download: url="{preview_url}" skip json')
+ return 500, '', 'exepected preview image got json'
+ if os.path.exists(preview_file):
+ return 304, '', 'already exists'
+ # res = f'CivitAI download: url={preview_url} file="{preview_file}"'
+ r = shared.req(preview_url, stream=True)
+ total_size = int(r.headers.get('content-length', 0))
+ block_size = 16384 # 16KB blocks
+ written = 0
+ img = None
+ shared.state.begin('CivitAI')
+ if pbar is None:
+ pbar = p.Progress(p.TextColumn('[cyan]Download'), p.DownloadColumn(), p.BarColumn(), p.TaskProgressColumn(), p.TimeRemainingColumn(), p.TimeElapsedColumn(), p.TransferSpeedColumn(), p.TextColumn('[yellow]{task.description}'), console=shared.console)
+ try:
+ with open(preview_file, 'wb') as f:
+ with pbar:
+ task = pbar.add_task(description=preview_file, total=total_size)
+ for data in r.iter_content(block_size):
+ written = written + len(data)
+ f.write(data)
+ pbar.update(task, advance=block_size)
+ if written < 1024: # min threshold
+ os.remove(preview_file)
+ return 400, '', 'removed invalid download'
+ if is_video:
+ img = save_video_frame(preview_file)
+ else:
+ img = Image.open(preview_file)
+ except Exception as e:
+ shared.log.error(f'CivitAI download error: url={preview_url} file="{preview_file}" written={written} {e}')
+ return 500, '', str(e)
+ shared.state.end()
+ if img is None:
+ return 500, '', 'image is none'
+ shared.log.info(f'CivitAI download: url={preview_url} file="{preview_file}" size={total_size} image={img.size}')
+ img.close()
+ return 200, str(total_size), '' # code/size/note
+
+
+def download_civit_model_thread(model_name: str, model_url: str, model_path: str = "", model_type: str = "Model", token: str = None):
+ import hashlib
+ sha256 = hashlib.sha256()
+ sha256.update(model_url.encode('utf-8'))
+ temp_file = sha256.hexdigest()[:8] + '.tmp'
+
+ headers = {}
+ starting_pos = 0
+ if os.path.isfile(temp_file):
+ starting_pos = os.path.getsize(temp_file)
+ headers['Range'] = f'bytes={starting_pos}-'
+ if token is None or len(token) == 0:
+ token = shared.opts.civitai_token
+ if token is not None and len(token) > 0:
+ headers['Authorization'] = f'Bearer {token}'
+
+ r = shared.req(model_url, headers=headers, stream=True)
+ total_size = int(r.headers.get('content-length', 0))
+ if model_name is None or len(model_name) == 0:
+ cn = r.headers.get('content-disposition', '')
+ model_name = cn.split('filename=')[-1].strip('"')
+
+ model_path = model_path.strip()
+ if len(model_path) > 0:
+ if os.path.isabs(model_path):
+ pass
+ else:
+ model_path = os.path.join(paths.models_path, model_path)
+ elif model_type.lower() == 'lora':
+ model_path = shared.opts.lora_dir
+ elif model_type.lower() == 'embedding':
+ model_path = shared.opts.embeddings_dir
+ elif model_type.lower() == 'vae':
+ model_path = shared.opts.vae_dir
+ else:
+ model_path = shared.opts.ckpt_dir
+ model_file = os.path.join(model_path, model_name)
+ temp_file = os.path.join(model_path, temp_file)
+
+ res = f'Model download: name="{model_name}" url="{model_url}" path="{model_path}" temp="{temp_file}"'
+ if os.path.isfile(model_file):
+ res += ' already exists'
+ shared.log.warning(res)
+ return res
+
+ res += f' size={round((starting_pos + total_size)/1024/1024, 2)}Mb'
+ shared.log.info(res)
+ shared.state.begin('CivitAI')
+ block_size = 16384 # 16KB blocks
+ written = starting_pos
+ global pbar # pylint: disable=global-statement
+ if pbar is None:
+ pbar = p.Progress(p.TextColumn('[cyan]{task.description}'), p.DownloadColumn(), p.BarColumn(), p.TaskProgressColumn(), p.TimeRemainingColumn(), p.TimeElapsedColumn(), p.TransferSpeedColumn(), p.TextColumn('[cyan]{task.fields[name]}'), console=shared.console)
+ with pbar:
+ task = pbar.add_task(description="Download starting", total=starting_pos+total_size, name=model_name)
+ try:
+ with open(temp_file, 'ab') as f:
+ for data in r.iter_content(block_size):
+ if written == 0:
+ try: # check if response is JSON message instead of bytes
+ shared.log.error(f'Model download: response={json.loads(data.decode("utf-8"))}')
+ raise ValueError('response: type=json expected=bytes')
+ except Exception: # this is good
+ pass
+ written = written + len(data)
+ f.write(data)
+ pbar.update(task, description="Download", completed=written)
+ if written < 1024: # min threshold
+ os.remove(temp_file)
+ raise ValueError(f'removed invalid download: bytes={written}')
+ except Exception as e:
+ shared.log.error(f'{res} {e}')
+ finally:
+ pbar.stop_task(task)
+ pbar.remove_task(task)
+ if starting_pos+total_size != written:
+ shared.log.warning(f'{res} written={round(written/1024/1024)}Mb incomplete download')
+ elif os.path.exists(temp_file):
+ shared.log.debug(f'Model download complete: temp="{temp_file}" path="{model_file}"')
+ os.rename(temp_file, model_file)
+ shared.state.end()
+ if os.path.exists(model_file):
+ return model_file
+ else:
+ return None
+
+
+def download_civit_model(model_url: str, model_name: str = '', model_path: str = '', model_type: str = '', token: str = None):
+ import threading
+ if model_url is None or len(model_url) == 0:
+ err = 'Model download: no url provided'
+ shared.log.error(err)
+ return err
+ thread = threading.Thread(target=download_civit_model_thread, args=(model_name, model_url, model_path, model_type, token))
+ thread.start()
+ thread.join()
+ from modules.sd_models import list_models # pylint: disable=W0621
+ list_models()
diff --git a/modules/civitai/metadata_civitai.py b/modules/civitai/metadata_civitai.py
index 8bc2d47b9..3d40296b5 100644
--- a/modules/civitai/metadata_civitai.py
+++ b/modules/civitai/metadata_civitai.py
@@ -1,7 +1,6 @@
import os
import re
import time
-import json
import gradio as gr
from modules.shared import log, opts, req, readfile, max_workers
@@ -58,7 +57,8 @@ def civit_update_metadata():
return html.format(tbody=tbody)
log.debug('CivitAI update metadata: models')
- from modules import ui_extra_networks, modelloader
+ from modules import ui_extra_networks
+ from modules.civitai.download_civitai import download_civit_meta
pages = ui_extra_networks.get_pages('Model')
if len(pages) == 0:
return 'CivitAI update metadata: no models found'
@@ -75,7 +75,7 @@ def civit_update_metadata():
if r.status_code == 200:
d = r.json()
model.id = d['modelId']
- modelloader.download_civit_meta(model.fn, model.id)
+ download_civit_meta(model.fn, model.id)
fn = os.path.splitext(item['filename'])[0] + '.json'
model.meta = readfile(fn, silent=True)
model.name = model.meta.get('name', model.name)
@@ -158,64 +158,8 @@ def civit_search_model(name, tag, model_type):
return res, gr.update(visible=len(data1) > 0, value=data1 if len(data1) > 0 else []), gr.update(visible=False, value=None), gr.update(visible=False, value=None)
-def civit_select1(evt: gr.SelectData, in_data):
- model_id = in_data[evt.index[0]][0]
- data2 = []
- preview_img = None
- for model in data:
- if model['id'] == model_id:
- for d in model['modelVersions']:
- try:
- if d.get('images') is not None and len(d['images']) > 0 and len(d['images'][0]['url']) > 0:
- preview_img = d['images'][0]['url']
- data2.append([d.get('id', None), d.get('modelId', None) or model_id, d.get('name', None), d.get('baseModel', None), d.get('createdAt', None) or d.get('publishedAt', None)])
- except Exception as e:
- log.error(f'CivitAI select: model="{in_data[evt.index[0]]}" {e}')
- log.error(f'CivitAI version data={type(d)}: {d}')
- log.debug(f'CivitAI select: model="{in_data[evt.index[0]]}" versions={len(data2)}')
- return data2, None, preview_img
-
-
-def civit_select2(evt: gr.SelectData, in_data):
- variant_id = in_data[evt.index[0]][0]
- model_id = in_data[evt.index[0]][1]
- data3 = []
- for model in data:
- if model['id'] == model_id:
- for variant in model['modelVersions']:
- if variant['id'] == variant_id:
- for f in variant['files']:
- try:
- if os.path.splitext(f['name'])[1].lower() in ['.safetensors', '.ckpt', '.pt', '.pth', '.bin']:
- data3.append([f['name'], round(f['sizeKB']), json.dumps(f['metadata']), f['downloadUrl']])
- except Exception:
- pass
- log.debug(f'CivitAI select: model="{in_data[evt.index[0]]}" files={len(data3)}')
- return data3
-
-
-def civit_select3(evt: gr.SelectData, in_data):
- log.debug(f'CivitAI select: variant={in_data[evt.index[0]]}')
- return in_data[evt.index[0]][3], in_data[evt.index[0]][0], gr.update(interactive=True)
-
-
-def civit_download_model(model_url: str, model_name: str, model_path: str, model_type: str, token: str = None):
- if model_url is None or len(model_url) == 0:
- return 'No model selected'
- try:
- from modules.modelloader import download_civit_model
- res = download_civit_model(model_url, model_name, model_path, model_type, token=token)
- except Exception as e:
- res = f"CivitAI model downloaded error: model={model_url} {e}"
- log.error(res)
- return res
- from modules.sd_models import list_models # pylint: disable=W0621
- list_models()
- return res
-
-
def atomic_civit_search_metadata(item, results):
- from modules.modelloader import download_civit_preview, download_civit_meta
+ from modules.civitai.download_civitai import download_civit_preview, download_civit_meta
if item is None:
return
try:
@@ -344,9 +288,3 @@ def civit_search_metadata(title: str = None):
t1 = time.time()
log.debug(f'CivitAI search metadata: scanned={scanned} skipped={skipped} time={t1-t0:.2f}')
return create_search_metadata_table(results)
-
-
-def civitai_update_token(token):
- log.debug('CivitAI update token')
- opts.civitai_token = token
- opts.save()
diff --git a/modules/modelloader.py b/modules/modelloader.py
index 9813b5ee5..c4a1d3e00 100644
--- a/modules/modelloader.py
+++ b/modules/modelloader.py
@@ -1,14 +1,11 @@
import io
import os
import time
-import json
import shutil
import importlib
import contextlib
from typing import Dict
from urllib.parse import urlparse
-from PIL import Image
-import rich.progress as p
import huggingface_hub as hf
from installer import install, log
from modules import shared, errors, files_cache
@@ -48,185 +45,6 @@ def hf_login(token=None):
return True
-def save_video_frame(filepath: str):
- from modules import video
- try:
- frames, fps, duration, w, h, codec, frame = video.get_video_params(filepath, capture=True)
- except Exception as e:
- shared.log.error(f'Video: file={filepath} {e}')
- return None
- if frame is not None:
- basename = os.path.splitext(filepath)
- thumb = f'{basename[0]}.thumb.jpg'
- shared.log.debug(f'Video: file={filepath} frames={frames} fps={fps} size={w}x{h} codec={codec} duration={duration} thumb={thumb}')
- frame.save(thumb)
- else:
- shared.log.error(f'Video: file={filepath} no frames found')
- return frame
-
-
-def download_civit_meta(model_path: str, model_id):
- fn = os.path.splitext(model_path)[0] + '.json'
- url = f'https://civitai.com/api/v1/models/{model_id}'
- r = shared.req(url)
- if r.status_code == 200:
- try:
- data = r.json()
- shared.writefile(data, filename=fn, mode='w', silent=True)
- shared.log.info(f'CivitAI download: id={model_id} url={url} file="{fn}"')
- return r.status_code, len(data), '' # code/size/note
- except Exception as e:
- errors.display(e, 'civitai meta')
- shared.log.error(f'CivitAI meta: id={model_id} url={url} file="{fn}" {e}')
- return r.status_code, '', str(e)
- return r.status_code, '', ''
-
-
-def download_civit_preview(model_path: str, preview_url: str):
- global pbar # pylint: disable=global-statement
- if model_path is None:
- pbar = None
- return 500, '', ''
- ext = os.path.splitext(preview_url)[1]
- preview_file = os.path.splitext(model_path)[0] + ext
- is_video = preview_file.lower().endswith('.mp4')
- is_json = preview_file.lower().endswith('.json')
- if is_json:
- shared.log.warning(f'CivitAI download: url="{preview_url}" skip json')
- return 500, '', 'exepected preview image got json'
- if os.path.exists(preview_file):
- return 304, '', 'already exists'
- # res = f'CivitAI download: url={preview_url} file="{preview_file}"'
- r = shared.req(preview_url, stream=True)
- total_size = int(r.headers.get('content-length', 0))
- block_size = 16384 # 16KB blocks
- written = 0
- img = None
- shared.state.begin('CivitAI')
- if pbar is None:
- pbar = p.Progress(p.TextColumn('[cyan]Download'), p.DownloadColumn(), p.BarColumn(), p.TaskProgressColumn(), p.TimeRemainingColumn(), p.TimeElapsedColumn(), p.TransferSpeedColumn(), p.TextColumn('[yellow]{task.description}'), console=shared.console)
- try:
- with open(preview_file, 'wb') as f:
- with pbar:
- task = pbar.add_task(description=preview_file, total=total_size)
- for data in r.iter_content(block_size):
- written = written + len(data)
- f.write(data)
- pbar.update(task, advance=block_size)
- if written < 1024: # min threshold
- os.remove(preview_file)
- return 400, '', 'removed invalid download'
- if is_video:
- img = save_video_frame(preview_file)
- else:
- img = Image.open(preview_file)
- except Exception as e:
- shared.log.error(f'CivitAI download error: url={preview_url} file="{preview_file}" written={written} {e}')
- return 500, '', str(e)
- shared.state.end()
- if img is None:
- return 500, '', 'image is none'
- shared.log.info(f'CivitAI download: url={preview_url} file="{preview_file}" size={total_size} image={img.size}')
- img.close()
- return 200, str(total_size), '' # code/size/note
-
-
-download_pbar = None
-
-def download_civit_model_thread(model_name: str, model_url: str, model_path: str = "", model_type: str = "Model", token: str = None):
- import hashlib
- sha256 = hashlib.sha256()
- sha256.update(model_url.encode('utf-8'))
- temp_file = sha256.hexdigest()[:8] + '.tmp'
-
- headers = {}
- starting_pos = 0
- if os.path.isfile(temp_file):
- starting_pos = os.path.getsize(temp_file)
- headers['Range'] = f'bytes={starting_pos}-'
- if token is None:
- token = shared.opts.civitai_token
- if token is not None and len(token) > 0:
- headers['Authorization'] = f'Bearer {token}'
-
- r = shared.req(model_url, headers=headers, stream=True)
- total_size = int(r.headers.get('content-length', 0))
- if model_name is None or len(model_name) == 0:
- cn = r.headers.get('content-disposition', '')
- model_name = cn.split('filename=')[-1].strip('"')
-
- if model_type == 'LoRA':
- model_file = os.path.join(shared.opts.lora_dir, model_path, model_name)
- temp_file = os.path.join(shared.opts.lora_dir, model_path, temp_file)
- elif model_type == 'Embedding':
- model_file = os.path.join(shared.opts.embeddings_dir, model_path, model_name)
- temp_file = os.path.join(shared.opts.embeddings_dir, model_path, temp_file)
- elif model_type == 'VAE':
- model_file = os.path.join(shared.opts.vae_dir, model_path, model_name)
- temp_file = os.path.join(shared.opts.vae_dir, model_path, temp_file)
- else:
- model_file = os.path.join(shared.opts.ckpt_dir, model_path, model_name)
- temp_file = os.path.join(shared.opts.ckpt_dir, model_path, temp_file)
-
- res = f'Model download: name="{model_name}" url="{model_url}" path="{model_path}" temp="{temp_file}"'
- if os.path.isfile(model_file):
- res += ' already exists'
- shared.log.warning(res)
- return res
-
- res += f' size={round((starting_pos + total_size)/1024/1024, 2)}Mb'
- shared.log.info(res)
- shared.state.begin('CivitAI')
- block_size = 16384 # 16KB blocks
- written = starting_pos
- global download_pbar # pylint: disable=global-statement
- if download_pbar is None:
- download_pbar = p.Progress(p.TextColumn('[cyan]{task.description}'), p.DownloadColumn(), p.BarColumn(), p.TaskProgressColumn(), p.TimeRemainingColumn(), p.TimeElapsedColumn(), p.TransferSpeedColumn(), p.TextColumn('[cyan]{task.fields[name]}'), console=shared.console)
- with download_pbar:
- task = download_pbar.add_task(description="Download starting", total=starting_pos+total_size, name=model_name)
- try:
- with open(temp_file, 'ab') as f:
- for data in r.iter_content(block_size):
- if written == 0:
- try: # check if response is JSON message instead of bytes
- shared.log.error(f'Model download: response={json.loads(data.decode("utf-8"))}')
- raise ValueError('response: type=json expected=bytes')
- except Exception: # this is good
- pass
- written = written + len(data)
- f.write(data)
- download_pbar.update(task, description="Download", completed=written)
- if written < 1024: # min threshold
- os.remove(temp_file)
- raise ValueError(f'removed invalid download: bytes={written}')
- except Exception as e:
- shared.log.error(f'{res} {e}')
- finally:
- download_pbar.stop_task(task)
- download_pbar.remove_task(task)
- if starting_pos+total_size != written:
- shared.log.warning(f'{res} written={round(written/1024/1024)}Mb incomplete download')
- elif os.path.exists(temp_file):
- shared.log.debug(f'Model download complete: temp="{temp_file}" path="{model_file}"')
- os.rename(temp_file, model_file)
- shared.state.end()
- if os.path.exists(model_file):
- return model_file
- else:
- return None
-
-
-def download_civit_model(model_url: str, model_name: str, model_path: str, model_type: str, token: str = None):
- import threading
- if model_name is None or len(model_name) == 0:
- err = 'Model download: no target model name provided'
- shared.log.error(err)
- return err
- thread = threading.Thread(target=download_civit_model_thread, args=(model_name, model_url, model_path, model_type, token))
- thread.start()
- return f'Model download: name={model_name} url={model_url} path={model_path}'
-
-
def download_diffusers_model(hub_id: str, cache_dir: str = None, download_config: Dict[str, str] = None, token = None, variant = None, revision = None, mirror = None, custom_pipeline = None):
if hub_id is None or len(hub_id) == 0:
return None
@@ -430,6 +248,7 @@ def load_civitai(model: str, url: str):
return name # already downloaded
else:
shared.log.debug(f'Reference download start: model="{name}"')
+ from modules.civitai.download_civitai import download_civit_model_thread
download_civit_model_thread(model_name=model, model_url=url, model_path='', model_type='safetensors', token=shared.opts.civitai_token)
shared.log.debug(f'Reference download complete: model="{name}"')
sd_models.list_models()
diff --git a/modules/sd_checkpoint.py b/modules/sd_checkpoint.py
index 1f193f603..62f464946 100644
--- a/modules/sd_checkpoint.py
+++ b/modules/sd_checkpoint.py
@@ -244,7 +244,8 @@ def get_closet_checkpoint_match(s: str) -> CheckpointInfo:
# civitai search
if shared.opts.sd_checkpoint_autodownload and s.startswith("https://civitai.com/api/download/models"):
- fn = modelloader.download_civit_model_thread(model_name=None, model_url=s, model_path='', model_type='Model', token=None)
+ from modules.civitai.download_civitai import download_civit_model_thread
+ fn = download_civit_model_thread(model_name=None, model_url=s, model_path='', model_type='Model', token=None)
if fn is not None:
checkpoint_info = CheckpointInfo(fn)
return checkpoint_info
diff --git a/modules/shared.py b/modules/shared.py
index 6c971f4eb..7df525310 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -514,26 +514,43 @@ options_templates.update(options_section(('image-metadata', "Image Metadata"), {
}))
options_templates.update(options_section(('ui', "User Interface"), {
+ "themes_sep_ui": OptionInfo("
Theme options
", "", gr.HTML),
"theme_type": OptionInfo("Standard", "Theme type", gr.Radio, {"choices": ["Modern", "Standard", "None"]}),
"theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}),
"gradio_theme": OptionInfo("black-teal", "UI theme", gr.Dropdown, lambda: {"choices": theme.list_themes()}, refresh=theme.refresh_themes),
- "ui_locale": OptionInfo("Auto", "UI locale", gr.Dropdown, lambda: {"choices": theme.list_locales()}),
- "subpath": OptionInfo("", "Mount URL subpath"),
+
+ "quicksetting_sep_images": OptionInfo("
Quicksettings
", "", gr.HTML),
+ "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", gr.Dropdown, lambda: {"multiselect":True, "choices": opts.list()}),
+
+ "server_sep_ui": OptionInfo("
Startup & Server Options
", "", gr.HTML),
"autolaunch": OptionInfo(False, "Autolaunch browser upon startup"),
+ "motd": OptionInfo(False, "Show MOTD"),
+ "subpath": OptionInfo("", "Mount URL subpath"),
+ "ui_request_timeout": OptionInfo(30000, "UI request timeout", gr.Slider, {"minimum": 1000, "maximum": 120000, "step": 10}),
+
+ "cards_sep_ui": OptionInfo("
Card options
", "", gr.HTML),
+ "extra_networks_card_size": OptionInfo(140, "UI card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}),
+ "extra_networks_card_cover": OptionInfo("sidebar", "UI position", gr.Radio, {"choices": ["cover", "inline", "sidebar"]}),
+ "extra_networks_card_square": OptionInfo(True, "UI disable variable aspect ratio"),
+
+ "other_sep_ui": OptionInfo("
Other...
", "", gr.HTML),
+ "ui_locale": OptionInfo("Auto", "UI locale", gr.Dropdown, lambda: {"choices": theme.list_locales()}),
"font_size": OptionInfo(14, "Font size", gr.Slider, {"minimum": 8, "maximum": 32, "step": 1}),
"aspect_ratios": OptionInfo("1:1, 4:3, 3:2, 16:9, 16:10, 21:9, 2:3, 3:4, 9:16, 10:16, 9:21", "Allowed aspect ratios"),
- "logmonitor_show": OptionInfo(True, "Show log view"),
- "logmonitor_refresh_period": OptionInfo(5000, "Log view update period", gr.Slider, {"minimum": 0, "maximum": 30000, "step": 25}),
- "ui_request_timeout": OptionInfo(30000, "UI request timeout", gr.Slider, {"minimum": 1000, "maximum": 120000, "step": 10}),
- "motd": OptionInfo(False, "Show MOTD"),
"compact_view": OptionInfo(False, "Compact view"),
"ui_columns": OptionInfo(4, "Gallery view columns", gr.Slider, {"minimum": 1, "maximum": 8, "step": 1}),
+
+ "images_sep_log": OptionInfo("
Log Display
", "", gr.HTML),
+ "logmonitor_show": OptionInfo(True, "Show log view"),
+ "logmonitor_refresh_period": OptionInfo(5000, "Log view update period", gr.Slider, {"minimum": 0, "maximum": 30000, "step": 25}),
+
+ "images_sep_ui": OptionInfo("
Outputs & Images
", "", gr.HTML),
"return_grid": OptionInfo(True, "Show grid in results"),
"return_mask": OptionInfo(False, "Inpainting include greyscale mask in results"),
"return_mask_composite": OptionInfo(False, "Inpainting include masked composite in results"),
"send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface", gr.Checkbox, {"visible": False}),
"send_size": OptionInfo(False, "Send size when sending prompt or image to another interface", gr.Checkbox, {"visible": False}),
- "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", gr.Dropdown, lambda: {"multiselect":True, "choices": opts.list()}),
+
}))
options_templates.update(options_section(('live-preview', "Live Previews"), {
@@ -642,11 +659,8 @@ options_templates.update(options_section(('extra_networks', "Networks"), {
"extra_networks": OptionInfo(["All"], "Available networks", gr.Dropdown, lambda: {"multiselect":True, "choices": ['All'] + [en.title for en in extra_networks]}),
"extra_networks_sort": OptionInfo("Default", "Sort order", gr.Dropdown, {"choices": ['Default', 'Name [A-Z]', 'Name [Z-A]', 'Date [Newest]', 'Date [Oldest]', 'Size [Largest]', 'Size [Smallest]']}),
"extra_networks_view": OptionInfo("gallery", "UI view", gr.Radio, {"choices": ["gallery", "list"]}),
- "extra_networks_card_cover": OptionInfo("sidebar", "UI position", gr.Radio, {"choices": ["cover", "inline", "sidebar"]}),
- "extra_networks_height": OptionInfo(0, "UI height (%)", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}), # set in ui_javascript
"extra_networks_sidebar_width": OptionInfo(35, "UI sidebar width (%)", gr.Slider, {"minimum": 10, "maximum": 80, "step": 1}),
- "extra_networks_card_size": OptionInfo(140, "UI card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}),
- "extra_networks_card_square": OptionInfo(True, "UI disable variable aspect ratio"),
+ "extra_networks_height": OptionInfo(0, "UI height (%)", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}), # set in ui_javascript
"extra_networks_fetch": OptionInfo(True, "UI fetch network info on mouse-over"),
"extra_network_skip_indexing": OptionInfo(False, "Build info on first access", gr.Checkbox),
diff --git a/modules/ui_models.py b/modules/ui_models.py
index d008d80fb..f6f2aa257 100644
--- a/modules/ui_models.py
+++ b/modules/ui_models.py
@@ -475,13 +475,28 @@ def create_ui():
html = create_model_cards(results)
return html
+ def civitai_update_token(token):
+ log.debug('CivitAI update token')
+ opts.civitai_token = token
+ opts.save()
+
+ def civitai_download(model_url, model_name, model_type, model_path, civit_token, model_output):
+ from modules.civitai.download_civitai import download_civit_model
+ msg = f"
Initiating download
"
+ yield msg + model_output
+ download_civit_model(model_url, model_name, model_path, model_type, civit_token)
+ yield model_output
+
with gr.Row():
gr.HTML('
Search & Download
')
with gr.Row(elem_id='civitai_search_row'):
civit_search_text = gr.Textbox(label='', placeholder='keyword', elem_id="civit_search_text")
civit_search_tag = gr.Textbox(label='', placeholder='tag', elem_id="civit_search_text")
civit_search_text_btn = ToolButton(value=ui_symbols.search, interactive=True)
- with gr.Accordion(label='Search options', open=False, elem_id="civitai_search_options"):
+ with gr.Accordion(label='Advanced', open=False, elem_id="civitai_search_options"):
+ civit_download_btn = gr.Button(value="Download model", variant='primary', elem_id="civitai_download_btn", visible=False)
+ with gr.Row():
+ civit_token = gr.Textbox(opts.civitai_token, label='CivitAI token', placeholder='optional access token for private or gated models')
with gr.Row():
civit_nsfw = gr.Checkbox(label='NSFW allowed', value=True)
with gr.Row():
@@ -489,71 +504,17 @@ def create_ui():
with gr.Row():
civit_base = gr.Textbox(label='Base model', placeholder='SDXL, ...')
with gr.Row():
- civit_token = gr.Textbox(opts.civitai_token, label='CivitAI token', placeholder='optional access token for private or gated models')
+ civit_folder = gr.Textbox(label='Download folder', placeholder='optional folder for downloads')
+ with gr.Row():
+ civitai_models_output = gr.HTML('', elem_id="civitai_models_output")
# sort, period, limit
+ _dummy = gr.Label(visible=False) # dummy component to get argspec later
civit_inputs = [civit_search_text, civit_search_tag, civit_nsfw, civit_type, civit_base, civit_token]
- civit_search_text_btn.click(fn=civitai_search, inputs=civit_inputs, outputs=[models_outcome])
- civit_search_text.submit(fn=civitai_search, inputs=civit_inputs, outputs=[models_outcome])
- civit_search_tag.submit(fn=civitai_search, inputs=civit_inputs, outputs=[models_outcome])
-
- """
- from modules.civitai.legacy_civitai import civitai_update_token, civit_search_model, civit_search_metadata, civit_select1, civit_select2, civit_select3, civit_download_model
-
- with gr.Row():
- gr.HTML('
Search for models
')
- with gr.Row():
- with gr.Column(scale=1):
- civit_model_type = gr.Dropdown(label='CivitAI model type', choices=['Model', 'LoRA', 'Embedding', 'VAE', 'Other'], value='Model')
- with gr.Column(scale=15):
- with gr.Row():
- civit_search_text = gr.Textbox('', label='Search models', placeholder='keyword')
- civit_search_tag = gr.Textbox('', label='', placeholder='tags')
- civit_search_btn = ToolButton(value=ui_symbols.search, interactive=True)
- with gr.Row():
- civit_search_res = gr.HTML('')
- with gr.Row():
- gr.HTML('
 CivitAI download model
')
- with gr.Row():
- civit_download_model_btn = gr.Button(value="Download", variant='primary')
- gr.HTML('
Select a model, model version and and model variant from the search results to download or enter model URL manually')
- with gr.Row():
- civit_token = gr.Textbox(opts.civitai_token, label='CivitAI token', placeholder='optional access token for private or gated models')
- civit_token.change(fn=civitai_update_token, inputs=[civit_token], outputs=[])
- with gr.Row():
- civit_name = gr.Textbox('', label='Model name', placeholder='select model from search results', visible=True)
- civit_selected = gr.Textbox('', label='Model URL', placeholder='select model from search results', visible=True)
- civit_path = gr.Textbox('', label='Download path', placeholder='optional subfolder path where to save model', visible=True)
- with gr.Row():
- gr.HTML('
Search results
')
- with gr.Row():
- civit_headers1 = ['ID', 'Name', 'Tags', 'Downloads', 'Rating']
- civit_types1 = ['number', 'str', 'str', 'number', 'number']
- civit_results1 = gr.DataFrame(value=None, label=None, show_label=False, interactive=False, wrap=True, headers=civit_headers1, datatype=civit_types1, type='array', visible=False)
- with gr.Row():
- with gr.Column():
- civit_headers2 = ['ID', 'ModelID', 'Name', 'Base', 'Created', 'Preview']
- civit_types2 = ['number', 'number', 'str', 'str', 'date', 'str']
- civit_results2 = gr.DataFrame(value=None, label='Model versions', show_label=True, interactive=False, wrap=True, headers=civit_headers2, datatype=civit_types2, type='array', visible=False)
- with gr.Column():
- civit_headers3 = ['Name', 'Size', 'Metadata', 'URL']
- civit_types3 = ['str', 'number', 'str', 'str']
- civit_results3 = gr.DataFrame(value=None, label='Model variants', show_label=True, interactive=False, wrap=True, headers=civit_headers3, datatype=civit_types3, type='array', visible=False)
-
- def is_visible(component):
- visible = len(component) > 0 if component is not None else False
- return gr.update(visible=visible)
-
- civit_search_text.submit(fn=civit_search_model, inputs=[civit_search_text, civit_search_tag, civit_model_type], outputs=[civit_search_res, civit_results1, civit_results2, civit_results3])
- civit_search_tag.submit(fn=civit_search_model, inputs=[civit_search_text, civit_search_tag, civit_model_type], outputs=[civit_search_res, civit_results1, civit_results2, civit_results3])
- civit_search_btn.click(fn=civit_search_model, inputs=[civit_search_text, civit_search_tag, civit_model_type], outputs=[civit_search_res, civit_results1, civit_results2, civit_results3])
- civit_results1.select(fn=civit_select1, inputs=[civit_results1], outputs=[civit_results2, civit_results3, models_image])
- civit_results2.select(fn=civit_select2, inputs=[civit_results2], outputs=[civit_results3])
- civit_results3.select(fn=civit_select3, inputs=[civit_results3], outputs=[civit_selected, civit_name, civit_search_btn])
- civit_results1.change(fn=is_visible, inputs=[civit_results1], outputs=[civit_results1])
- civit_results2.change(fn=is_visible, inputs=[civit_results2], outputs=[civit_results2])
- civit_results3.change(fn=is_visible, inputs=[civit_results3], outputs=[civit_results3])
- civit_download_model_btn.click(fn=civit_download_model, inputs=[civit_selected, civit_name, civit_path, civit_model_type, civit_token], outputs=[models_outcome])
- """
+ civit_search_text_btn.click(fn=civitai_search, inputs=civit_inputs, outputs=[civitai_models_output])
+ civit_search_text.submit(fn=civitai_search, inputs=civit_inputs, outputs=[civitai_models_output])
+ civit_search_tag.submit(fn=civitai_search, inputs=civit_inputs, outputs=[civitai_models_output])
+ civit_token.change(fn=civitai_update_token, inputs=[civit_token], outputs=[])
+ civit_download_btn.click(fn=civitai_download, _js="downloadCivitModel", inputs=[_dummy, _dummy, _dummy, civit_folder, civit_token, civitai_models_output], outputs=[civitai_models_output])
with gr.Tab(label="Huggingface", elem_id="models_huggingface_tab"):
from modules.models_hf import hf_search, hf_select, hf_download_model, hf_update_token