import json
import html
import os.path
import urllib.parse
import threading
from pathlib import Path
from collections import OrderedDict
import gradio as gr
from PIL import Image
from modules import shared, scripts
from modules.generation_parameters_copypaste import image_from_url_text
from modules.ui_components import ToolButton
extra_pages = []
allowed_dirs = set()
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"""
extra_pages.append(page)
allowed_dirs.clear()
allowed_dirs.update(set(sum([x.allowed_directories_for_previews() for x in extra_pages], [])))
def fetch_file(filename: str = ""):
from starlette.responses import FileResponse, JSONResponse
if filename.startswith('html/'):
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
if not any(Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs):
return JSONResponse({"error": f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages."})
if os.path.splitext(filename)[1].lower() not in (".png", ".jpg", ".webp"):
return JSONResponse({"error": f"File cannot be fetched: {filename}. Only png and jpg and webp."})
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
def get_metadata(page: str = "", item: str = ""):
from starlette.responses import JSONResponse
page = next(iter([x for x in extra_pages if x.name == page]), None)
if page is None:
return JSONResponse({ 'metadata': 'none' })
metadata = page.metadata.get(item, 'none')
return JSONResponse({"metadata": metadata})
def get_info(page: str = "", item: str = ""):
from starlette.responses import JSONResponse
page = next(iter([x for x in extra_pages if x.name == page]), None)
if page is None:
return JSONResponse({ 'info': 'none' })
info = page.info.get(item, 'none')
return JSONResponse({"info": info})
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"])
app.add_api_route("/sd_extra_networks/info", get_info, methods=["GET"])
class ExtraNetworksPage:
def __init__(self, title):
self.title = title
self.name = title.lower()
self.allow_negative_prompt = False
self.metadata = {}
self.info = {}
self.html = ''
self.items = []
self.missing_thumbs = []
self.card = '''
{search_term}
{name}
{description}
đđđ đâšī¸
''' # noqa: RUF001
def refresh(self):
pass
def create_xyz_grid(self):
xyz_grid = [x for x in scripts.scripts_data if x.script_class.__module__ == "xyz_grid.py"][0].module
def add_prompt(p, opt, x):
for item in [x for x in self.items if x["name"] == opt]:
try:
p.prompt = f'{p.prompt} {eval(item["prompt"])}' # pylint: disable=eval-used
except Exception as e:
shared.log.error(f'Cannot evaluate extra network prompt: {item["prompt"]} {e}')
if not any(self.title in x.label for x in xyz_grid.axis_options):
if self.title == 'Checkpoints':
return
opt = xyz_grid.AxisOption(f"[Network] {self.title}", str, add_prompt, choices=lambda: [x["name"] for x in self.items])
xyz_grid.axis_options.append(opt)
def link_preview(self, filename):
quoted_filename = urllib.parse.quote(filename.replace('\\', '/'))
mtime = os.path.getmtime(filename)
return f"./sd_extra_networks/thumb?filename={quoted_filename}&mtime={mtime}"
def search_terms_from_path(self, filename, possible_directories=None):
abspath = os.path.abspath(filename)
for parentdir in (possible_directories if possible_directories is not None else self.allowed_directories_for_previews()):
parentdir = os.path.abspath(parentdir)
if abspath.startswith(parentdir):
return abspath[len(parentdir):].replace('\\', '/')
return ""
def is_empty(self, folder):
for f in os.listdir(folder):
_fn, ext = os.path.splitext(f)
if ext.lower() in ['.ckpt', '.safetensors', '.pt'] or os.path.isdir(os.path.join(folder, f)):
return False
return True
def create_thumb(self):
created = 0
for f in self.missing_thumbs:
fn, _ext = os.path.splitext(f)
fn = fn.replace('.preview', '')
fn = f'{fn}.thumb.jpg'
if os.path.exists(fn):
continue
try:
img = Image.open(f)
if img.width > 1024 or img.height > 1024:
img = img.convert('RGB')
img.thumbnail((512, 512), Image.HAMMING)
img.save(fn)
img.close()
created += 1
except Exception as e:
shared.log.error(f'Extra network error creating thumbnail: {f} {e}')
if len(self.missing_thumbs) > 0:
shared.log.info(f"Extra network created thumbnails: {self.name} {created}")
self.missing_thumbs.clear()
def create_html(self, tabname, skip = False):
self_name_id = self.name.replace(" ", "_")
if skip:
return f"
Extra network page not ready Click refresh to try again
"
subdirs = {}
allowed_folders = [os.path.abspath(x) for x in self.allowed_directories_for_previews()]
for parentdir in [*set(allowed_folders)]:
for root, dirs, _files in os.walk(parentdir, followlinks=True):
for dirname in dirs:
x = os.path.join(root, dirname)
if shared.opts.diffusers_dir in x:
subdirs[os.path.basename(shared.opts.diffusers_dir)] = 1
if (not os.path.isdir(x)) or ('models--' in x):
continue
subdir = os.path.abspath(x)[len(parentdir):].replace("\\", "/")
while subdir.startswith("/"):
subdir = subdir[1:]
if not self.is_empty(x):
subdirs[subdir] = 1
if subdirs:
subdirs = OrderedDict(sorted(subdirs.items()))
subdirs = {"": 1, **subdirs}
subdirs_html = "".join([f"""
""" for subdir in subdirs])
try:
if len(self.html) > 0:
res = f"
{subdirs_html}
{self.html}
"
return res
self.html = ''
self.items = list(self.list_items())
self.create_xyz_grid()
for item in self.items:
self.metadata[item["name"]] = item.get("metadata", {})
self.info[item["name"]] = self.find_info(item['filename'])
self.html += self.create_html_for_item(item, tabname)
if len(subdirs_html) > 0 or len(self.html) > 0:
res = f"