diff --git a/javascript/extensions.js b/javascript/extensions.js index 86f5336d..59179ca6 100644 --- a/javascript/extensions.js +++ b/javascript/extensions.js @@ -21,4 +21,15 @@ function extensions_check(){ }) return [] -} \ No newline at end of file +} + +function install_extension_from_index(button, url){ + button.disabled = "disabled" + button.value = "Installing..." + + textarea = gradioApp().querySelector('#extension_to_install textarea') + textarea.value = url + textarea.dispatchEvent(new Event("input", { bubbles: true })) + + gradioApp().querySelector('#install_extension_button').click() +} diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index e74b7d68..ab807722 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -13,6 +13,9 @@ import html from modules import extensions, shared, paths +available_extensions = {"extensions": []} + + def check_access(): assert not shared.cmd_opts.disable_extension_access, "extension access disabed because of commandline flags" @@ -96,6 +99,14 @@ def extension_table(): return code +def normalize_git_url(url): + if url is None: + return "" + + url = url.replace(".git", "") + return url + + def install_extension_from_url(dirname, url): check_access() @@ -103,14 +114,15 @@ def install_extension_from_url(dirname, url): if dirname is None or dirname == "": *parts, last_part = url.split('/') - last_part = last_part.replace(".git", "") + last_part = normalize_git_url(last_part) dirname = last_part target_dir = os.path.join(extensions.extensions_dir, dirname) assert not os.path.exists(target_dir), f'Extension directory already exists: {target_dir}' - assert len([x for x in extensions.extensions if x.remote == url]) == 0, 'Extension with this URL is already installed' + normalized_url = normalize_git_url(url) + assert len([x for x in extensions.extensions if normalize_git_url(x.remote) == normalized_url]) == 0, 'Extension with this URL is already installed' tmpdir = os.path.join(paths.script_path, "tmp", dirname) @@ -128,18 +140,80 @@ def install_extension_from_url(dirname, url): shutil.rmtree(tmpdir, True) +def install_extension_from_index(url): + ext_table, message = install_extension_from_url(None, url) + + return refresh_available_extensions_from_data(), ext_table, message + + +def refresh_available_extensions(url): + global available_extensions + + import urllib.request + with urllib.request.urlopen(url) as response: + text = response.read() + + available_extensions = json.loads(text) + + return url, refresh_available_extensions_from_data(), '' + + +def refresh_available_extensions_from_data(): + extlist = available_extensions["extensions"] + installed_extension_urls = {normalize_git_url(extension.remote): extension.name for extension in extensions.extensions} + + code = f""" + + + + + + + + + + """ + + for ext in extlist: + name = ext.get("name", "noname") + url = ext.get("url", None) + description = ext.get("description", "") + + if url is None: + continue + + existing = installed_extension_urls.get(normalize_git_url(url), None) + + install_code = f"""""" + + code += f""" + + + + + + """ + + code += """ + +
ExtensionDescriptionAction
{html.escape(name)}{html.escape(description)}{install_code}
+ """ + + return code + + def create_ui(): import modules.ui with gr.Blocks(analytics_enabled=False) as ui: with gr.Tabs(elem_id="tabs_extensions") as tabs: with gr.TabItem("Installed"): - extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False) - extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False) with gr.Row(): apply = gr.Button(value="Apply and restart UI", variant="primary") check = gr.Button(value="Check for updates") + extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False).style(container=False) + extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False).style(container=False) extensions_table = gr.HTML(lambda: extension_table()) @@ -157,16 +231,38 @@ def create_ui(): outputs=[extensions_table], ) + with gr.TabItem("Available"): + with gr.Row(): + refresh_available_extensions_button = gr.Button(value="Load from:", variant="primary") + available_extensions_index = gr.Text(value="https://raw.githubusercontent.com/wiki/AUTOMATIC1111/stable-diffusion-webui/Extensions-index.md", label="Extension index URL").style(container=False) + extension_to_install = gr.Text(elem_id="extension_to_install", visible=False) + install_extension_button = gr.Button(elem_id="install_extension_button", visible=False) + + install_result = gr.HTML() + available_extensions_table = gr.HTML() + + refresh_available_extensions_button.click( + fn=modules.ui.wrap_gradio_call(refresh_available_extensions, extra_outputs=[gr.update(), gr.update()]), + inputs=[available_extensions_index], + outputs=[available_extensions_index, available_extensions_table, install_result], + ) + + install_extension_button.click( + fn=modules.ui.wrap_gradio_call(install_extension_from_index, extra_outputs=[gr.update(), gr.update()]), + inputs=[extension_to_install], + outputs=[available_extensions_table, extensions_table, install_result], + ) + with gr.TabItem("Install from URL"): install_url = gr.Text(label="URL for extension's git repository") install_dirname = gr.Text(label="Local directory name", placeholder="Leave empty for auto") - intall_button = gr.Button(value="Install", variant="primary") - intall_result = gr.HTML(elem_id="extension_install_result") + install_button = gr.Button(value="Install", variant="primary") + install_result = gr.HTML(elem_id="extension_install_result") - intall_button.click( + install_button.click( fn=modules.ui.wrap_gradio_call(install_extension_from_url, extra_outputs=[gr.update()]), inputs=[install_dirname, install_url], - outputs=[extensions_table, intall_result], + outputs=[extensions_table, install_result], ) return ui diff --git a/style.css b/style.css index 859c3933..dfef0dc5 100644 --- a/style.css +++ b/style.css @@ -532,16 +532,16 @@ img2maskimg, #img2maskimg > .h-60, #img2maskimg > .h-60 > div, #img2maskimg > .h /* Extensions */ -#extensions{ +#tab_extensions table{ border-collapse: collapse; } -#extensions td, #extensions th{ +#tab_extensions table td, #tab_extensions table th{ border: 1px solid #ccc; padding: 0.25em 0.5em; } -#extensions input[type="checkbox"]{ +#tab_extensions table input[type="checkbox"]{ margin-right: 0.5em; } @@ -549,6 +549,9 @@ img2maskimg, #img2maskimg > .h-60, #img2maskimg > .h-60 > div, #img2maskimg > .h max-width: 16em; } +#tab_extensions input[disabled="disabled"]{ + opacity: 0.5; +} /* The following handles localization for right-to-left (RTL) languages like Arabic. The rtl media type will only be activated by the logic in javascript/localization.js.