diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0c61db1..fc12bf637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Several new models: [Qwen-Image](https://qwenlm.github.io/blog/qwen-image/) (plus *Lightning* variant) and [FLUX.1-Krea-Dev](https://www.krea.ai/blog/flux-krea-open-source-release) Several updated models: [Chroma](https://huggingface.co/lodestones/Chroma), [SkyReels-V2](https://huggingface.co/Skywork/SkyReels-V2-DF-14B-720P-Diffusers), [Wan-VACE](https://huggingface.co/Wan-AI/Wan2.1-VACE-14B-diffusers), [HunyuanDiT](https://huggingface.co/Tencent-Hunyuan/HunyuanDiT-v1.2-Diffusers-Distilled) -Plus continuing with major **UI** work, we have new embedded **Docs/Wiki** search, redesigned real-time **hints**, built-in **GPU monitor**, **CivitAI** integration and more! +Plus continuing with major **UI** work, we have new embedded **Docs/Wiki** search, redesigned real-time **hints**, **wildcards** UI selector, built-in **GPU monitor**, **CivitAI** integration and more! On the compute side, new profiles for high-vram GPUs, offloading improvements, support for new `torch` release and improved quality when using low-bit quantization! And (*as always*) many bugfixes and improvements to existing features! @@ -50,6 +50,8 @@ SD.Next will warn on startup on unused cache entries that can be removed. Also, **Docs** search: fully-local and works in real-time on all document pages **Wiki** search: uses github api to search online wiki pages - updated real-time hints, thanks @CalamitousFelicitousness + - add **Wilcards** UI + in networks display - every heading element is collapsible! - quicksettings reset button to restore all quicksettings to default values because things do sometimes get wrong... diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index c7cfcdeaf..e13323307 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -226,8 +226,8 @@ function sortExtraNetworks(fixed = 'no') { case 0: return 0; case 1: return a.dataset.name ? a.dataset.name.localeCompare(b.dataset.name) : 0; case 2: return b.dataset.name ? b.dataset.name.localeCompare(a.dataset.name) : 0; - case 3: return a.dataset.mtime && !isNaN(a.dataset.mtime) ? parseFloat(b.dataset.mtime) - parseFloat(a.dataset.mtime) : 0; - case 4: return b.dataset.mtime && !isNaN(b.dataset.mtime) ? parseFloat(a.dataset.mtime) - parseFloat(b.dataset.mtime) : 0; + case 3: return a.dataset.mtime ? (new Date(b.dataset.mtime)).getTime() - (new Date(a.dataset.mtime)).getTime() : 0; + case 4: return b.dataset.mtime ? (new Date(a.dataset.mtime)).getTime() - (new Date(b.dataset.mtime)).getTime() : 0; case 5: return a.dataset.size && !isNaN(a.dataset.size) ? parseFloat(b.dataset.size) - parseFloat(a.dataset.size) : 0; case 6: return b.dataset.size && !isNaN(b.dataset.size) ? parseFloat(a.dataset.size) - parseFloat(b.dataset.size) : 0; } diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 47859a003..ce701602a 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -503,6 +503,8 @@ def register_pages(): register_page(ExtraNetworksPageStyles()) from modules.ui_extra_networks_lora import ExtraNetworksPageLora register_page(ExtraNetworksPageLora()) + from modules.ui_extra_networks_wildcards import ExtraNetworksPageWildcards + register_page(ExtraNetworksPageWildcards()) if shared.opts.latent_history > 0: from modules.ui_extra_networks_history import ExtraNetworksPageHistory register_page(ExtraNetworksPageHistory()) @@ -515,7 +517,7 @@ def get_pages(title=None): visible = shared.opts.extra_networks pages = [] if 'All' in visible or visible == []: # default en sort order - visible = ['Model', 'Lora', 'Style', 'Embedding', 'VAE', 'History', 'Hypernetwork'] + visible = ['Model', 'Lora', 'Style', 'Wildcards', 'Embedding', 'VAE', 'History', 'Hypernetwork'] titles = [page.title for page in shared.extra_networks] if title is None: diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 15efb0e5c..ff050bd1e 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -2,7 +2,7 @@ import os import html import json import concurrent -from modules import shared, ui_extra_networks, sd_models +from modules import shared, ui_extra_networks, sd_models, modelstats reference_dir = os.path.join('models', 'Reference') @@ -46,7 +46,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): record = None try: checkpoint: sd_models.CheckpointInfo = sd_models.checkpoints_list.get(name) - exists = os.path.exists(checkpoint.filename) + size, mtime = modelstats.stat(checkpoint.filename) record = { "type": 'Model', "name": checkpoint.name, @@ -55,8 +55,8 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): "hash": checkpoint.shorthash, "metadata": checkpoint.metadata, "onclick": '"' + html.escape(f"selectCheckpoint({json.dumps(name)})") + '"', - "mtime": os.path.getmtime(checkpoint.filename) if exists else 0, - "size": os.path.getsize(checkpoint.filename) if exists else 0, + "mtime": mtime, + "size": size, } record["info"] = self.find_info(checkpoint.filename) record["description"] = self.find_description(checkpoint.filename, record["info"]) diff --git a/modules/ui_extra_networks_lora.py b/modules/ui_extra_networks_lora.py index 597f32941..8ab95ce8d 100644 --- a/modules/ui_extra_networks_lora.py +++ b/modules/ui_extra_networks_lora.py @@ -1,7 +1,7 @@ import os import json import concurrent -from modules import shared, ui_extra_networks +from modules import shared, ui_extra_networks, modelstats from modules.lora import lora_load @@ -85,6 +85,8 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): try: # path, _ext = os.path.splitext(l.filename) name = os.path.splitext(os.path.relpath(l.filename, shared.cmd_opts.lora_dir))[0] + size, mtime = modelstats.stat(l.filename) + info = self.find_info(l.filename) item = { "type": 'Lora', "name": name, @@ -93,14 +95,13 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): "hash": l.shorthash, "prompt": json.dumps(f" "), "metadata": json.dumps(l.metadata, indent=4) if l.metadata else None, - "mtime": os.path.getmtime(l.filename), - "size": os.path.getsize(l.filename), + "mtime": mtime, + "size": size, "version": l.sd_version, + "info": info, + "description": self.find_description(l.filename, info), + "tags": self.get_tags(l, info), } - info = self.find_info(l.filename) - item["info"] = info - item["description"] = self.find_description(l.filename, info) # use existing info instead of double-read - item["tags"] = self.get_tags(l, info) return item except Exception as e: shared.log.error(f'Networks: type=lora file="{name}" {e}') diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index 92c8acc92..6857447f2 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -1,6 +1,6 @@ import json import os -from modules import shared, sd_models, ui_extra_networks, files_cache +from modules import shared, sd_models, ui_extra_networks, files_cache, modelstats from modules.textual_inversion import Embedding @@ -23,6 +23,8 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): if embedding.tag is not None: tags[embedding.tag]=1 name = os.path.splitext(embedding.basename)[0] + size, mtime = modelstats.stat(embedding.filename) + info = self.find_info(embedding.filename) record = { "type": 'Embedding', "name": name, @@ -30,11 +32,11 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): "alias": os.path.splitext(os.path.basename(embedding.filename))[0], "prompt": json.dumps(f" {os.path.splitext(embedding.name)[0]}"), "tags": tags, - "mtime": os.path.getmtime(embedding.filename), - "size": os.path.getsize(embedding.filename), + "mtime": mtime, + "size": size, + "info": info, + "description": self.find_description(embedding.filename, info), } - record["info"] = self.find_info(embedding.filename) - record["description"] = self.find_description(embedding.filename, record["info"]) except Exception as e: shared.log.debug(f'Networks error: type=embedding file="{embedding.filename}" {e}') return record diff --git a/modules/ui_extra_networks_vae.py b/modules/ui_extra_networks_vae.py index ed9ddadc3..0db733a3c 100644 --- a/modules/ui_extra_networks_vae.py +++ b/modules/ui_extra_networks_vae.py @@ -1,7 +1,7 @@ import html import json import os -from modules import shared, ui_extra_networks, sd_vae, hashes +from modules import shared, ui_extra_networks, sd_vae, hashes, modelstats class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage): @@ -14,6 +14,8 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage): def list_items(self): for name, filename in sd_vae.vae_dict.items(): try: + size, mtime = modelstats.stat(filename) + info = self.find_info(filename) record = { "type": 'VAE', "name": name, @@ -25,11 +27,11 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage): "local_preview": f"{os.path.splitext(filename)[0]}.{shared.opts.samples_format}", "metadata": {}, "onclick": '"' + html.escape(f"""return selectVAE({json.dumps(name)})""") + '"', - "mtime": os.path.getmtime(filename), - "size": os.path.getsize(filename), + "mtime": mtime, + "size": size, + "info": info, + "description": self.find_description(filename, info), } - record["info"] = self.find_info(filename) - record["description"] = self.find_description(filename, record["info"]) yield record except Exception as e: shared.log.debug(f'Networks error: type=vae file="{filename}" {e}') diff --git a/modules/ui_extra_networks_wildcards.py b/modules/ui_extra_networks_wildcards.py new file mode 100644 index 000000000..e60af679e --- /dev/null +++ b/modules/ui_extra_networks_wildcards.py @@ -0,0 +1,50 @@ +import os +import json +from modules import shared, ui_extra_networks, modelstats, files_cache + + +wildcards_list = [] + + +class ExtraNetworksPageWildcards(ui_extra_networks.ExtraNetworksPage): + def __init__(self): + super().__init__('Wildcards') + + def parents(self, file): + folder = os.path.dirname(file) + if folder != shared.opts.wildcards_dir and folder not in wildcards_list: + wildcards_list.append(folder) + self.parents(folder) + + def refresh(self): + wildcards_list.clear() + files = files_cache.list_files(shared.opts.wildcards_dir, ext_filter=[".txt"], recursive=True) + for file in files: + wildcards_list.append(file) + self.parents(file) + + def list_items(self): + self.refresh() + for filename in wildcards_list: + relname = os.path.relpath(filename, shared.opts.wildcards_dir) + name = os.path.splitext(relname)[0] + size, mtime = modelstats.stat(filename) + try: + record = { + "type": 'Wildcard', + "name": name, + "filename": filename, + "preview": self.find_preview(filename), + "local_preview": f"{os.path.splitext(filename)[0]}.{shared.opts.samples_format}", + "prompt": json.dumps(f" __{name}__"), + "mtime": mtime, + "size": size, + "description": '', + "info": {}, + } + yield record + except Exception as e: + shared.log.debug(f'Networks error: type=wildcard file="{filename}" {e}') + + def allowed_directories_for_previews(self): + return [v for v in [shared.opts.wildcards_dir] if v is not None]