From aaa367e35ce4e823219c2954ca141ca1ed14800e Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 14:18:18 +0300 Subject: new setting: Extra text to add before <...> when adding extra network to prompt --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javascript/extraNetworks.js') diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 17bf2000..5781df4f 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -78,7 +78,7 @@ function cardClicked(tabname, textToAdd, allowNegativePrompt){ var textarea = allowNegativePrompt ? activePromptTextarea[tabname] : gradioApp().querySelector("#" + tabname + "_prompt > label > textarea") if(! tryToRemoveExtraNetworkFromPrompt(textarea, textToAdd)){ - textarea.value = textarea.value + " " + textToAdd + textarea.value = textarea.value + opts.extra_networks_add_text_separator + textToAdd } updateInput(textarea) -- cgit v1.2.3 From 247a34498b337798a371d69483bbcab49b5c320c Mon Sep 17 00:00:00 2001 From: Kilvoctu Date: Sat, 11 Mar 2023 13:11:26 -0600 Subject: restore text, remove 'close' don't use emojis for extra network buttons; remove 'close' --- javascript/extraNetworks.js | 2 -- modules/ui_extra_networks.py | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) (limited to 'javascript/extraNetworks.js') diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 17bf2000..8f7cee03 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -5,12 +5,10 @@ function setupExtraNetworksForTab(tabname){ var tabs = gradioApp().querySelector('#'+tabname+'_extra_tabs > div') var search = gradioApp().querySelector('#'+tabname+'_extra_search textarea') var refresh = gradioApp().getElementById(tabname+'_extra_refresh') - var close = gradioApp().getElementById(tabname+'_extra_close') search.classList.add('search') tabs.appendChild(search) tabs.appendChild(refresh) - tabs.appendChild(close) search.addEventListener("input", function(evt){ searchTerm = search.value.toLowerCase() diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 8786fde6..3f56bf63 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -13,10 +13,6 @@ from modules.generation_parameters_copypaste import image_from_url_text extra_pages = [] allowed_dirs = set() -# Using constants for these since the variation selector isn't visible. -# Important that they exactly match script.js for tooltip to work. -refresh_symbol = '\U0001f504' # 🔄 -close_symbol = '\U0000274C' # ❌ def register_page(page): """registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions""" @@ -186,8 +182,7 @@ def create_ui(container, button, tabname): ui.pages.append(page_elem) filter = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False) - button_refresh = gr.Button(refresh_symbol, elem_id=tabname+"_extra_refresh") - button_close = gr.Button(close_symbol, elem_id=tabname+"_extra_close") + button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh") ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False) ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) @@ -198,7 +193,6 @@ def create_ui(container, button, tabname): state_visible = gr.State(value=False) button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container]) - button_close.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container]) def refresh(): res = [] -- cgit v1.2.3 From c19530f1a590d758463f84523dd4c48c34d723e6 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Tue, 14 Mar 2023 09:10:26 +0300 Subject: Add view metadata button for Lora cards. --- extensions-builtin/Lora/lora.py | 21 +++++++- extensions-builtin/Lora/ui_extra_networks_lora.py | 1 + html/extra-networks-card.html | 2 + javascript/extraNetworks.js | 38 +++++++++++++- modules/sd_models.py | 24 +++++++++ modules/ui_extra_networks.py | 7 +++ style.css | 61 +++++++++++++++++++++++ 7 files changed, 152 insertions(+), 2 deletions(-) (limited to 'javascript/extraNetworks.js') diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index cb8f1d36..8937b585 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -3,7 +3,9 @@ import os import re import torch -from modules import shared, devices, sd_models +from modules import shared, devices, sd_models, errors + +metadata_tags_order = {"ss_sd_model_name": 1, "ss_resolution": 2, "ss_clip_skip": 3, "ss_num_train_images": 10, "ss_tag_frequency": 20} re_digits = re.compile(r"\d+") re_unet_down_blocks = re.compile(r"lora_unet_down_blocks_(\d+)_attentions_(\d+)_(.+)") @@ -43,6 +45,23 @@ class LoraOnDisk: def __init__(self, name, filename): self.name = name self.filename = filename + self.metadata = {} + + _, ext = os.path.splitext(filename) + if ext.lower() == ".safetensors": + try: + self.metadata = sd_models.read_metadata_from_safetensors(filename) + except Exception as e: + errors.display(e, f"reading lora {filename}") + + if self.metadata: + m = {} + for k, v in sorted(self.metadata.items(), key=lambda x: metadata_tags_order.get(x[0], 999)): + m[k] = v + + self.metadata = m + + self.ssmd_cover_images = self.metadata.pop('ssmd_cover_images', None) # those are cover images and they are too big to display in UI as text class LoraModule: diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 8d32052e..68b11332 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -23,6 +23,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): "search_term": self.search_terms_from_path(lora_on_disk.filename), "prompt": json.dumps(f""), "local_preview": f"{path}.{shared.opts.samples_format}", + "metadata": json.dumps(lora_on_disk.metadata, indent=4) if lora_on_disk.metadata else None, } def allowed_directories_for_previews(self): diff --git a/html/extra-networks-card.html b/html/extra-networks-card.html index 8612396d..1bf3fc30 100644 --- a/html/extra-networks-card.html +++ b/html/extra-networks-card.html @@ -1,4 +1,6 @@
+ {metadata_button} +
    diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index d0177ad6..2fb87cd5 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -102,4 +102,40 @@ function extraNetworksSearchButton(tabs_id, event){ searchTextarea.value = text updateInput(searchTextarea) -} \ No newline at end of file +} + +var globalPopup = null; +var globalPopupInner = null; +function popup(contents){ + if(! globalPopup){ + globalPopup = document.createElement('div') + globalPopup.onclick = function(){ globalPopup.style.display = "none"; }; + globalPopup.classList.add('global-popup'); + + var close = document.createElement('div') + close.classList.add('global-popup-close'); + close.onclick = function(){ globalPopup.style.display = "none"; }; + close.title = "Close"; + globalPopup.appendChild(close) + + globalPopupInner = document.createElement('div') + globalPopupInner.onclick = function(event){ event.stopPropagation(); return false; }; + globalPopupInner.classList.add('global-popup-inner'); + globalPopup.appendChild(globalPopupInner) + + gradioApp().appendChild(globalPopup); + } + + globalPopupInner.innerHTML = ''; + globalPopupInner.appendChild(contents); + + globalPopup.style.display = "flex"; +} + +function extraNetworksShowMetadata(text){ + elem = document.createElement('pre') + elem.classList.add('popup-metadata'); + elem.textContent = text; + + popup(elem); +} diff --git a/modules/sd_models.py b/modules/sd_models.py index 93959f55..5f57ec0c 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -210,6 +210,30 @@ def get_state_dict_from_checkpoint(pl_sd): return pl_sd +def read_metadata_from_safetensors(filename): + import json + + with open(filename, mode="rb") as file: + metadata_len = file.read(8) + metadata_len = int.from_bytes(metadata_len, "little") + json_start = file.read(2) + + assert metadata_len > 2 and json_start in (b'{"', b"{'"), f"{filename} is not a safetensors file" + json_data = json_start + file.read(metadata_len-2) + json_obj = json.loads(json_data) + + res = {} + for k, v in json_obj.get("__metadata__", {}).items(): + res[k] = v + if isinstance(v, str) and v[0] == '{': + try: + res[k] = json.loads(v) + except Exception as e: + pass + + return res + + def read_state_dict(checkpoint_file, print_global_state=False, map_location=None): _, extension = os.path.splitext(checkpoint_file) if extension.lower() == ".safetensors": diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 01df5e90..b5847299 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -124,6 +124,12 @@ class ExtraNetworksPage: if onclick is None: onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"' + metadata_button = "" + metadata = item.get("metadata") + if metadata: + metadata_onclick = '"' + html.escape(f"""extraNetworksShowMetadata({json.dumps(metadata)}); return false;""") + '"' + metadata_button = f"" + args = { "preview_html": "style='background-image: url(\"" + html.escape(preview) + "\")'" if preview else '', "prompt": item.get("prompt", None), @@ -134,6 +140,7 @@ class ExtraNetworksPage: "card_clicked": onclick, "save_card_preview": '"' + html.escape(f"""return saveCardPreview(event, {json.dumps(tabname)}, {json.dumps(item["local_preview"])})""") + '"', "search_term": item.get("search_term", ""), + "metadata_button": metadata_button, } return self.card_page.format(**args) diff --git a/style.css b/style.css index 2f26ad02..3eac2b17 100644 --- a/style.css +++ b/style.css @@ -362,6 +362,46 @@ input[type="range"]{ height: 100%; } +.popup-metadata{ + color: black; + background: white; + display: inline-block; + padding: 1em; + white-space: pre-wrap; +} + +.global-popup{ + display: flex; + position: fixed; + z-index: 1001; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(20, 20, 20, 0.95); +} + + +.global-popup-close:before { + content: "×"; +} + +.global-popup-close{ + position: fixed; + right: 0.25em; + top: 0; + cursor: pointer; + color: white; + font-size: 32pt; +} + +.global-popup-inner{ + display: inline-block; + margin: auto; + padding: 2em; +} + #lightboxModal{ display: none; position: fixed; @@ -837,6 +877,27 @@ footer { margin-left: 0.5em; } + +.extra-network-cards .card .metadata-button:before, .extra-network-thumbs .card .metadata-button:before{ + content: "🛈"; +} +.extra-network-cards .card .metadata-button, .extra-network-thumbs .card .metadata-button{ + display: none; + position: absolute; + right: 0; + color: white; + text-shadow: 2px 2px 3px black; + padding: 0.25em; + font-size: 22pt; +} +.extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ + display: inline-block; +} +.extra-network-cards .card .metadata-button:hover, .extra-network-thumbs .card .metadata-button:hover{ + color: red; +} + + .extra-network-thumbs { display: flex; flex-flow: row wrap; -- cgit v1.2.3 From 9ed04e759d8b4a84db1f0e37abee59178fe1f586 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 25 Mar 2023 10:11:04 +0300 Subject: use HTTP request to fetch metadata for Lora cards instead of including it into the main page --- javascript/extraNetworks.js | 36 +++++++++++++++++++++++++++++++++ modules/ui_extra_networks.py | 47 +++++++++++++++++++++++++++++++++----------- style.css | 1 + 3 files changed, 72 insertions(+), 12 deletions(-) (limited to 'javascript/extraNetworks.js') diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 2fb87cd5..40818bb4 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -139,3 +139,39 @@ function extraNetworksShowMetadata(text){ popup(elem); } + +function requestGet(url, data, handler, errorHandler){ + var xhr = new XMLHttpRequest(); + var args = Object.keys(data).map(function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }).join('&') + xhr.open("GET", url + "?" + args, true); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + try { + var js = JSON.parse(xhr.responseText); + handler(js) + } catch (error) { + console.error(error); + errorHandler() + } + } else{ + errorHandler() + } + } + }; + var js = JSON.stringify(data); + xhr.send(js); +} + +function extraNetworksRequestMetadata(extraPage, cardName){ + showError = function(){ extraNetworksShowMetadata("there was an error getting metadata"); } + + requestGet("./sd_extra_networks/metadata", {"page": extraPage, "item": cardName}, function(data){ + if(data && data.metadata){ + extraNetworksShowMetadata(data.metadata) + } else{ + showError() + } + }, showError) +} diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 8cd6d8cf..3cf8fcb6 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -22,21 +22,37 @@ def register_page(page): allowed_dirs.update(set(sum([x.allowed_directories_for_previews() for x in extra_pages], []))) -def add_pages_to_demo(app): - def fetch_file(filename: str = ""): - from starlette.responses import FileResponse +def fetch_file(filename: str = ""): + from starlette.responses import FileResponse + + if not any([Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs]): + raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.") + + ext = os.path.splitext(filename)[1].lower() + if ext not in (".png", ".jpg", ".webp"): + raise ValueError(f"File cannot be fetched: {filename}. Only png and jpg and webp.") + + # would profit from returning 304 + return FileResponse(filename, headers={"Accept-Ranges": "bytes"}) + + +def get_metadata(page: str = "", item: str = ""): + from starlette.responses import JSONResponse - if not any([Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs]): - raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.") + page = next(iter([x for x in extra_pages if x.name == page]), None) + if page is None: + return JSONResponse({}) - ext = os.path.splitext(filename)[1].lower() - if ext not in (".png", ".jpg", ".webp"): - raise ValueError(f"File cannot be fetched: {filename}. Only png and jpg and webp.") + metadata = page.metadata.get(item) + if metadata is None: + return JSONResponse({}) - # would profit from returning 304 - return FileResponse(filename, headers={"Accept-Ranges": "bytes"}) + return JSONResponse({"metadata": metadata}) + +def add_pages_to_demo(app): app.add_api_route("/sd_extra_networks/thumb", fetch_file, methods=["GET"]) + app.add_api_route("/sd_extra_networks/metadata", get_metadata, methods=["GET"]) class ExtraNetworksPage: @@ -45,6 +61,7 @@ class ExtraNetworksPage: self.name = title.lower() self.card_page = shared.html("extra-networks-card.html") self.allow_negative_prompt = False + self.metadata = {} def refresh(self): pass @@ -66,6 +83,8 @@ class ExtraNetworksPage: view = shared.opts.extra_networks_default_view items_html = '' + self.metadata = {} + subdirs = {} for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]: for x in glob.glob(os.path.join(parentdir, '**/*'), recursive=True): @@ -92,6 +111,10 @@ class ExtraNetworksPage: """ for subdir in subdirs]) for item in self.list_items(): + metadata = item.get("metadata") + if metadata: + self.metadata[item["name"]] = metadata + items_html += self.create_html_for_item(item, tabname) if items_html == '': @@ -127,8 +150,7 @@ class ExtraNetworksPage: metadata_button = "" metadata = item.get("metadata") if metadata: - metadata_onclick = '"' + html.escape(f"""extraNetworksShowMetadata({json.dumps(metadata)}); return false;""") + '"' - metadata_button = f"" + metadata_button = f"" args = { "preview_html": "style='background-image: url(\"" + html.escape(preview) + "\")'" if preview else '', @@ -215,6 +237,7 @@ def create_ui(container, button, tabname): with gr.Tabs(elem_id=tabname+"_extra_tabs") as tabs: for page in ui.stored_extra_pages: with gr.Tab(page.title): + page_elem = gr.HTML(page.create_html(ui.tabname)) ui.pages.append(page_elem) diff --git a/style.css b/style.css index aa5ecb34..41d2859c 100644 --- a/style.css +++ b/style.css @@ -641,6 +641,7 @@ footer { text-shadow: 2px 2px 3px black; padding: 0.25em; font-size: 22pt; + width: 1.5em; } .extra-network-cards .card:hover .metadata-button, .extra-network-thumbs .card:hover .metadata-button{ display: inline-block; -- cgit v1.2.3