Skip to content
代码片段 群组 项目
ui_extra_networks.py 7.5 KB
更新 更旧
  • 了解如何忽略特定修订
  • AUTOMATIC's avatar
    AUTOMATIC 已提交
    import os.path
    
    import urllib.parse
    from pathlib import Path
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
    from modules import shared
    import gradio as gr
    import json
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
    from modules.generation_parameters_copypaste import image_from_url_text
    
    extra_pages = []
    
    allowed_dirs = set()
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
    
    def register_page(page):
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
        """registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions"""
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
        extra_pages.append(page)
    
        allowed_dirs.clear()
        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
    
            if not any([Path(x).resolve() in Path(filename).resolve().parents for x in allowed_dirs]):
                raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.")
    
            if os.path.splitext(filename)[1].lower() != ".png":
                raise ValueError(f"File cannot be fetched: {filename}. Only png.")
    
            # would profit from returning 304
            return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
    
        app.add_api_route("/sd_extra_networks/thumb", fetch_file, methods=["GET"])
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
    
    class ExtraNetworksPage:
        def __init__(self, title):
            self.title = title
    
            self.name = title.lower()
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
            self.card_page = shared.html("extra-networks-card.html")
            self.allow_negative_prompt = False
    
        def refresh(self):
            pass
    
    
        def link_preview(self, filename):
            return "./sd_extra_networks/thumb?filename=" + urllib.parse.quote(filename.replace('\\', '/')) + "&mtime=" + str(os.path.getmtime(filename))
    
    
        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 create_html(self, tabname):
            view = shared.opts.extra_networks_default_view
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
            items_html = ''
    
            for item in self.list_items():
                items_html += self.create_html_for_item(item, tabname)
    
            if items_html == '':
                dirs = "".join([f"<li>{x}</li>" for x in self.allowed_directories_for_previews()])
                items_html = shared.html("extra-networks-no-cards.html").format(dirs=dirs)
    
    
            res = f"""
    
    <div id='{tabname}_{self.name}_cards' class='extra-network-{view}'>
    
    {items_html}
    </div>
    """
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
            return res
    
        def list_items(self):
            raise NotImplementedError()
    
        def allowed_directories_for_previews(self):
            return []
    
        def create_html_for_item(self, item, tabname):
            preview = item.get("preview", None)
    
    
            onclick = item.get("onclick", None)
            if onclick is None:
                onclick = '"' + html.escape(f"""return cardClicked({json.dumps(tabname)}, {item["prompt"]}, {"true" if self.allow_negative_prompt else "false"})""") + '"'
    
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
            args = {
    
                "preview_html": "style='background-image: url(\"" + html.escape(preview) + "\")'" if preview else '',
    
                "prompt": item.get("prompt", None),
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
                "tabname": json.dumps(tabname),
                "local_preview": json.dumps(item["local_preview"]),
                "name": item["name"],
    
                "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", ""),
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
            }
    
            return self.card_page.format(**args)
    
    
    def intialize():
        extra_pages.clear()
    
    
    class ExtraNetworksUi:
        def __init__(self):
            self.pages = None
            self.stored_extra_pages = None
    
            self.button_save_preview = None
            self.preview_target_filename = None
    
            self.tabname = None
    
    
    
    def pages_in_preferred_order(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])
    
    
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    def create_ui(container, button, tabname):
        ui = ExtraNetworksUi()
        ui.pages = []
    
        ui.stored_extra_pages = pages_in_preferred_order(extra_pages.copy())
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
        ui.tabname = tabname
    
        with gr.Tabs(elem_id=tabname+"_extra_tabs") as tabs:
    
            for page in ui.stored_extra_pages:
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
                with gr.Tab(page.title):
                    page_elem = gr.HTML(page.create_html(ui.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', elem_id=tabname+"_extra_refresh")
        button_close = gr.Button('Close', elem_id=tabname+"_extra_close")
    
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
        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)
    
    
        def toggle_visibility(is_visible):
            is_visible = not is_visible
            return is_visible, gr.update(visible=is_visible)
    
        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])
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
            res = []
    
            for pg in ui.stored_extra_pages:
                pg.refresh()
    
                res.append(pg.create_html(ui.tabname))
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
            return res
    
    
        button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages)
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
        return ui
    
    
    def path_is_parent(parent_path, child_path):
        parent_path = os.path.abspath(parent_path)
        child_path = os.path.abspath(child_path)
    
    
        return child_path.startswith(parent_path)
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
    
    def setup_ui(ui, gallery):
    
        def save_preview(index, images, filename):
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
            if len(images) == 0:
                print("There is no image in gallery to save as a preview.")
                return [page.create_html(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
    
            img_info = images[index if index >= 0 else 0]
            image = image_from_url_text(img_info)
    
            is_allowed = False
            for extra_page in ui.stored_extra_pages:
                if any([path_is_parent(x, filename) for x in extra_page.allowed_directories_for_previews()]):
                    is_allowed = True
                    break
    
            assert is_allowed, f'writing to {filename} is not allowed'
    
            image.save(filename)
    
    
            return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
    
        ui.button_save_preview.click(
            fn=save_preview,
    
            _js="function(x, y, z){return [selected_gallery_index(), y, z]}",
    
            inputs=[ui.preview_target_filename, gallery, ui.preview_target_filename],
    
    AUTOMATIC's avatar
    AUTOMATIC 已提交
            outputs=[*ui.pages]
        )