2022-10-31 22:36:45 +08:00
import json
2023-07-15 17:09:51 +08:00
import os
2023-05-16 01:57:11 +08:00
import threading
2022-10-31 22:36:45 +08:00
import time
2023-08-30 15:18:31 +08:00
from datetime import datetime , timezone
2022-10-31 22:36:45 +08:00
import git
import gradio as gr
import html
2022-11-14 02:39:41 +08:00
import shutil
import errno
2022-10-31 22:36:45 +08:00
2023-06-06 01:04:28 +08:00
from modules import extensions , shared , paths , config_states , errors , restart
2023-03-30 05:46:03 +08:00
from modules . paths_internal import config_states_dir
2023-01-28 20:57:56 +08:00
from modules . call_queue import wrap_gradio_gpu_call
2022-10-31 22:36:45 +08:00
2022-11-01 14:59:00 +08:00
available_extensions = { " extensions " : [ ] }
2023-03-30 07:32:54 +08:00
STYLE_PRIMARY = ' style= " color: var(--primary-400) " '
2022-11-01 14:59:00 +08:00
2022-10-31 23:33:44 +08:00
def check_access ( ) :
2022-12-02 18:53:26 +08:00
assert not shared . cmd_opts . disable_extension_access , " extension access disabled because of command line flags "
2022-10-31 23:33:44 +08:00
2023-03-28 00:44:49 +08:00
def apply_and_restart ( disable_list , update_list , disable_all ) :
2022-10-31 23:33:44 +08:00
check_access ( )
2022-10-31 22:36:45 +08:00
disabled = json . loads ( disable_list )
assert type ( disabled ) == list , f " wrong disable_list data for apply_and_restart: { disable_list } "
update = json . loads ( update_list )
assert type ( update ) == list , f " wrong update_list data for apply_and_restart: { update_list } "
2023-03-30 05:46:03 +08:00
if update :
save_config_state ( " Backup (pre-update) " )
2022-10-31 22:36:45 +08:00
update = set ( update )
for ext in extensions . extensions :
if ext . name not in update :
continue
try :
2022-11-13 02:44:42 +08:00
ext . fetch_and_reset_hard ( )
2022-10-31 22:36:45 +08:00
except Exception :
2023-06-01 00:56:37 +08:00
errors . report ( f " Error getting updates for { ext . name } " , exc_info = True )
2022-10-31 22:36:45 +08:00
shared . opts . disabled_extensions = disabled
2023-03-28 00:44:49 +08:00
shared . opts . disable_all_extensions = disable_all
2022-10-31 22:36:45 +08:00
shared . opts . save ( shared . config_filename )
2023-06-06 01:04:28 +08:00
if restart . is_restartable ( ) :
restart . restart_program ( )
else :
restart . stop_program ( )
2022-10-31 22:36:45 +08:00
2023-03-30 05:46:03 +08:00
def save_config_state ( name ) :
current_config_state = config_states . get_config ( )
if not name :
name = " Config "
current_config_state [ " name " ] = name
2023-05-10 03:17:58 +08:00
timestamp = datetime . now ( ) . strftime ( ' % Y_ % m_ %d - % H_ % M_ % S ' )
filename = os . path . join ( config_states_dir , f " { timestamp } _ { name } .json " )
2023-03-30 05:46:03 +08:00
print ( f " Saving backup of webui/extension state to { filename } . " )
with open ( filename , " w " , encoding = " utf-8 " ) as f :
2023-11-26 20:55:50 +08:00
json . dump ( current_config_state , f , indent = 4 , ensure_ascii = False )
2023-03-30 05:46:03 +08:00
config_states . list_config_states ( )
new_value = next ( iter ( config_states . all_config_states . keys ( ) ) , " Current " )
new_choices = [ " Current " ] + list ( config_states . all_config_states . keys ( ) )
2023-03-30 07:32:54 +08:00
return gr . Dropdown . update ( value = new_value , choices = new_choices ) , f " <span>Saved current webui/extension state to \" { filename } \" </span> "
2023-03-30 05:46:03 +08:00
def restore_config_state ( confirmed , config_state_name , restore_type ) :
if config_state_name == " Current " :
return " <span>Select a config to restore from.</span> "
if not confirmed :
return " <span>Cancelled.</span> "
check_access ( )
config_state = config_states . all_config_states [ config_state_name ]
2023-03-30 07:32:54 +08:00
print ( f " *** Restoring webui state from backup: { restore_type } *** " )
2023-03-30 05:46:03 +08:00
if restore_type == " extensions " or restore_type == " both " :
2023-03-30 07:32:54 +08:00
shared . opts . restore_config_state_file = config_state [ " filepath " ]
2023-03-30 05:46:03 +08:00
shared . opts . save ( shared . config_filename )
if restore_type == " webui " or restore_type == " both " :
config_states . restore_webui_config ( config_state )
2023-05-12 04:46:45 +08:00
shared . state . request_restart ( )
2023-03-30 05:46:03 +08:00
return " "
2023-01-28 20:57:56 +08:00
def check_updates ( id_task , disable_list ) :
2022-10-31 23:33:44 +08:00
check_access ( )
2023-01-28 20:57:56 +08:00
disabled = json . loads ( disable_list )
assert type ( disabled ) == list , f " wrong disable_list data for apply_and_restart: { disable_list } "
exts = [ ext for ext in extensions . extensions if ext . remote is not None and ext . name not in disabled ]
shared . state . job_count = len ( exts )
for ext in exts :
shared . state . textinfo = ext . name
2022-10-31 22:36:45 +08:00
try :
ext . check_updates ( )
2023-03-27 15:02:30 +08:00
except FileNotFoundError as e :
if ' FETCH_HEAD ' not in str ( e ) :
raise
2022-10-31 22:36:45 +08:00
except Exception :
2023-06-01 00:56:37 +08:00
errors . report ( f " Error checking updates for { ext . name } " , exc_info = True )
2022-10-31 22:36:45 +08:00
2023-01-28 20:57:56 +08:00
shared . state . nextjob ( )
return extension_table ( ) , " "
2022-10-31 22:36:45 +08:00
2023-03-30 08:21:57 +08:00
def make_commit_link ( commit_hash , remote , text = None ) :
if text is None :
text = commit_hash [ : 8 ]
if remote . startswith ( " https://github.com/ " ) :
2023-05-19 13:14:38 +08:00
if remote . endswith ( " .git " ) :
remote = remote [ : - 4 ]
href = remote + " /commit/ " + commit_hash
2023-03-30 08:21:57 +08:00
return f ' <a href= " { href } " target= " _blank " > { text } </a> '
else :
return text
2022-10-31 22:36:45 +08:00
def extension_table ( ) :
code = f """ <!-- { time . time ( ) } -->
< table id = " extensions " >
< thead >
< tr >
2023-06-25 11:30:08 +08:00
< th >
< input class = " gr-check-radio gr-checkbox all_extensions_toggle " type = " checkbox " { ' checked= " checked " ' if all ( ext . enabled for ext in extensions . extensions ) else ' ' } onchange = " toggle_all_extensions(event) " / >
< abbr title = " Use checkbox to enable the extension; it will be enabled or disabled when you click apply button " > Extension < / abbr >
< / th >
2022-10-31 22:36:45 +08:00
< th > URL < / th >
2023-05-16 02:22:35 +08:00
< th > Branch < / th >
< th > Version < / th >
< th > Date < / th >
2022-10-31 22:36:45 +08:00
< th > < abbr title = " Use checkbox to mark the extension for update; it will be updated when you click apply button " > Update < / abbr > < / th >
< / tr >
< / thead >
< tbody >
"""
for ext in extensions . extensions :
2023-05-16 02:22:35 +08:00
ext : extensions . Extension
2023-03-27 15:02:30 +08:00
ext . read_info_from_repo ( )
2023-02-14 00:04:34 +08:00
remote = f """ <a href= " { html . escape ( ext . remote or ' ' ) } " target= " _blank " > { html . escape ( " built-in " if ext . is_builtin else ext . remote or ' ' ) } </a> """
2022-12-03 23:06:33 +08:00
2022-10-31 22:36:45 +08:00
if ext . can_update :
ext_status = f """ <label><input class= " gr-check-radio gr-checkbox " name= " update_ { html . escape ( ext . name ) } " checked= " checked " type= " checkbox " > { html . escape ( ext . status ) } </label> """
else :
ext_status = ext . status
2023-03-28 00:44:49 +08:00
style = " "
2023-07-29 00:07:35 +08:00
if shared . cmd_opts . disable_extra_extensions and not ext . is_builtin or shared . opts . disable_all_extensions == " extra " and not ext . is_builtin or shared . cmd_opts . disable_all_extensions or shared . opts . disable_all_extensions == " all " :
2023-03-30 07:32:54 +08:00
style = STYLE_PRIMARY
2023-03-28 00:44:49 +08:00
2023-03-30 08:21:57 +08:00
version_link = ext . version
if ext . commit_hash and ext . remote :
version_link = make_commit_link ( ext . commit_hash , ext . remote , ext . version )
2023-03-28 00:44:49 +08:00
2022-10-31 22:36:45 +08:00
code + = f """
< tr >
2023-06-25 11:30:08 +08:00
< td > < label { style } > < input class = " gr-check-radio gr-checkbox extension_toggle " name = " enable_ { html.escape(ext.name)} " type = " checkbox " { ' checked= " checked " ' if ext . enabled else ' ' } onchange = " toggle_extension(event) " / > { html . escape ( ext . name ) } < / label > < / td >
2022-12-03 23:06:33 +08:00
< td > { remote } < / td >
2023-05-16 02:22:35 +08:00
< td > { ext . branch } < / td >
2023-03-30 08:21:57 +08:00
< td > { version_link } < / td >
2023-08-30 14:28:46 +08:00
< td > { datetime . fromtimestamp ( ext . commit_date ) if ext . commit_date else " " } < / td >
2022-10-31 22:36:45 +08:00
< td { ' class= " extension_status " ' if ext . remote is not None else ' ' } > { ext_status } < / td >
< / tr >
"""
code + = """
< / tbody >
< / table >
"""
return code
2023-03-30 05:46:03 +08:00
def update_config_states_table ( state_name ) :
if state_name == " Current " :
config_state = config_states . get_config ( )
else :
config_state = config_states . all_config_states [ state_name ]
config_name = config_state . get ( " name " , " Config " )
2023-09-16 08:05:42 +08:00
created_date = datetime . fromtimestamp ( config_state [ " created_at " ] ) . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2023-03-30 07:55:57 +08:00
filepath = config_state . get ( " filepath " , " <unknown> " )
2023-03-30 05:46:03 +08:00
2023-08-20 16:34:20 +08:00
try :
webui_remote = config_state [ " webui " ] [ " remote " ] or " "
webui_branch = config_state [ " webui " ] [ " branch " ]
webui_commit_hash = config_state [ " webui " ] [ " commit_hash " ] or " <unknown> "
webui_commit_date = config_state [ " webui " ] [ " commit_date " ]
if webui_commit_date :
webui_commit_date = time . asctime ( time . gmtime ( webui_commit_date ) )
2023-03-30 05:46:03 +08:00
else :
2023-08-20 16:34:20 +08:00
webui_commit_date = " <unknown> "
2023-03-30 05:46:03 +08:00
2023-08-20 16:34:20 +08:00
remote = f """ <a href= " { html . escape ( webui_remote ) } " target= " _blank " > { html . escape ( webui_remote or ' ' ) } </a> """
commit_link = make_commit_link ( webui_commit_hash , webui_remote )
date_link = make_commit_link ( webui_commit_hash , webui_remote , webui_commit_date )
current_webui = config_states . get_webui_config ( )
2023-03-30 05:46:03 +08:00
style_remote = " "
style_branch = " "
style_commit = " "
2023-08-20 16:34:20 +08:00
if current_webui [ " remote " ] != webui_remote :
style_remote = STYLE_PRIMARY
if current_webui [ " branch " ] != webui_branch :
style_branch = STYLE_PRIMARY
if current_webui [ " commit_hash " ] != webui_commit_hash :
style_commit = STYLE_PRIMARY
code = f """ <!-- { time . time ( ) } -->
< h2 > Config Backup : { config_name } < / h2 >
< div > < b > Filepath : < / b > { filepath } < / div >
< div > < b > Created at : < / b > { created_date } < / div >
< h2 > WebUI State < / h2 >
< table id = " config_state_webui " >
< thead >
< tr >
< th > URL < / th >
< th > Branch < / th >
< th > Commit < / th >
< th > Date < / th >
< / tr >
< / thead >
< tbody >
< tr >
< td >
< label { style_remote } > { remote } < / label >
< / td >
< td >
< label { style_branch } > { webui_branch } < / label >
< / td >
< td >
< label { style_commit } > { commit_link } < / label >
< / td >
< td >
< label { style_commit } > { date_link } < / label >
< / td >
< / tr >
< / tbody >
< / table >
< h2 > Extension State < / h2 >
< table id = " config_state_extensions " >
< thead >
< tr >
< th > Extension < / th >
< th > URL < / th >
< th > Branch < / th >
< th > Commit < / th >
< th > Date < / th >
< / tr >
< / thead >
< tbody >
"""
ext_map = { ext . name : ext for ext in extensions . extensions }
for ext_name , ext_conf in config_state [ " extensions " ] . items ( ) :
ext_remote = ext_conf [ " remote " ] or " "
ext_branch = ext_conf [ " branch " ] or " <unknown> "
ext_enabled = ext_conf [ " enabled " ]
ext_commit_hash = ext_conf [ " commit_hash " ] or " <unknown> "
ext_commit_date = ext_conf [ " commit_date " ]
if ext_commit_date :
ext_commit_date = time . asctime ( time . gmtime ( ext_commit_date ) )
else :
ext_commit_date = " <unknown> "
remote = f """ <a href= " { html . escape ( ext_remote ) } " target= " _blank " > { html . escape ( ext_remote or ' ' ) } </a> """
commit_link = make_commit_link ( ext_commit_hash , ext_remote )
date_link = make_commit_link ( ext_commit_hash , ext_remote , ext_commit_date )
style_enabled = " "
style_remote = " "
style_branch = " "
style_commit = " "
if ext_name in ext_map :
current_ext = ext_map [ ext_name ]
current_ext . read_info_from_repo ( )
if current_ext . enabled != ext_enabled :
style_enabled = STYLE_PRIMARY
if current_ext . remote != ext_remote :
style_remote = STYLE_PRIMARY
if current_ext . branch != ext_branch :
style_branch = STYLE_PRIMARY
if current_ext . commit_hash != ext_commit_hash :
style_commit = STYLE_PRIMARY
code + = f """ <tr>
< td > < label { style_enabled } > < input class = " gr-check-radio gr-checkbox " type = " checkbox " disabled = " true " { ' checked= " checked " ' if ext_enabled else ' ' } > { html . escape ( ext_name ) } < / label > < / td >
< td > < label { style_remote } > { remote } < / label > < / td >
< td > < label { style_branch } > { ext_branch } < / label > < / td >
< td > < label { style_commit } > { commit_link } < / label > < / td >
< td > < label { style_commit } > { date_link } < / label > < / td >
< / tr >
"""
code + = """ </tbody>
< / table > """
except Exception as e :
print ( f " [ERROR]: Config states { filepath } , { e } " )
code = f """ <!-- { time . time ( ) } -->
< h2 > Config Backup : { config_name } < / h2 >
< div > < b > Filepath : < / b > { filepath } < / div >
< div > < b > Created at : < / b > { created_date } < / div >
< h2 > This file is corrupted < / h2 > """
2023-03-30 05:46:03 +08:00
return code
2022-11-01 14:59:00 +08:00
def normalize_git_url ( url ) :
if url is None :
return " "
url = url . replace ( " .git " , " " )
return url
2023-11-23 12:40:24 +08:00
def get_extension_dirname_from_url ( url ) :
* parts , last_part = url . split ( ' / ' )
return normalize_git_url ( last_part )
2023-07-08 22:52:03 +08:00
def install_extension_from_url ( dirname , url , branch_name = None ) :
2022-10-31 23:33:44 +08:00
check_access ( )
2023-06-25 01:31:02 +08:00
if isinstance ( dirname , str ) :
dirname = dirname . strip ( )
if isinstance ( url , str ) :
url = url . strip ( )
2022-10-31 22:36:45 +08:00
assert url , ' No URL specified '
if dirname is None or dirname == " " :
2023-11-23 12:40:24 +08:00
dirname = get_extension_dirname_from_url ( url )
2022-10-31 22:36:45 +08:00
target_dir = os . path . join ( extensions . extensions_dir , dirname )
assert not os . path . exists ( target_dir ) , f ' Extension directory already exists: { target_dir } '
2022-11-01 14:59:00 +08:00
normalized_url = normalize_git_url ( url )
2023-06-02 19:58:10 +08:00
if any ( x for x in extensions . extensions if normalize_git_url ( x . remote ) == normalized_url ) :
raise Exception ( f ' Extension with this URL is already installed: { url } ' )
2022-10-31 22:36:45 +08:00
2023-01-26 00:15:42 +08:00
tmpdir = os . path . join ( paths . data_path , " tmp " , dirname )
2022-10-31 22:36:45 +08:00
try :
shutil . rmtree ( tmpdir , True )
2023-04-29 18:45:05 +08:00
if not branch_name :
2023-04-10 09:38:26 +08:00
# if no branch is specified, use the default branch
2023-07-09 22:40:23 +08:00
with git . Repo . clone_from ( url , tmpdir , filter = [ ' blob:none ' ] ) as repo :
2023-04-10 09:38:26 +08:00
repo . remote ( ) . fetch ( )
for submodule in repo . submodules :
submodule . update ( )
else :
2023-07-09 22:40:23 +08:00
with git . Repo . clone_from ( url , tmpdir , filter = [ ' blob:none ' ] , branch = branch_name ) as repo :
2023-04-10 09:38:26 +08:00
repo . remote ( ) . fetch ( )
for submodule in repo . submodules :
submodule . update ( )
2022-11-14 02:39:41 +08:00
try :
os . rename ( tmpdir , target_dir )
except OSError as err :
if err . errno == errno . EXDEV :
# Cross device link, typical in docker or when tmp/ and extensions/ are on different file systems
# Since we can't use a rename, do the slower but more versitile shutil.move()
shutil . move ( tmpdir , target_dir )
else :
# Something else, not enough free space, permissions, etc. rethrow it so that it gets handled.
2023-03-16 11:35:48 +08:00
raise err
2022-10-31 22:36:45 +08:00
2022-11-12 16:11:47 +08:00
import launch
launch . run_extension_installer ( target_dir )
2022-10-31 22:36:45 +08:00
extensions . list_extensions ( )
return [ extension_table ( ) , html . escape ( f " Installed into { target_dir } . Use Installed tab to restart. " ) ]
finally :
shutil . rmtree ( tmpdir , True )
2023-03-23 23:43:00 +08:00
def install_extension_from_index ( url , hide_tags , sort_column , filter_text ) :
2022-11-01 14:59:00 +08:00
ext_table , message = install_extension_from_url ( None , url )
2023-03-23 23:43:00 +08:00
code , _ = refresh_available_extensions_from_data ( hide_tags , sort_column , filter_text )
2022-11-01 14:59:00 +08:00
2023-03-23 23:43:00 +08:00
return code , ext_table , message , ' '
2022-11-01 14:59:00 +08:00
2022-11-06 15:12:53 +08:00
2023-01-06 17:32:44 +08:00
def refresh_available_extensions ( url , hide_tags , sort_column ) :
2022-11-01 14:59:00 +08:00
global available_extensions
import urllib . request
with urllib . request . urlopen ( url ) as response :
text = response . read ( )
available_extensions = json . loads ( text )
2023-01-06 17:32:44 +08:00
code , tags = refresh_available_extensions_from_data ( hide_tags , sort_column )
2022-11-06 15:12:53 +08:00
2023-03-23 23:43:00 +08:00
return url , code , gr . CheckboxGroup . update ( choices = tags ) , ' ' , ' '
2022-11-06 15:12:53 +08:00
2023-03-23 23:43:00 +08:00
def refresh_available_extensions_for_tags ( hide_tags , sort_column , filter_text ) :
code , _ = refresh_available_extensions_from_data ( hide_tags , sort_column , filter_text )
return code , ' '
def search_extensions ( filter_text , hide_tags , sort_column ) :
code , _ = refresh_available_extensions_from_data ( hide_tags , sort_column , filter_text )
2022-11-01 14:59:00 +08:00
2022-11-06 15:12:53 +08:00
return code , ' '
2022-11-01 14:59:00 +08:00
2022-11-06 15:12:53 +08:00
2023-01-06 17:32:44 +08:00
sort_ordering = [
# (reverse, order_by_function)
( True , lambda x : x . get ( ' added ' , ' z ' ) ) ,
( False , lambda x : x . get ( ' added ' , ' z ' ) ) ,
( False , lambda x : x . get ( ' name ' , ' z ' ) ) ,
( True , lambda x : x . get ( ' name ' , ' z ' ) ) ,
( False , lambda x : ' z ' ) ,
2023-06-29 21:46:59 +08:00
( True , lambda x : x . get ( ' commit_time ' , ' ' ) ) ,
( True , lambda x : x . get ( ' created_at ' , ' ' ) ) ,
2023-06-29 18:25:18 +08:00
( True , lambda x : x . get ( ' stars ' , 0 ) ) ,
2023-01-06 17:32:44 +08:00
]
2023-06-29 21:46:59 +08:00
def get_date ( info : dict , key ) :
try :
2023-08-30 15:18:31 +08:00
return datetime . strptime ( info . get ( key ) , " % Y- % m- %d T % H: % M: % SZ " ) . replace ( tzinfo = timezone . utc ) . astimezone ( ) . strftime ( " % Y- % m- %d " )
2023-06-29 21:46:59 +08:00
except ( ValueError , TypeError ) :
return ' '
2023-03-23 23:43:00 +08:00
def refresh_available_extensions_from_data ( hide_tags , sort_column , filter_text = " " ) :
2022-11-01 14:59:00 +08:00
extlist = available_extensions [ " extensions " ]
2023-11-23 12:40:24 +08:00
installed_extensions = { extension . name for extension in extensions . extensions }
2023-11-27 00:04:39 +08:00
installed_extension_urls = { normalize_git_url ( extension . remote ) for extension in extensions . extensions if extension . remote is not None }
2022-11-01 14:59:00 +08:00
2022-11-06 15:12:53 +08:00
tags = available_extensions . get ( " tags " , { } )
tags_to_hide = set ( hide_tags )
hidden = 0
2022-11-01 14:59:00 +08:00
code = f """ <!-- { time . time ( ) } -->
< table id = " available_extensions " >
< thead >
< tr >
< th > Extension < / th >
< th > Description < / th >
< th > Action < / th >
< / tr >
< / thead >
< tbody >
"""
2023-01-06 17:32:44 +08:00
sort_reverse , sort_function = sort_ordering [ sort_column if 0 < = sort_column < len ( sort_ordering ) else 0 ]
for ext in sorted ( extlist , key = sort_function , reverse = sort_reverse ) :
2022-11-01 14:59:00 +08:00
name = ext . get ( " name " , " noname " )
2023-06-29 18:25:18 +08:00
stars = int ( ext . get ( " stars " , 0 ) )
2023-01-06 17:32:44 +08:00
added = ext . get ( ' added ' , ' unknown ' )
2023-06-29 21:46:59 +08:00
update_time = get_date ( ext , ' commit_time ' )
create_time = get_date ( ext , ' created_at ' )
2022-11-01 14:59:00 +08:00
url = ext . get ( " url " , None )
description = ext . get ( " description " , " " )
2022-11-06 15:12:53 +08:00
extension_tags = ext . get ( " tags " , [ ] )
2022-11-01 14:59:00 +08:00
if url is None :
continue
2023-11-27 00:04:39 +08:00
existing = get_extension_dirname_from_url ( url ) in installed_extensions or normalize_git_url ( url ) in installed_extension_urls
2022-12-10 20:05:22 +08:00
extension_tags = extension_tags + [ " installed " ] if existing else extension_tags
2023-06-02 19:58:10 +08:00
if any ( x for x in extension_tags if x in tags_to_hide ) :
2022-11-06 15:12:53 +08:00
hidden + = 1
continue
2023-03-23 23:43:00 +08:00
if filter_text and filter_text . strip ( ) :
if filter_text . lower ( ) not in html . escape ( name ) . lower ( ) and filter_text . lower ( ) not in html . escape ( description ) . lower ( ) :
hidden + = 1
continue
2023-03-21 13:49:08 +08:00
install_code = f """ <button onclick= " install_extension_from_index(this, ' { html . escape ( url ) } ' ) " { " disabled=disabled " if existing else " " } class= " lg secondary gradio-button custom-button " > { " Install " if not existing else " Installed " } </button> """
2022-11-01 14:59:00 +08:00
2022-11-06 15:12:53 +08:00
tags_text = " , " . join ( [ f " <span class= ' extension-tag ' title= ' { tags . get ( x , ' ' ) } ' > { x } </span> " for x in extension_tags ] )
2022-11-01 14:59:00 +08:00
code + = f """
< tr >
2022-11-06 15:12:53 +08:00
< td > < a href = " { html.escape(url)} " target = " _blank " > { html . escape ( name ) } < / a > < br / > { tags_text } < / td >
2023-06-29 21:46:59 +08:00
< td > { html . escape ( description ) } < p class = " info " >
< span class = " date_added " > Update : { html . escape ( update_time ) } Added : { html . escape ( added ) } Created : { html . escape ( create_time ) } < / span > < span class = " star_count " > stars : < b > { stars } < / b > < / a > < / p > < / td >
2022-11-01 14:59:00 +08:00
< td > { install_code } < / td >
< / tr >
2023-05-11 23:28:15 +08:00
2022-12-10 20:05:22 +08:00
"""
for tag in [ x for x in extension_tags if x not in tags ] :
tags [ tag ] = tag
2022-11-01 14:59:00 +08:00
code + = """
< / tbody >
< / table >
"""
2022-11-06 15:12:53 +08:00
if hidden > 0 :
code + = f " <p>Extension hidden: { hidden } </p> "
return code , list ( tags )
2022-11-01 14:59:00 +08:00
2023-05-16 01:57:11 +08:00
def preload_extensions_git_metadata ( ) :
for extension in extensions . extensions :
extension . read_info_from_repo ( )
2022-10-31 22:36:45 +08:00
def create_ui ( ) :
import modules . ui
2023-03-30 05:46:03 +08:00
config_states . list_config_states ( )
2023-05-16 01:57:11 +08:00
threading . Thread ( target = preload_extensions_git_metadata ) . start ( )
2022-10-31 22:36:45 +08:00
with gr . Blocks ( analytics_enabled = False ) as ui :
2023-05-10 12:52:45 +08:00
with gr . Tabs ( elem_id = " tabs_extensions " ) :
2023-03-30 02:04:02 +08:00
with gr . TabItem ( " Installed " , id = " installed " ) :
2022-10-31 22:36:45 +08:00
2023-01-28 20:57:56 +08:00
with gr . Row ( elem_id = " extensions_installed_top " ) :
2023-06-06 01:04:28 +08:00
apply_label = ( " Apply and restart UI " if restart . is_restartable ( ) else " Apply and quit " )
apply = gr . Button ( value = apply_label , variant = " primary " )
2022-10-31 22:36:45 +08:00
check = gr . Button ( value = " Check for updates " )
2023-03-28 00:44:49 +08:00
extensions_disable_all = gr . Radio ( label = " Disable all extensions " , choices = [ " none " , " extra " , " all " ] , value = shared . opts . disable_all_extensions , elem_id = " extensions_disable_all " )
2023-08-04 12:50:17 +08:00
extensions_disabled_list = gr . Text ( elem_id = " extensions_disabled_list " , visible = False , container = False )
extensions_update_list = gr . Text ( elem_id = " extensions_update_list " , visible = False , container = False )
2024-02-07 20:06:17 +08:00
refresh = gr . Button ( value = ' Refresh ' , variant = " compact " )
2022-10-31 22:36:45 +08:00
2023-03-28 00:44:49 +08:00
html = " "
2023-07-29 00:07:35 +08:00
if shared . cmd_opts . disable_all_extensions or shared . cmd_opts . disable_extra_extensions or shared . opts . disable_all_extensions != " none " :
if shared . cmd_opts . disable_all_extensions :
msg = ' " --disable-all-extensions " was used, remove it to load all extensions again '
elif shared . opts . disable_all_extensions != " none " :
msg = ' " Disable all extensions " was set, change it to " none " to load all extensions again '
elif shared . cmd_opts . disable_extra_extensions :
msg = ' " --disable-extra-extensions " was used, remove it to load all extensions again '
html = f ' <span style= " color: var(--primary-400); " > { msg } </span> '
2023-08-30 13:05:18 +08:00
with gr . Row ( ) :
info = gr . HTML ( html )
with gr . Row ( elem_classes = " progress-container " ) :
extensions_table = gr . HTML ( ' Loading... ' , elem_id = " extensions_installed_html " )
2024-02-07 20:06:17 +08:00
ui . load ( fn = extension_table , inputs = [ ] , outputs = [ extensions_table ] , show_progress = False )
refresh . click ( fn = extension_table , inputs = [ ] , outputs = [ extensions_table ] , show_progress = False )
2022-10-31 22:36:45 +08:00
apply . click (
fn = apply_and_restart ,
_js = " extensions_apply " ,
2023-03-28 00:44:49 +08:00
inputs = [ extensions_disabled_list , extensions_update_list , extensions_disable_all ] ,
2022-10-31 22:36:45 +08:00
outputs = [ ] ,
)
check . click (
2023-01-28 20:57:56 +08:00
fn = wrap_gradio_gpu_call ( check_updates , extra_outputs = [ gr . update ( ) ] ) ,
2022-10-31 22:36:45 +08:00
_js = " extensions_check " ,
2023-01-28 20:57:56 +08:00
inputs = [ info , extensions_disabled_list ] ,
outputs = [ extensions_table , info ] ,
2022-10-31 22:36:45 +08:00
)
2023-03-30 02:04:02 +08:00
with gr . TabItem ( " Available " , id = " available " ) :
2022-11-01 14:59:00 +08:00
with gr . Row ( ) :
refresh_available_extensions_button = gr . Button ( value = " Load from: " , variant = " primary " )
2023-07-15 17:09:51 +08:00
extensions_index_url = os . environ . get ( ' WEBUI_EXTENSIONS_INDEX ' , " https://raw.githubusercontent.com/AUTOMATIC1111/stable-diffusion-webui-extensions/master/index.json " )
2023-08-04 12:50:17 +08:00
available_extensions_index = gr . Text ( value = extensions_index_url , label = " Extension index URL " , container = False )
2022-11-01 14:59:00 +08:00
extension_to_install = gr . Text ( elem_id = " extension_to_install " , visible = False )
install_extension_button = gr . Button ( elem_id = " install_extension_button " , visible = False )
2022-11-06 15:12:53 +08:00
with gr . Row ( ) :
2022-12-10 20:05:22 +08:00
hide_tags = gr . CheckboxGroup ( value = [ " ads " , " localization " , " installed " ] , label = " Hide extensions with tags " , choices = [ " script " , " ads " , " localization " , " installed " ] )
2023-06-29 21:46:59 +08:00
sort_column = gr . Radio ( value = " newest first " , label = " Order " , choices = [ " newest first " , " oldest first " , " a-z " , " z-a " , " internal order " , ' update time ' , ' create time ' , " stars " ] , type = " index " )
2022-11-06 15:12:53 +08:00
2023-05-11 23:28:15 +08:00
with gr . Row ( ) :
2023-08-04 12:50:17 +08:00
search_extensions_text = gr . Text ( label = " Search " , container = False )
2023-05-11 23:28:15 +08:00
2022-11-01 14:59:00 +08:00
install_result = gr . HTML ( )
available_extensions_table = gr . HTML ( )
refresh_available_extensions_button . click (
2023-06-27 13:59:35 +08:00
fn = modules . ui . wrap_gradio_call ( refresh_available_extensions , extra_outputs = [ gr . update ( ) , gr . update ( ) , gr . update ( ) , gr . update ( ) ] ) ,
2023-01-06 17:32:44 +08:00
inputs = [ available_extensions_index , hide_tags , sort_column ] ,
2023-06-27 13:59:35 +08:00
outputs = [ available_extensions_index , available_extensions_table , hide_tags , search_extensions_text , install_result ] ,
2022-11-01 14:59:00 +08:00
)
install_extension_button . click (
fn = modules . ui . wrap_gradio_call ( install_extension_from_index , extra_outputs = [ gr . update ( ) , gr . update ( ) ] ) ,
2023-03-23 23:43:00 +08:00
inputs = [ extension_to_install , hide_tags , sort_column , search_extensions_text ] ,
2022-11-01 14:59:00 +08:00
outputs = [ available_extensions_table , extensions_table , install_result ] ,
)
2023-03-23 23:43:00 +08:00
search_extensions_text . change (
fn = modules . ui . wrap_gradio_call ( search_extensions , extra_outputs = [ gr . update ( ) ] ) ,
inputs = [ search_extensions_text , hide_tags , sort_column ] ,
outputs = [ available_extensions_table , install_result ] ,
)
2022-11-06 15:12:53 +08:00
hide_tags . change (
fn = modules . ui . wrap_gradio_call ( refresh_available_extensions_for_tags , extra_outputs = [ gr . update ( ) ] ) ,
2023-03-23 23:43:00 +08:00
inputs = [ hide_tags , sort_column , search_extensions_text ] ,
2023-01-06 17:32:44 +08:00
outputs = [ available_extensions_table , install_result ]
)
sort_column . change (
fn = modules . ui . wrap_gradio_call ( refresh_available_extensions_for_tags , extra_outputs = [ gr . update ( ) ] ) ,
2023-03-23 23:43:00 +08:00
inputs = [ hide_tags , sort_column , search_extensions_text ] ,
2022-11-06 15:12:53 +08:00
outputs = [ available_extensions_table , install_result ]
)
2023-03-30 02:04:02 +08:00
with gr . TabItem ( " Install from URL " , id = " install_from_url " ) :
2022-10-31 22:36:45 +08:00
install_url = gr . Text ( label = " URL for extension ' s git repository " )
2023-04-10 09:38:26 +08:00
install_branch = gr . Text ( label = " Specific branch name " , placeholder = " Leave empty for default main branch " )
2022-10-31 22:36:45 +08:00
install_dirname = gr . Text ( label = " Local directory name " , placeholder = " Leave empty for auto " )
2022-11-01 14:59:00 +08:00
install_button = gr . Button ( value = " Install " , variant = " primary " )
install_result = gr . HTML ( elem_id = " extension_install_result " )
2022-10-31 22:36:45 +08:00
2022-11-01 14:59:00 +08:00
install_button . click (
2023-05-16 12:59:43 +08:00
fn = modules . ui . wrap_gradio_call ( lambda * args : [ gr . update ( ) , * install_extension_from_url ( * args ) ] , extra_outputs = [ gr . update ( ) , gr . update ( ) ] ) ,
2023-07-08 22:52:03 +08:00
inputs = [ install_dirname , install_url , install_branch ] ,
2023-05-16 12:59:43 +08:00
outputs = [ install_url , extensions_table , install_result ] ,
2022-10-31 22:36:45 +08:00
)
2023-03-30 05:46:03 +08:00
with gr . TabItem ( " Backup/Restore " ) :
with gr . Row ( elem_id = " extensions_backup_top_row " ) :
config_states_list = gr . Dropdown ( label = " Saved Configs " , elem_id = " extension_backup_saved_configs " , value = " Current " , choices = [ " Current " ] + list ( config_states . all_config_states . keys ( ) ) )
modules . ui . create_refresh_button ( config_states_list , config_states . list_config_states , lambda : { " choices " : [ " Current " ] + list ( config_states . all_config_states . keys ( ) ) } , " refresh_config_states " )
config_restore_type = gr . Radio ( label = " State to restore " , choices = [ " extensions " , " webui " , " both " ] , value = " extensions " , elem_id = " extension_backup_restore_type " )
config_restore_button = gr . Button ( value = " Restore Selected Config " , variant = " primary " , elem_id = " extension_backup_restore " )
with gr . Row ( elem_id = " extensions_backup_top_row2 " ) :
config_save_name = gr . Textbox ( " " , placeholder = " Config Name " , show_label = False )
config_save_button = gr . Button ( value = " Save Current Config " )
config_states_info = gr . HTML ( " " )
2023-05-16 01:57:11 +08:00
config_states_table = gr . HTML ( " Loading... " )
ui . load ( fn = update_config_states_table , inputs = [ config_states_list ] , outputs = [ config_states_table ] )
2023-03-30 05:46:03 +08:00
config_save_button . click ( fn = save_config_state , inputs = [ config_save_name ] , outputs = [ config_states_list , config_states_info ] )
dummy_component = gr . Label ( visible = False )
config_restore_button . click ( fn = restore_config_state , _js = " config_state_confirm_restore " , inputs = [ dummy_component , config_states_list , config_restore_type ] , outputs = [ config_states_info ] )
config_states_list . change (
fn = update_config_states_table ,
inputs = [ config_states_list ] ,
outputs = [ config_states_table ] ,
)
2023-05-16 01:57:11 +08:00
2022-10-31 22:36:45 +08:00
return ui