mirror of
https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
synced 2026-04-23 15:59:29 +00:00
Merge branch 'dev' into gradio4
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import functools
|
||||
import os.path
|
||||
import urllib.parse
|
||||
from base64 import b64decode
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
from dataclasses import dataclass
|
||||
@@ -11,6 +13,7 @@ import gradio as gr
|
||||
import json
|
||||
import html
|
||||
from fastapi.exceptions import HTTPException
|
||||
from PIL import Image
|
||||
|
||||
from modules.infotext_utils import image_from_url_text
|
||||
|
||||
@@ -108,6 +111,31 @@ def fetch_file(filename: str = ""):
|
||||
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
|
||||
|
||||
|
||||
def fetch_cover_images(page: str = "", item: str = "", index: int = 0):
|
||||
from starlette.responses import Response
|
||||
|
||||
page = next(iter([x for x in extra_pages if x.name == page]), None)
|
||||
if page is None:
|
||||
raise HTTPException(status_code=404, detail="File not found")
|
||||
|
||||
metadata = page.metadata.get(item)
|
||||
if metadata is None:
|
||||
raise HTTPException(status_code=404, detail="File not found")
|
||||
|
||||
cover_images = json.loads(metadata.get('ssmd_cover_images', {}))
|
||||
image = cover_images[index] if index < len(cover_images) else None
|
||||
if not image:
|
||||
raise HTTPException(status_code=404, detail="File not found")
|
||||
|
||||
try:
|
||||
image = Image.open(BytesIO(b64decode(image)))
|
||||
buffer = BytesIO()
|
||||
image.save(buffer, format=image.format)
|
||||
return Response(content=buffer.getvalue(), media_type=image.get_format_mimetype())
|
||||
except Exception as err:
|
||||
raise ValueError(f"File cannot be fetched: {item}. Failed to load cover image.") from err
|
||||
|
||||
|
||||
def get_metadata(page: str = "", item: str = ""):
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
@@ -119,6 +147,8 @@ def get_metadata(page: str = "", item: str = ""):
|
||||
if metadata is None:
|
||||
return JSONResponse({})
|
||||
|
||||
metadata = {i:metadata[i] for i in metadata if i != 'ssmd_cover_images'} # those are cover images, and they are too big to display in UI as text
|
||||
|
||||
return JSONResponse({"metadata": json.dumps(metadata, indent=4, ensure_ascii=False)})
|
||||
|
||||
|
||||
@@ -142,6 +172,7 @@ def get_single_card(page: str = "", tabname: str = "", name: str = ""):
|
||||
|
||||
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/cover-images", fetch_cover_images, methods=["GET"])
|
||||
app.add_api_route("/sd_extra_networks/metadata", get_metadata, methods=["GET"])
|
||||
app.add_api_route("/sd_extra_networks/get-single-card", get_single_card, methods=["GET"])
|
||||
|
||||
@@ -151,6 +182,7 @@ def quote_js(s):
|
||||
s = s.replace('"', '\\"')
|
||||
return f'"{s}"'
|
||||
|
||||
|
||||
class ExtraNetworksPage:
|
||||
def __init__(self, title):
|
||||
self.title = title
|
||||
@@ -164,6 +196,8 @@ class ExtraNetworksPage:
|
||||
self.lister = util.MassFileLister()
|
||||
# HTML Templates
|
||||
self.pane_tpl = shared.html("extra-networks-pane.html")
|
||||
self.pane_content_tree_tpl = shared.html("extra-networks-pane-tree.html")
|
||||
self.pane_content_dirs_tpl = shared.html("extra-networks-pane-dirs.html")
|
||||
self.card_tpl = shared.html("extra-networks-card.html")
|
||||
self.btn_tree_tpl = shared.html("extra-networks-tree-button.html")
|
||||
self.btn_copy_path_tpl = shared.html("extra-networks-copy-path-button.html")
|
||||
@@ -243,14 +277,12 @@ class ExtraNetworksPage:
|
||||
btn_metadata = self.btn_metadata_tpl.format(
|
||||
**{
|
||||
"extra_networks_tabname": self.extra_networks_tabname,
|
||||
"name": html.escape(item["name"]),
|
||||
}
|
||||
)
|
||||
btn_edit_item = self.btn_edit_item_tpl.format(
|
||||
**{
|
||||
"tabname": tabname,
|
||||
"extra_networks_tabname": self.extra_networks_tabname,
|
||||
"name": html.escape(item["name"]),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -476,6 +508,47 @@ class ExtraNetworksPage:
|
||||
|
||||
return f"<ul class='tree-list tree-list--tree'>{res}</ul>"
|
||||
|
||||
def create_dirs_view_html(self, tabname: str) -> str:
|
||||
"""Generates HTML for displaying folders."""
|
||||
|
||||
subdirs = {}
|
||||
for parentdir in [os.path.abspath(x) for x in self.allowed_directories_for_previews()]:
|
||||
for root, dirs, _ in sorted(os.walk(parentdir, followlinks=True), key=lambda x: shared.natural_sort_key(x[0])):
|
||||
for dirname in sorted(dirs, key=shared.natural_sort_key):
|
||||
x = os.path.join(root, dirname)
|
||||
|
||||
if not os.path.isdir(x):
|
||||
continue
|
||||
|
||||
subdir = os.path.abspath(x)[len(parentdir):]
|
||||
|
||||
if shared.opts.extra_networks_dir_button_function:
|
||||
if not subdir.startswith(os.path.sep):
|
||||
subdir = os.path.sep + subdir
|
||||
else:
|
||||
while subdir.startswith(os.path.sep):
|
||||
subdir = subdir[1:]
|
||||
|
||||
is_empty = len(os.listdir(x)) == 0
|
||||
if not is_empty and not subdir.endswith(os.path.sep):
|
||||
subdir = subdir + os.path.sep
|
||||
|
||||
if (os.path.sep + "." in subdir or subdir.startswith(".")) and not shared.opts.extra_networks_show_hidden_directories:
|
||||
continue
|
||||
|
||||
subdirs[subdir] = 1
|
||||
|
||||
if subdirs:
|
||||
subdirs = {"": 1, **subdirs}
|
||||
|
||||
subdirs_html = "".join([f"""
|
||||
<button class='lg secondary gradio-button custom-button{" search-all" if subdir == "" else ""}' onclick='extraNetworksSearchButton("{tabname}", "{self.extra_networks_tabname}", event)'>
|
||||
{html.escape(subdir if subdir != "" else "all")}
|
||||
</button>
|
||||
""" for subdir in subdirs])
|
||||
|
||||
return subdirs_html
|
||||
|
||||
def create_card_view_html(self, tabname: str, *, none_message) -> str:
|
||||
"""Generates HTML for the network Card View section for a tab.
|
||||
|
||||
@@ -489,15 +562,15 @@ class ExtraNetworksPage:
|
||||
Returns:
|
||||
HTML formatted string.
|
||||
"""
|
||||
res = ""
|
||||
res = []
|
||||
for item in self.items.values():
|
||||
res += self.create_item_html(tabname, item, self.card_tpl)
|
||||
res.append(self.create_item_html(tabname, item, self.card_tpl))
|
||||
|
||||
if res == "":
|
||||
if not res:
|
||||
dirs = "".join([f"<li>{x}</li>" for x in self.allowed_directories_for_previews()])
|
||||
res = none_message or shared.html("extra-networks-no-cards.html").format(dirs=dirs)
|
||||
res = [none_message or shared.html("extra-networks-no-cards.html").format(dirs=dirs)]
|
||||
|
||||
return res
|
||||
return "".join(res)
|
||||
|
||||
def create_html(self, tabname, *, empty=False):
|
||||
"""Generates an HTML string for the current pane.
|
||||
@@ -526,35 +599,28 @@ class ExtraNetworksPage:
|
||||
if "user_metadata" not in item:
|
||||
self.read_user_metadata(item)
|
||||
|
||||
data_sortdir = shared.opts.extra_networks_card_order
|
||||
data_sortmode = shared.opts.extra_networks_card_order_field.lower().replace("sort", "").replace(" ", "_").rstrip("_").strip()
|
||||
data_sortkey = f"{data_sortmode}-{data_sortdir}-{len(self.items)}"
|
||||
tree_view_btn_extra_class = ""
|
||||
tree_view_div_extra_class = "hidden"
|
||||
tree_view_div_default_display = "none"
|
||||
extra_network_pane_content_default_display = "flex"
|
||||
if shared.opts.extra_networks_tree_view_default_enabled:
|
||||
tree_view_btn_extra_class = "extra-network-control--enabled"
|
||||
tree_view_div_extra_class = ""
|
||||
tree_view_div_default_display = "block"
|
||||
extra_network_pane_content_default_display = "grid"
|
||||
show_tree = shared.opts.extra_networks_tree_view_default_enabled
|
||||
|
||||
return self.pane_tpl.format(
|
||||
**{
|
||||
"tabname": tabname,
|
||||
"extra_networks_tabname": self.extra_networks_tabname,
|
||||
"data_sortmode": data_sortmode,
|
||||
"data_sortkey": data_sortkey,
|
||||
"data_sortdir": data_sortdir,
|
||||
"tree_view_btn_extra_class": tree_view_btn_extra_class,
|
||||
"tree_view_div_extra_class": tree_view_div_extra_class,
|
||||
"tree_html": self.create_tree_view_html(tabname),
|
||||
"items_html": self.create_card_view_html(tabname, none_message="Loading..." if empty else None),
|
||||
"extra_networks_tree_view_default_width": shared.opts.extra_networks_tree_view_default_width,
|
||||
"tree_view_div_default_display": tree_view_div_default_display,
|
||||
"extra_network_pane_content_default_display": extra_network_pane_content_default_display,
|
||||
}
|
||||
)
|
||||
page_params = {
|
||||
"tabname": tabname,
|
||||
"extra_networks_tabname": self.extra_networks_tabname,
|
||||
"data_sortdir": shared.opts.extra_networks_card_order,
|
||||
"sort_path_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Path' else '',
|
||||
"sort_name_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Name' else '',
|
||||
"sort_date_created_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Date Created' else '',
|
||||
"sort_date_modified_active": ' extra-network-control--enabled' if shared.opts.extra_networks_card_order_field == 'Date Modified' else '',
|
||||
"tree_view_btn_extra_class": "extra-network-control--enabled" if show_tree else "",
|
||||
"items_html": self.create_card_view_html(tabname, none_message="Loading..." if empty else None),
|
||||
"extra_networks_tree_view_default_width": shared.opts.extra_networks_tree_view_default_width,
|
||||
"tree_view_div_default_display_class": "" if show_tree else "extra-network-dirs-hidden",
|
||||
}
|
||||
|
||||
if shared.opts.extra_networks_tree_view_style == "Tree":
|
||||
pane_content = self.pane_content_tree_tpl.format(**page_params, tree_html=self.create_tree_view_html(tabname))
|
||||
else:
|
||||
pane_content = self.pane_content_dirs_tpl.format(**page_params, dirs_html=self.create_dirs_view_html(tabname))
|
||||
|
||||
return self.pane_tpl.format(**page_params, pane_content=pane_content)
|
||||
|
||||
def create_item(self, name, index=None):
|
||||
raise NotImplementedError()
|
||||
@@ -591,6 +657,17 @@ class ExtraNetworksPage:
|
||||
|
||||
return None
|
||||
|
||||
def find_embedded_preview(self, path, name, metadata):
|
||||
"""
|
||||
Find if embedded preview exists in safetensors metadata and return endpoint for it.
|
||||
"""
|
||||
|
||||
file = f"{path}.safetensors"
|
||||
if self.lister.exists(file) and 'ssmd_cover_images' in metadata and len(list(filter(None, json.loads(metadata['ssmd_cover_images'])))) > 0:
|
||||
return f"./sd_extra_networks/cover-images?page={self.extra_networks_tabname}&item={name}"
|
||||
|
||||
return None
|
||||
|
||||
def find_description(self, path):
|
||||
"""
|
||||
Find and read a description file for a given path (without extension).
|
||||
|
||||
Reference in New Issue
Block a user