mirror of
https://github.com/vladmandic/automatic
synced 2026-09-18 16:54:33 +02:00
add styles to extra networks
This commit is contained in:
+9
-1
@@ -4,15 +4,23 @@
|
||||
|
||||
- extra networks:
|
||||
- support for **tags**
|
||||
search by tag, list tags, add to prompt, etc.
|
||||
show tags on hover, search by tag, list tags, add to prompt, etc.
|
||||
- **styles** are now also listed as part of extra networks
|
||||
existing `styles.csv` is converted upon startup to individual styles inside `models/style`
|
||||
this is stage one of new styles functionality
|
||||
old styles interface is still available, but will be removed in future
|
||||
- cache file lists for much faster startup
|
||||
- simplified handling of **descriptions**
|
||||
now shows on-mouse-over without the need for user interaction
|
||||
- **metadata** and **info** buttons only show if there is actual content
|
||||
- diffusers:
|
||||
- ability to interrupt (stop/skip) model generate
|
||||
- add `diffusers_force_zeros` setting
|
||||
create zero-tensor for prompt if prompt is empty (positive or negative)
|
||||
- add `diffusers_aesthetics_score` setting
|
||||
automatically guide unet towards higher pleasing images
|
||||
- fixes:
|
||||
- pre-create all system folders on startup
|
||||
|
||||
## Update for 2023-08-30
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
"hash": lora_on_disk.shorthash,
|
||||
"preview": self.find_preview(path),
|
||||
"description": self.find_description(path),
|
||||
"info": self.find_info(path),
|
||||
"search_term": self.search_terms_from_path(lora_on_disk.filename) + ' '.join(tags.keys()),
|
||||
"prompt": prompt,
|
||||
"local_preview": f"{path}.{shared.opts.samples_format}",
|
||||
|
||||
Submodule extensions-builtin/sd-webui-controlnet updated: c3b32f2543...42bb1b1a62
@@ -478,6 +478,10 @@ def check_modified_files():
|
||||
res = git('status --porcelain')
|
||||
files = [x[2:].strip() for x in res.split('\n')]
|
||||
files = [x for x in files if len(x) > 0 and (not x.startswith('extensions')) and (not x.startswith('wiki')) and (not x.endswith('.json')) and ('.log' not in x)]
|
||||
deleted = [x for x in files if not os.path.exists(x)]
|
||||
if len(deleted) > 0:
|
||||
log.warning(f'Deleted files: {files}')
|
||||
files = [x for x in files if os.path.exists(x) and not os.path.isdir(x)]
|
||||
if len(files) > 0:
|
||||
log.warning(f'Modified files: {files}')
|
||||
except Exception:
|
||||
|
||||
@@ -278,4 +278,21 @@ function extraNetworksSearchButton(event) {
|
||||
updateInput(searchTextarea);
|
||||
}
|
||||
|
||||
let desiredStyle = '';
|
||||
function selectStyle(name) {
|
||||
desiredStyle = name;
|
||||
const tabname = getENActiveTab();
|
||||
const button = gradioApp().querySelector(`#${tabname}_styles_select`);
|
||||
button.click();
|
||||
}
|
||||
|
||||
function applyStyles(styles) {
|
||||
let newStyles = [];
|
||||
if (styles) newStyles = Array.isArray(styles) ? styles : [styles];
|
||||
const index = newStyles.indexOf(desiredStyle);
|
||||
if (index > -1) newStyles.splice(index, 1);
|
||||
else newStyles.push(desiredStyle);
|
||||
return newStyles.join('|');
|
||||
}
|
||||
|
||||
onUiLoaded(setupExtraNetworks);
|
||||
|
||||
@@ -235,7 +235,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt
|
||||
.extra-networks-page { display: flex }
|
||||
.extra-networks .custom-button { width: 120px; width: 100%; background: none; justify-content: left; text-align: left; padding: 2px 8px 2px 16px; text-indent: -8px; box-shadow: none; line-break: auto; }
|
||||
.extra-networks .custom-button:hover { background: var(--button-primary-background-fill) }
|
||||
.extra-network-cards { display: flex; flex-wrap: wrap; overflow-y: scroll; overflow-x: hidden; width: -webkit-fill-available; }
|
||||
.extra-network-cards { display: flex; flex-wrap: wrap; overflow-y: auto; overflow-x: hidden; align-content: flex-start; width: -webkit-fill-available; }
|
||||
.extra-network-cards .card { height: fit-content; margin: 0.5em; position: relative; scroll-snap-align: start; scroll-margin-top: 0; }
|
||||
.extra-network-cards .card .overlay { position: absolute; bottom: 0; padding: 0.2em; z-index: 10; width: 100%; background: none; }
|
||||
.extra-network-cards .card:hover .overlay { background: rgba(0, 0, 0, 0.40); }
|
||||
|
||||
@@ -204,7 +204,7 @@ class PreprocessResponse(BaseModel):
|
||||
|
||||
fields = {}
|
||||
for key, metadata in shared.opts.data_labels.items():
|
||||
value = shared.opts.data.get(key)
|
||||
value = shared.opts.data.get(key) or shared.opts.data_labels[key].default
|
||||
optType = shared.opts.typemap.get(type(metadata.default), type(value))
|
||||
|
||||
if metadata is not None:
|
||||
|
||||
+16
-7
@@ -50,29 +50,37 @@ for d, must_exist, what, _options in path_dirs:
|
||||
paths[what] = d
|
||||
|
||||
|
||||
def create_paths(opts):
|
||||
def create_paths(opts, log=None):
|
||||
def create_path(folder):
|
||||
if folder is None or folder == '':
|
||||
return
|
||||
if not os.path.exists(folder):
|
||||
try:
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
if log is not None:
|
||||
log.debug(f'Create path: {folder}')
|
||||
except Exception as e:
|
||||
if log is not None:
|
||||
log.error(f'Failed to create path: {folder} {e}')
|
||||
|
||||
def fix_path(folder):
|
||||
if opts.data.get(folder, None) is None or opts.data[folder] is None or opts.data[folder] == '':
|
||||
tgt = opts.data.get(folder, None) or opts.data_labels[folder].default
|
||||
if tgt is None or tgt == '':
|
||||
return
|
||||
if os.path.isabs(opts.data[folder]) or (len(data_path) > 0 and opts.data[folder].startswith(data_path)) and not opts.data[folder].startswith(script_path):
|
||||
if os.path.isabs(tgt) or (len(data_path) > 0 and tgt.startswith(data_path)) and not tgt.startswith(script_path):
|
||||
return
|
||||
fullpath = os.path.join(data_path, opts.data[folder])
|
||||
fullpath = os.path.join(data_path, tgt)
|
||||
relpath = os.path.relpath(fullpath, script_path)
|
||||
opts.data[folder] = relpath
|
||||
return
|
||||
|
||||
create_path(fix_path('temp_dir'))
|
||||
create_path(data_path)
|
||||
create_path(script_path)
|
||||
create_path(models_path)
|
||||
create_path(sd_configs_path)
|
||||
create_path(extensions_dir)
|
||||
create_path(extensions_builtin_dir)
|
||||
create_path(fix_path('temp_dir'))
|
||||
create_path(fix_path('hypernetwork_dir'))
|
||||
create_path(fix_path('ckpt_dir'))
|
||||
create_path(fix_path('vae_dir'))
|
||||
@@ -86,6 +94,7 @@ def create_paths(opts):
|
||||
create_path(fix_path('outdir_txt2img_grids'))
|
||||
create_path(fix_path('outdir_img2img_grids'))
|
||||
create_path(fix_path('outdir_save'))
|
||||
create_path(fix_path('styles_dir'))
|
||||
|
||||
|
||||
class Prioritize:
|
||||
|
||||
@@ -536,7 +536,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
return None
|
||||
stored_opts = {}
|
||||
for k in p.override_settings.keys():
|
||||
stored_opts[k] = shared.opts.data.get(k, None)
|
||||
stored_opts[k] = shared.opts.data.get(k, None) or shared.opts.data_labels[k].default
|
||||
try:
|
||||
# if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint
|
||||
if p.override_settings.get('sd_model_checkpoint', None) is not None and sd_models.checkpoint_aliases.get(p.override_settings.get('sd_model_checkpoint')) is None:
|
||||
@@ -575,6 +575,8 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
setattr(shared.opts, k, v)
|
||||
if k == 'sd_model_checkpoint':
|
||||
sd_models.reload_model_weights()
|
||||
if k == 'sd_model_refiner':
|
||||
sd_models.reload_model_weights()
|
||||
if k == 'sd_vae':
|
||||
sd_vae.reload_vae_weights()
|
||||
return res
|
||||
|
||||
@@ -329,12 +329,15 @@ def read_metadata_from_safetensors(filename):
|
||||
json_data = json_start + file.read(metadata_len-2)
|
||||
json_obj = json.loads(json_data)
|
||||
for k, v in json_obj.get("__metadata__", {}).items():
|
||||
res[k] = v
|
||||
if k == 'format' and v == 'pt':
|
||||
continue
|
||||
if isinstance(v, str) and v[0:1] == '{':
|
||||
try:
|
||||
res[k] = json.loads(v)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
res[k] = v
|
||||
sd_metadata[filename] = res
|
||||
global sd_metadata_pending # pylint: disable=global-statement
|
||||
sd_metadata_pending += 1
|
||||
|
||||
+1
-2
@@ -631,7 +631,6 @@ options_templates.update(options_section(('interrogate', "Interrogate"), {
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('extra_networks', "Extra Networks"), {
|
||||
"ui_extra_networks_tab_reorder": OptionInfo("Checkpoints, Lora, LyCORIS, Textual Inversion, Hypernetworks", "Extra networks tab order"),
|
||||
"extra_networks_card_cover": OptionInfo("sidebar", "UI position", gr.Radio, lambda: {"choices": ["cover", "inline", "sidebar"]}),
|
||||
"extra_networks_height": OptionInfo(53, "UI height (%)", gr.Slider, {"minimum": 10, "maximum": 100, "step": 1}),
|
||||
"extra_networks_sidebar_width": OptionInfo(35, "UI sidebar width (%)", gr.Slider, {"minimum": 10, "maximum": 80, "step": 1}),
|
||||
@@ -820,7 +819,7 @@ opts.data['uni_pc_lower_order_final'] = opts.schedulers_use_loworder
|
||||
opts.data['uni_pc_order'] = opts.schedulers_solver_order
|
||||
log.info(f'Engine: backend={backend}')
|
||||
|
||||
prompt_styles = modules.styles.StyleDatabase(opts.styles_dir)
|
||||
prompt_styles = modules.styles.StyleDatabase(opts)
|
||||
cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or (cmd_opts.server_name or False)) and not cmd_opts.insecure
|
||||
devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer'])
|
||||
device = devices.device
|
||||
|
||||
+61
-17
@@ -2,11 +2,11 @@
|
||||
from __future__ import annotations
|
||||
import csv
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import json
|
||||
import shutil
|
||||
import typing
|
||||
from installer import log
|
||||
from modules import paths
|
||||
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -18,6 +18,7 @@ class PromptStyle(typing.NamedTuple):
|
||||
name: str
|
||||
prompt: str
|
||||
negative_prompt: str
|
||||
extra: str = ""
|
||||
|
||||
|
||||
def merge_prompts(style_prompt: str, prompt: str) -> str:
|
||||
@@ -41,26 +42,38 @@ def apply_styles_to_prompt(prompt, styles):
|
||||
|
||||
|
||||
class StyleDatabase:
|
||||
def __init__(self, path: str):
|
||||
def __init__(self, opts):
|
||||
self.no_style = PromptStyle("None", "", "")
|
||||
self.styles = {}
|
||||
self.path = path
|
||||
self.path = opts.styles_dir
|
||||
if os.path.isfile(opts.styles_dir):
|
||||
legacy_file = opts.styles_dir
|
||||
self.load_csv(legacy_file)
|
||||
opts.styles_dir = os.path.join(paths.models_path, "styles")
|
||||
self.path = opts.styles_dir
|
||||
self.mkdir()
|
||||
self.save_styles(opts.styles_dir, verbose=True)
|
||||
log.debug(f'Migrated styles: file={legacy_file} folder={self.path}')
|
||||
self.mkdir()
|
||||
self.reload()
|
||||
|
||||
def mkdir(self):
|
||||
if not os.path.isdir(self.path):
|
||||
os.makedirs(self.path, exist_ok=True)
|
||||
log.debug(f'Created styles: folder={self.path}')
|
||||
|
||||
def reload(self):
|
||||
self.styles.clear()
|
||||
if not os.path.exists(self.path):
|
||||
self.save_styles(self.path)
|
||||
with open(self.path, "r", encoding="utf-8-sig", newline='') as file:
|
||||
reader = csv.DictReader(file, skipinitialspace=True)
|
||||
for row in reader:
|
||||
for fn in os.listdir(self.path):
|
||||
if not fn.endswith(".json"):
|
||||
continue
|
||||
with open(os.path.join(self.path, fn), 'r', encoding='utf-8') as f:
|
||||
try:
|
||||
prompt = row["prompt"] if "prompt" in row else row["text"]
|
||||
negative_prompt = row.get("negative_prompt", "")
|
||||
self.styles[row["name"]] = PromptStyle(row["name"], prompt, negative_prompt)
|
||||
except Exception:
|
||||
log.error(f'Styles error: {self.path} {row}')
|
||||
log.debug(f'Loaded styles: {self.path} items={len(self.styles.keys())}')
|
||||
style = json.load(f)
|
||||
self.styles[style["name"]] = PromptStyle(style["name"], style["prompt"], style["negative"], style["extra"])
|
||||
except Exception as e:
|
||||
log.error(f'Failed to load style: file={fn} error={e}')
|
||||
log.debug(f'Loaded styles: folder={self.path} items={len(self.styles.keys())}')
|
||||
|
||||
def get_style_prompts(self, styles):
|
||||
return [self.styles.get(x, self.no_style).prompt for x in styles]
|
||||
@@ -74,7 +87,38 @@ class StyleDatabase:
|
||||
def apply_negative_styles_to_prompt(self, prompt, styles):
|
||||
return apply_styles_to_prompt(prompt, [self.styles.get(x, self.no_style).negative_prompt for x in styles])
|
||||
|
||||
def save_styles(self, path: str) -> None:
|
||||
def save_styles(self, path, verbose=False):
|
||||
for name in list(self.styles):
|
||||
style = {
|
||||
"name": name,
|
||||
"prompt": self.styles[name].prompt,
|
||||
"negative": self.styles[name].negative_prompt,
|
||||
"extra": "",
|
||||
}
|
||||
fn = os.path.join(path, name + ".json")
|
||||
try:
|
||||
with open(fn, 'w', encoding='utf-8') as f:
|
||||
json.dump(style, f, indent=2)
|
||||
if verbose:
|
||||
log.debug(f'Saved style: name={name} file={fn}')
|
||||
except Exception as e:
|
||||
log.error(f'Failed to save style: name={name} file={path} error={e}')
|
||||
log.debug(f'Saved styles: {path} {len(self.styles.keys())}')
|
||||
|
||||
def load_csv(self, legacy_file):
|
||||
with open(legacy_file, "r", encoding="utf-8-sig", newline='') as file:
|
||||
reader = csv.DictReader(file, skipinitialspace=True)
|
||||
for row in reader:
|
||||
try:
|
||||
prompt = row["prompt"] if "prompt" in row else row["text"]
|
||||
negative_prompt = row.get("negative_prompt", "")
|
||||
self.styles[row["name"]] = PromptStyle(row["name"], prompt, negative_prompt)
|
||||
except Exception:
|
||||
log.error(f'Styles error: file={legacy_file} row={row}')
|
||||
log.debug(f'Loaded legacy styles: file={legacy_file} items={len(self.styles.keys())}')
|
||||
|
||||
def save_csv(self, path: str) -> None:
|
||||
import tempfile
|
||||
basedir = os.path.dirname(path)
|
||||
if basedir is not None and len(basedir) > 0:
|
||||
os.makedirs(basedir, exist_ok=True)
|
||||
@@ -83,5 +127,5 @@ class StyleDatabase:
|
||||
writer = csv.DictWriter(file, fieldnames=PromptStyle._fields)
|
||||
writer.writeheader()
|
||||
writer.writerows(style._asdict() for k, style in self.styles.items())
|
||||
log.debug(f'Saved styles: {path} {len(self.styles.keys())}')
|
||||
log.debug(f'Saved legacy styles: {path} {len(self.styles.keys())}')
|
||||
shutil.move(temp_path, path)
|
||||
|
||||
@@ -120,11 +120,11 @@ class EmbeddingDatabase:
|
||||
return embedding
|
||||
|
||||
def get_expected_shape(self):
|
||||
if shared.backend == shared.Backend.DIFFUSERS:
|
||||
return 0
|
||||
if shared.sd_model is None:
|
||||
shared.log.error('Model not loaded')
|
||||
return 0
|
||||
if shared.backend == shared.Backend.DIFFUSERS:
|
||||
return 0
|
||||
vec = shared.sd_model.cond_stage_model.encode_embedding_init_text(",", 1)
|
||||
return vec.shape[1]
|
||||
|
||||
@@ -213,11 +213,14 @@ class EmbeddingDatabase:
|
||||
self.skipped_embeddings[name] = embedding
|
||||
|
||||
def load_from_dir(self, embdir):
|
||||
if sd_models.model_data.sd_model is None:
|
||||
shared.log.info('Skipping embeddings load: model not loaded')
|
||||
return
|
||||
if not os.path.isdir(embdir.path):
|
||||
return
|
||||
|
||||
is_ext = extension_filter(['.PNG', '.WEBP', '.JXL', '.AVIF', '.BIN', '.PT', '.SAFETENSORS'])
|
||||
is_not_preview = lambda fp: not next(iter(os.path.splitext(fp))).upper().endswith('.PREVIEW')
|
||||
is_not_preview = lambda fp: not next(iter(os.path.splitext(fp))).upper().endswith('.PREVIEW') # pylint: disable=unnecessary-lambda-assignment
|
||||
|
||||
for file_path in [*filter(lambda fp: is_ext(fp) and is_not_preview(fp), directory_files(embdir.path))]:
|
||||
try:
|
||||
|
||||
@@ -117,6 +117,10 @@ def apply_styles(prompt, prompt_neg, styles):
|
||||
return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=prompt_neg), gr.Dropdown.update(value=[])]
|
||||
|
||||
|
||||
def parse_style(styles):
|
||||
return styles.split('|')
|
||||
|
||||
|
||||
def process_interrogate(interrogation_function, mode, ii_input_files, ii_input_dir, ii_output_dir, *ii_singles):
|
||||
if mode in {0, 1, 3, 4}:
|
||||
return [interrogation_function(ii_singles[mode]), None]
|
||||
@@ -278,6 +282,8 @@ def create_toprow(is_img2img):
|
||||
with gr.Row(elem_id=f"{id_part}_styles_row"):
|
||||
prompt_styles = gr.Dropdown(label="Styles", elem_id=f"{id_part}_styles", choices=[k for k, v in modules.shared.prompt_styles.styles.items()], value=[], multiselect=True)
|
||||
create_refresh_button(prompt_styles, modules.shared.prompt_styles.reload, lambda: {"choices": [k for k, v in modules.shared.prompt_styles.styles.items()]}, f"refresh_{id_part}_styles")
|
||||
prompt_styles_btn = gr.Button('Apply', elem_id=f"{id_part}_styles_select", visible=False)
|
||||
prompt_styles_btn.click(_js="applyStyles", fn=parse_style, inputs=[prompt_styles], outputs=[prompt_styles])
|
||||
return prompt, prompt_styles, negative_prompt, submit, button_interrogate, button_deepbooru, prompt_style_apply, save_style, paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button
|
||||
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ class ExtraNetworksPage:
|
||||
self.html = ''
|
||||
self.items = []
|
||||
self.missing_thumbs = []
|
||||
self.refresh_time = None
|
||||
# class additional is to keep old extensions happy
|
||||
self.card = '''
|
||||
<div class='card' onclick={card_click} title='{title}' data-filename='{local_preview}' data-description='{description}' data-tags='{tags}'>
|
||||
@@ -102,13 +103,12 @@ class ExtraNetworksPage:
|
||||
<div class='additional'><ul></ul></div>
|
||||
<span title="Save current image as preview image" onclick={card_save_preview}>⏺️</span>
|
||||
<span title="Save current description" onclick={card_save_desc}>🛅</span>
|
||||
<span title="Read metadata" onclick={card_read_meta}>📘</span>
|
||||
<span title="Read info" onclick={card_read_info}>ℹ️</span>
|
||||
{card_extra}
|
||||
</div>
|
||||
</div>
|
||||
<img class='preview' src='{preview}' style='width: {width}px; height: {height}px; object-fit: {fit}' loading='{loading}'></img>
|
||||
</div>
|
||||
''' # noqa: RUF001
|
||||
'''
|
||||
|
||||
def refresh(self):
|
||||
pass
|
||||
@@ -168,11 +168,11 @@ class ExtraNetworksPage:
|
||||
created += 1
|
||||
except Exception as e:
|
||||
shared.log.error(f'Extra network error creating thumbnail: {f} {e}')
|
||||
if len(self.missing_thumbs) > 0:
|
||||
if created > 0:
|
||||
shared.log.info(f"Extra network created thumbnails: {self.name} {created}")
|
||||
self.missing_thumbs.clear()
|
||||
|
||||
def create_html(self, tabname, skip = False):
|
||||
def create_page(self, tabname, skip = False):
|
||||
t0 = time.time()
|
||||
self_name_id = self.name.replace(" ", "_")
|
||||
if skip:
|
||||
@@ -197,35 +197,35 @@ class ExtraNetworksPage:
|
||||
<button class='lg secondary gradio-button custom-button{" search-all" if subdir=="" else ""}' onclick='extraNetworksSearchButton(event)'>
|
||||
{html.escape(subdir) if subdir!="" else "all"}
|
||||
</button><br>""" for subdir in subdirs])
|
||||
try:
|
||||
if len(self.html) > 0:
|
||||
res = f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}</div><div id='{tabname}_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"
|
||||
return res
|
||||
self.html = ''
|
||||
# try:
|
||||
if len(self.html) > 0:
|
||||
res = f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}</div><div id='{tabname}_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"
|
||||
return res
|
||||
self.html = ''
|
||||
if self.refresh_time is None or len(self.items) == 0:
|
||||
try:
|
||||
self.items = list(self.list_items())
|
||||
except Exception as e:
|
||||
self.items = []
|
||||
shared.log.error(f'Extra networks error listing items: class={self.__class__} tab={tabname} {e}')
|
||||
self.create_xyz_grid()
|
||||
htmls = []
|
||||
items = self.items
|
||||
for item in items:
|
||||
self.metadata[item["name"]] = item.get("metadata", {})
|
||||
self.info[item["name"]] = self.find_info(item['filename'])
|
||||
htmls.append(self.create_html_for_item(item, tabname))
|
||||
self.html += ''.join(htmls)
|
||||
if len(subdirs_html) > 0 or len(self.html) > 0:
|
||||
res = f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}</div><div id='{tabname}_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"
|
||||
else:
|
||||
return ''
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Extra networks: {self.name} items={len(self.items)} subdirs={len(subdirs)} time={round(t1-t0, 2)}')
|
||||
threading.Thread(target=self.create_thumb).start()
|
||||
return res
|
||||
except Exception as e:
|
||||
shared.log.error(f'Extra networks page error: title={self.title} tab={tabname} class={e.__class__.__name__} {e}')
|
||||
return f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'></div><div id='{tabname}_{self_name_id}_cards' class='extra-network-cards'>Extra network error<br>{e}</div>"
|
||||
self.create_xyz_grid()
|
||||
htmls = []
|
||||
for item in self.items:
|
||||
self.metadata[item["name"]] = item.get("metadata", {})
|
||||
self.info[item["name"]] = item.get('info', None) or self.find_info(item['filename'])
|
||||
htmls.append(self.create_html(item, tabname))
|
||||
self.html += ''.join(htmls)
|
||||
if len(subdirs_html) > 0 or len(self.html) > 0:
|
||||
res = f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}</div><div id='{tabname}_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"
|
||||
else:
|
||||
return ''
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Extra networks: {self.name} items={len(self.items)} subdirs={len(subdirs)} time={round(t1-t0, 2)}')
|
||||
threading.Thread(target=self.create_thumb).start()
|
||||
return res
|
||||
# except Exception as e:
|
||||
# shared.log.error(f'Extra networks page error: title={self.title} tab={tabname} class={e.__class__.__name__} {e}')
|
||||
# return f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'></div><div id='{tabname}_{self_name_id}_cards' class='extra-network-cards'>Extra network error<br>{e}</div>"
|
||||
|
||||
def list_items(self):
|
||||
raise NotImplementedError
|
||||
@@ -233,7 +233,7 @@ class ExtraNetworksPage:
|
||||
def allowed_directories_for_previews(self):
|
||||
return []
|
||||
|
||||
def create_html_for_item(self, item, tabname):
|
||||
def create_html(self, item, tabname):
|
||||
try:
|
||||
args = {
|
||||
"tabname": json.dumps(tabname),
|
||||
@@ -252,11 +252,19 @@ class ExtraNetworksPage:
|
||||
"card_click": item.get("onclick", '"' + html.escape(f'return cardClicked({item.get("prompt", None)}, {"true" if self.allow_negative_prompt else "false"})') + '"'),
|
||||
"card_save_preview": '"' + html.escape('return saveCardPreview(event)') + '"',
|
||||
"card_save_desc": '"' + html.escape('return saveCardDescription(event)') + '"',
|
||||
"card_read_meta": '"' + html.escape(f'return readCardMetadata(event, {json.dumps(self.name)}, {json.dumps(item["name"])})') + '"',
|
||||
"card_read_info": '"' + html.escape(f'return readCardInformation(event, {json.dumps(self.name)}, {json.dumps(item["name"])})') + '"',
|
||||
"card_extra": "",
|
||||
}
|
||||
if item.get("alias", None) is not None:
|
||||
args['title'] += f'\nAlias: {item["alias"]}'
|
||||
metadata = item.get("metadata", None)
|
||||
if metadata is not None and len(metadata) > 0:
|
||||
card_read_meta = '"' + html.escape(f'return readCardMetadata(event, {json.dumps(self.name)}, {json.dumps(item["name"])})') + '"'
|
||||
args['card_extra'] += f'<span title="Read metadata" onclick={card_read_meta}>📘</span>'
|
||||
info = item.get("info", None)
|
||||
if info is not None and len(info) > 0:
|
||||
card_read_info = '"' + html.escape(f'return readCardInformation(event, {json.dumps(self.name)}, {json.dumps(item["name"])})') + '"'
|
||||
args['card_extra'] += f'<span title="Read info" onclick={card_read_info}>ℹ️</span>' # noqa
|
||||
alias = item.get("alias", None)
|
||||
if alias is not None:
|
||||
args['title'] += f'\nAlias: {alias}'
|
||||
return self.card.format(**args)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Extra networks item error: page={tabname} item={item["name"]} {e}')
|
||||
@@ -308,9 +316,11 @@ def register_default_pages():
|
||||
from modules.ui_extra_networks_textual_inversion import ExtraNetworksPageTextualInversion
|
||||
from modules.ui_extra_networks_hypernets import ExtraNetworksPageHypernetworks
|
||||
from modules.ui_extra_networks_checkpoints import ExtraNetworksPageCheckpoints
|
||||
from modules.ui_extra_networks_styles import ExtraNetworksPageStyles
|
||||
register_page(ExtraNetworksPageCheckpoints())
|
||||
register_page(ExtraNetworksPageStyles())
|
||||
register_page(ExtraNetworksPageTextualInversion())
|
||||
register_page(ExtraNetworksPageHypernetworks())
|
||||
register_page(ExtraNetworksPageCheckpoints())
|
||||
|
||||
|
||||
class ExtraNetworksUi:
|
||||
@@ -328,24 +338,10 @@ class ExtraNetworksUi:
|
||||
self.search = None
|
||||
|
||||
|
||||
def sort_extra_pages(pages):
|
||||
tab_order = [x.lower().strip() for x in shared.opts.ui_extra_networks_tab_reorder.split(",")]
|
||||
|
||||
def tab_name_score(name):
|
||||
name = name.lower()
|
||||
for i, possible_match in enumerate(tab_order):
|
||||
if possible_match in name:
|
||||
return i
|
||||
return len(pages)
|
||||
|
||||
tab_scores = {page.name: (tab_name_score(page.name), original_index) for original_index, page in enumerate(pages)}
|
||||
return sorted(pages, key=lambda x: tab_scores[x.name])
|
||||
|
||||
|
||||
def create_ui(container, button, tabname, skip_indexing = False):
|
||||
ui = ExtraNetworksUi()
|
||||
ui.pages = []
|
||||
ui.stored_extra_pages = sort_extra_pages(extra_pages)
|
||||
ui.stored_extra_pages = extra_pages
|
||||
ui.tabname = tabname
|
||||
with gr.Tabs(elem_id=tabname+"_extra_tabs"):
|
||||
button_refresh = ToolButton(refresh_symbol, elem_id=tabname+"_extra_refresh")
|
||||
@@ -359,9 +355,9 @@ def create_ui(container, button, tabname, skip_indexing = False):
|
||||
|
||||
for page in ui.stored_extra_pages:
|
||||
shared.log.debug(f"Extra network page: {page.title} tab={tabname}")
|
||||
page_html = page.create_html(ui.tabname, skip_indexing)
|
||||
page_html = page.create_page(ui.tabname, skip_indexing)
|
||||
with gr.Tab(page.title, id=page.title.lower().replace(" ", "_"), elem_classes="extra-networks-tab"):
|
||||
page_elem = gr.HTML(page_html, elem_id=tabname+page.name+"_extra_page", elem_classes="extra-networks-page")
|
||||
page_elem = gr.HTML(page_html, elem_id=f'{tabname}{page.name}_extra_page', elem_classes="extra-networks-page")
|
||||
page_elem.change(fn=lambda: None, _js=f'() => refreshExtraNetworks("{tabname}")', inputs=[], outputs=[])
|
||||
ui.pages.append(page_elem)
|
||||
|
||||
@@ -378,8 +374,9 @@ def create_ui(container, button, tabname, skip_indexing = False):
|
||||
res = []
|
||||
for pg in ui.stored_extra_pages:
|
||||
pg.html = ''
|
||||
pg.refresh_time = None
|
||||
pg.refresh()
|
||||
res.append(pg.create_html(ui.tabname))
|
||||
res.append(pg.create_page(ui.tabname))
|
||||
ui.search.update(value = ui.search.value)
|
||||
return res
|
||||
|
||||
@@ -396,7 +393,7 @@ def path_is_parent(parent_path, child_path):
|
||||
def setup_ui(ui, gallery):
|
||||
def save_preview(index, images, filename):
|
||||
if len(images) == 0:
|
||||
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
|
||||
return [page.create_page(ui.tabname) for page in ui.stored_extra_pages]
|
||||
index = int(index)
|
||||
index = 0 if index < 0 else index
|
||||
index = len(images) - 1 if index >= len(images) else index
|
||||
@@ -415,11 +412,11 @@ def setup_ui(ui, gallery):
|
||||
shared.log.debug(f'Extra network delete thumbnail: {thumb}')
|
||||
os.remove(thumb)
|
||||
shared.log.info(f'Extra network save preview: {filename}')
|
||||
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
|
||||
return [page.create_page(ui.tabname) for page in ui.stored_extra_pages]
|
||||
|
||||
ui.button_save_preview.click(
|
||||
fn=save_preview,
|
||||
_js="function(x, y, z){return [selected_gallery_index(), y, z]}",
|
||||
_js="function(x, y, z) {return [selected_gallery_index(), y, z]}",
|
||||
inputs=[ui.preview_target_filename, gallery, ui.preview_target_filename],
|
||||
outputs=[*ui.pages]
|
||||
)
|
||||
@@ -435,11 +432,11 @@ def setup_ui(ui, gallery):
|
||||
shared.log.info(f'Extra network save description: {filename} {desc}')
|
||||
except Exception as e:
|
||||
shared.log.error(f'Extra network save description: {filename} {e}')
|
||||
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
|
||||
return [page.create_page(ui.tabname) for page in ui.stored_extra_pages]
|
||||
|
||||
ui.button_save_description.click(
|
||||
fn=save_description,
|
||||
_js="function(x,y) {return [x,y]}",
|
||||
_js="function(x, y) { return [x, y] }",
|
||||
inputs=[ui.description_target_filename, ui.description],
|
||||
outputs=[*ui.pages]
|
||||
)
|
||||
|
||||
@@ -23,6 +23,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
|
||||
"hash": checkpoint.shorthash,
|
||||
"preview": self.find_preview(path),
|
||||
"description": self.find_description(path),
|
||||
"info": self.find_info(path),
|
||||
"search_term": f'{self.search_terms_from_path(checkpoint.filename)} {(checkpoint.sha256 or "")} /{checkpoint.type}/',
|
||||
"onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"',
|
||||
"local_preview": f"{path}.{shared.opts.samples_format}",
|
||||
|
||||
@@ -18,6 +18,7 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
|
||||
"filename": path,
|
||||
"preview": self.find_preview(path),
|
||||
"description": self.find_description(path),
|
||||
"info": self.find_info(path),
|
||||
"search_term": self.search_terms_from_path(path),
|
||||
"prompt": json.dumps(f"<hypernet:{name}:{shared.opts.extra_networks_default_multiplier}>"),
|
||||
"local_preview": f"{path}.preview.{shared.opts.samples_format}",
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
import html
|
||||
import json
|
||||
|
||||
from modules import shared, ui_extra_networks
|
||||
|
||||
|
||||
class ExtraNetworksPageStyles(ui_extra_networks.ExtraNetworksPage):
|
||||
def __init__(self):
|
||||
super().__init__('Styles')
|
||||
|
||||
def refresh(self):
|
||||
shared.prompt_styles.reload()
|
||||
|
||||
def list_items(self):
|
||||
styles = list(shared.prompt_styles.styles)
|
||||
for style in styles:
|
||||
path = os.path.join(shared.opts.styles_dir, style)
|
||||
txt = f'Prompt: {shared.prompt_styles.styles[style].prompt}'
|
||||
negative = shared.prompt_styles.styles[style].negative_prompt
|
||||
if negative is not None and len(negative) > 0:
|
||||
txt += f'\nNegative: {negative}'
|
||||
yield {
|
||||
"name": style,
|
||||
"search_term": path,
|
||||
"filename": path,
|
||||
"preview": self.find_preview(path),
|
||||
"description": txt,
|
||||
"onclick": '"' + html.escape(f"""return selectStyle({json.dumps(style)})""") + '"',
|
||||
"local_preview": f"{path}.{shared.opts.samples_format}",
|
||||
}
|
||||
|
||||
def allowed_directories_for_previews(self):
|
||||
return [v for v in [shared.opts.styles_dir] if v is not None]
|
||||
@@ -29,6 +29,7 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
|
||||
"filename": embedding.filename,
|
||||
"preview": self.find_preview(path),
|
||||
"description": self.find_description(path),
|
||||
"info": self.find_info(path),
|
||||
"search_term": self.search_terms_from_path(embedding.filename),
|
||||
"prompt": json.dumps(os.path.splitext(embedding.name)[0]),
|
||||
"local_preview": f"{path}.preview.{shared.opts.samples_format}",
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ requests==2.31.0
|
||||
tqdm==4.65.0
|
||||
accelerate==0.20.3
|
||||
opencv-python-headless==4.7.0.72
|
||||
diffusers==0.20.1
|
||||
diffusers==0.20.2
|
||||
einops==0.4.1
|
||||
gradio==3.41.2
|
||||
huggingface_hub==0.16.4
|
||||
|
||||
@@ -242,7 +242,7 @@ def start_common():
|
||||
log.info(f'Using data path: {shared.cmd_opts.data_dir}')
|
||||
if shared.cmd_opts.models_dir is not None and len(shared.cmd_opts.models_dir) > 0:
|
||||
log.info(f'Using models path: {shared.cmd_opts.data_dir}')
|
||||
create_paths(opts)
|
||||
create_paths(opts, log)
|
||||
async_policy()
|
||||
initialize()
|
||||
if shared.opts.clean_temp_dir_at_start:
|
||||
|
||||
Reference in New Issue
Block a user