diff --git a/CHANGELOG.md b/CHANGELOG.md
index f69d8c7a3..e8bcd38c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,15 +4,18 @@
Mostly a service release
- tons of fixes
-- update ui hints
+- update **ui hints**
+- updated **models -> civitai**
+ - search and download loras
+ - find previews for already downloaded models or loras
- new option **inference mode**
- default is standard `torch.no_grad`
new option is `torch.inference_only` which is slightly faster and uses less vram, but only works on some gpus
- new cmdline param `--no-metadata`
skips reading metadata from models that are not already cached
-- updated gradio
-- styles support for subfolders
-- clean-up logging
+- updated **gradio**
+- **styles** support for subfolders
+- clean-up **logging**
- capture system info in startup log
- better diagnostic output
- capture extension output
diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py
index 4705830b8..fbac7e8fc 100644
--- a/extensions-builtin/Lora/lora.py
+++ b/extensions-builtin/Lora/lora.py
@@ -95,7 +95,7 @@ class LoraOnDisk:
def set_hash(self, v):
self.hash = v
- self.shorthash = self.hash[0:12]
+ self.shorthash = self.hash[0:10]
if self.shorthash:
available_lora_hash_lookup[self.shorthash] = self
diff --git a/modules/hashes.py b/modules/hashes.py
index ea8b91609..84071bfb5 100644
--- a/modules/hashes.py
+++ b/modules/hashes.py
@@ -21,12 +21,17 @@ def cache(subsection):
return s
-def calculate_sha256(filename):
+def calculate_sha256(filename, quiet=False):
hash_sha256 = hashlib.sha256()
blksize = 1024 * 1024
- with progress.open(filename, 'rb', description=f'Calculating model hash: [cyan]{filename}', auto_refresh=True) as f:
- for chunk in iter(lambda: f.read(blksize), b""):
- hash_sha256.update(chunk)
+ if not quiet:
+ with progress.open(filename, 'rb', description=f'Calculating model hash: [cyan]{filename}', auto_refresh=True) as f:
+ for chunk in iter(lambda: f.read(blksize), b""):
+ hash_sha256.update(chunk)
+ else:
+ with open(filename, 'rb') as f:
+ for chunk in iter(lambda: f.read(blksize), b""):
+ hash_sha256.update(chunk)
return hash_sha256.hexdigest()
diff --git a/modules/modelloader.py b/modules/modelloader.py
index 051aa7372..40136479d 100644
--- a/modules/modelloader.py
+++ b/modules/modelloader.py
@@ -4,6 +4,7 @@ import shutil
import importlib
from typing import Dict
from urllib.parse import urlparse
+import PIL.Image as Image
from modules import shared
from modules.upscaler import Upscaler, UpscalerLanczos, UpscalerNearest, UpscalerNone
from modules.paths import script_path, models_path
@@ -59,12 +60,13 @@ def download_civit_preview(model_path: str, preview_url: str):
import rich.progress as p
_, ext = os.path.splitext(preview_url)
model_name, _ = os.path.splitext(os.path.basename(model_path))
- preview_file = os.path.splitext(model_path)[0] + ext
+ preview_file = f'{os.path.splitext(model_path)[0]}{ext}' if '.safetensors' in model_path.lower() else f'{model_path}{ext}'
res = f'CivitAI download: name={model_name} url={preview_url}'
req = requests.get(preview_url, stream=True, timeout=30)
total_size = int(req.headers.get('content-length', 0))
block_size = 16384 # 16KB blocks
written = 0
+ img = None
shared.state.begin('civitai-download-preview')
try:
with open(preview_file, 'wb') as f:
@@ -77,13 +79,14 @@ def download_civit_preview(model_path: str, preview_url: str):
if written < 1024: # min threshold
os.remove(preview_file)
raise ValueError(f'removed invalid download: bytes={written}')
+ img = Image.open(preview_file)
except Exception as e:
shared.log.error(f'CivitAI download error: name={model_name} url={preview_url} {e}')
- if total_size == written:
- shared.log.info(f'{res} size={total_size}')
- else:
- shared.log.error(f'{res} size={total_size} written={written}')
shared.state.end()
+ if img is None:
+ return res
+ shared.log.info(f'{res} size={total_size} image={img.size}')
+ img.close()
return res
@@ -114,7 +117,7 @@ def download_civit_model(model_url: str, model_name: str, model_path: str, model
written = written + len(data)
f.write(data)
progress.update(task, advance=block_size, description="Downloading")
- if written < 1024 * 1024 * 1024: # min threshold
+ if written < 1024 * 1024: # min threshold
os.remove(model_file)
raise ValueError(f'removed invalid download: bytes={written}')
if preview is not None:
@@ -160,17 +163,16 @@ def download_diffusers_model(hub_id: str, cache_dir: str = None, download_config
except Exception as e:
shared.log.error(f"Diffusers download error: {hub_id} {e}")
try:
- model_info_dict = hf.model_info(hub_id).cardData # pylint: disable=no-member # TODO Diffusers is this real error?
+ model_info_dict = hf.model_info(hub_id).cardData if pipeline_dir is not None else None # pylint: disable=no-member # TODO Diffusers is this real error?
except Exception:
model_info_dict = None
- # some checkpoints need to be downloaded as "hidden" as they just serve as pre- or post-pipelines of other pipelines
- if model_info_dict is not None and "prior" in model_info_dict:
+ if model_info_dict is not None and "prior" in model_info_dict: # some checkpoints need to be downloaded as "hidden" as they just serve as pre- or post-pipelines of other pipelines
download_dir = DiffusionPipeline.download(model_info_dict["prior"][0], **download_config)
model_info_dict["prior"] = download_dir
- # mark prior as hidden
- with open(os.path.join(download_dir, "hidden"), "w", encoding="utf-8") as f:
+ with open(os.path.join(download_dir, "hidden"), "w", encoding="utf-8") as f: # mark prior as hidden
f.write("True")
- shared.writefile(model_info_dict, os.path.join(pipeline_dir, "model_info.json"))
+ if pipeline_dir is not None:
+ shared.writefile(model_info_dict, os.path.join(pipeline_dir, "model_info.json"))
shared.state.end()
return pipeline_dir
@@ -323,7 +325,7 @@ def extension_filter(ext_filter=None, ext_blacklist=None):
return (not ext_filter or any(fp.upper().endswith(ew) for ew in ext_filter)) and (not ext_blacklist or not any(fp.upper().endswith(ew) for ew in ext_blacklist))
return filter
-def load_file_from_url(url: str, *, model_dir: str, progress: bool = True, file_name: str | None = None) -> str:
+def load_file_from_url(url: str, *, model_dir: str, progress: bool = True, file_name = None):
"""Download a file from url into model_dir, using the file present if possible. Returns the path to the downloaded file."""
os.makedirs(model_dir, exist_ok=True)
if not file_name:
diff --git a/modules/ui_models.py b/modules/ui_models.py
index 7998e13ac..bb90ef822 100644
--- a/modules/ui_models.py
+++ b/modules/ui_models.py
@@ -8,6 +8,7 @@ from modules.ui_common import create_refresh_button
from modules.call_queue import wrap_gradio_gpu_call
from modules.shared import opts, log
import modules.errors
+import modules.hashes
def create_ui():
@@ -201,15 +202,11 @@ def create_ui():
def hf_download_model(hub_id: str, token, variant, revision, mirror):
from modules.modelloader import download_diffusers_model
- try:
- download_diffusers_model(hub_id, cache_dir=opts.diffusers_dir, token=token, variant=variant, revision=revision, mirror=mirror)
- except Exception as e:
- log.error(f"Diffuser model downloaded error: model={hub_id} {e}")
- return f"Diffuser model downloaded error: model={hub_id} {e}"
+ download_diffusers_model(hub_id, cache_dir=opts.diffusers_dir, token=token, variant=variant, revision=revision, mirror=mirror)
from modules.sd_models import list_models # pylint: disable=W0621
list_models()
- log.info(f"Diffuser model downloaded: model={hub_id}")
- return f'Diffuser model downloaded: model={hub_id}'
+ log.info(f'Diffuser model downloaded: model="{hub_id}"')
+ return f'Diffuser model downloaded: model="{hub_id}"'
with gr.Column(scale=6):
with gr.Row():
@@ -252,7 +249,7 @@ def create_ui():
if tag is not None and len(tag) > 0:
url += f'&tag={tag}'
r = requests.get(url, timeout=60, headers=headers)
- log.debug(f'CivitAI search: name={name} tag={tag} status={r.status_code}')
+ log.debug(f'CivitAI search: name="{name}" tag={tag or "none"} status={r.status_code}')
if r.status_code != 200:
return [], [], []
body = r.json()
@@ -261,6 +258,8 @@ def create_ui():
data1 = []
for model in data:
found = 0
+ if model_type == 'LoRA' and model['type'] == 'LORA':
+ found += 1
for variant in model['modelVersions']:
if model_type == 'SD 1.5':
if 'SD 1.' in variant['baseModel']:
@@ -297,7 +296,7 @@ def create_ui():
d['baseModel'],
d['createdAt'],
])
- log.debug(f'CivitAI select: model={in_data[evt.index[0]]} versions={len(data2)}')
+ log.debug(f'CivitAI select: model="{in_data[evt.index[0]]}" versions={len(data2)}')
return data2, preview_img
def civit_select2(evt: gr.SelectData, in_data):
@@ -315,7 +314,7 @@ def create_ui():
json.dumps(f['metadata']),
f['downloadUrl'],
])
- log.debug(f'CivitAI select: model={in_data[evt.index[0]]} files={len(data3)}')
+ log.debug(f'CivitAI select: model="{in_data[evt.index[0]]}" files={len(data3)}')
return data3
def civit_select3(evt: gr.SelectData, in_data):
@@ -336,7 +335,7 @@ def create_ui():
list_models()
return res
- def civit_download_previews():
+ def civit_download_previews(civit_previews_rehash):
import requests
from modules.ui_extra_networks import extra_pages
from modules.modelloader import download_civit_preview
@@ -347,17 +346,26 @@ def create_ui():
if item.get('fullname', None) is None:
continue
if 'card-no-preview.png' in item['preview'] and os.path.isfile(item['fullname']):
+ sha = item.get('hash', None)
if item.get('hash', None) is None:
- log.debug(f'CivitAI skipping item without hash: name={item["name"]}')
+ log.debug(f'CivitAI skipping item without hash: name="{item["name"]}"')
continue
- url = f'https://civitai.com/api/v1/model-versions/by-hash/{item["hash"]}'
- r = requests.get(url, timeout=5, headers=headers)
- log.debug(f'CivitAI search: name={item["name"]} hash={item["hash"]} status={r.status_code}')
+ r = requests.get(f'https://civitai.com/api/v1/model-versions/by-hash/{sha}', timeout=5, headers=headers)
+ log.debug(f'CivitAI search: name="{item["name"]}" hash={sha} status={r.status_code}')
if r.status_code == 200:
d = r.json()
if d.get('images') is not None and len(d['images']) > 0 and len(d['images'][0]['url']) > 0:
preview_url = d['images'][0]['url']
res += download_civit_preview(item['filename'], preview_url) + '
'
+ elif civit_previews_rehash and os.stat(item['fullname']).st_size < (1024 * 1024 * 1024):
+ sha = modules.hashes.calculate_sha256(item['fullname'], quiet=True)
+ r = requests.get(f'https://civitai.com/api/v1/model-versions/by-hash/{sha}', timeout=5, headers=headers)
+ log.debug(f'CivitAI search: name="{item["name"]}" hash={sha} status={r.status_code}')
+ if r.status_code == 200:
+ d = r.json()
+ if d.get('images') is not None and len(d['images']) > 0 and len(d['images'][0]['url']) > 0:
+ preview_url = d['images'][0]['url']
+ res += download_civit_preview(item['filename'], preview_url) + '
'
return res
with gr.Row():
@@ -389,6 +397,7 @@ def create_ui():
civit_results1 = gr.DataFrame(value = None, label = 'Search results', show_label = True, interactive = False, wrap = True, overflow_row_behaviour = 'paginate', max_rows = 10, headers = civit_headers1, datatype = civit_types1, type='array')
with gr.Row():
civit_previews_btn = gr.Button(value="Fetch previews for existing models", variant='primary')
+ civit_previews_rehash = gr.Checkbox(value=False, label="Check alternative hash")
civit_search_text.submit(fn=civit_search, inputs=[civit_search_text, civit_search_tag, civit_model_type], outputs=[civit_results1, civit_results2, civit_results3])
civit_search_tag.submit(fn=civit_search, inputs=[civit_search_text, civit_search_tag, civit_model_type], outputs=[civit_results1, civit_results2, civit_results3])
@@ -397,4 +406,4 @@ def create_ui():
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_download_model_btn.click(fn=civit_download_model, inputs=[civit_selected, civit_name, civit_path, civit_model_type, models_image], outputs=[models_outcome])
- civit_previews_btn.click(fn=civit_download_previews, inputs=[], outputs=[models_outcome])
+ civit_previews_btn.click(fn=civit_download_previews, inputs=[civit_previews_rehash], outputs=[models_outcome])