From 32a4c8d961df3da4534c98fd0573d854cff1bb91 Mon Sep 17 00:00:00 2001 From: Kilvoctu Date: Mon, 20 Feb 2023 15:14:06 -0600 Subject: [PATCH 01/22] use emojis for extra network buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔄 for refresh ❌ for close --- modules/ui_extra_networks.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 71f1d81f2..8786fde6b 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -13,6 +13,10 @@ from modules.generation_parameters_copypaste import image_from_url_text extra_pages = [] allowed_dirs = set() +# Using constants for these since the variation selector isn't visible. +# Important that they exactly match script.js for tooltip to work. +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""" @@ -182,8 +186,8 @@ def create_ui(container, button, 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") + button_refresh = gr.Button(refresh_symbol, elem_id=tabname+"_extra_refresh") + button_close = gr.Button(close_symbol, elem_id=tabname+"_extra_close") 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) From 3b6de96467b0ee6d59f3aaf4bafd1467633e14c6 Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sun, 26 Feb 2023 19:17:58 -0500 Subject: [PATCH 02/22] Added alwayson_script_name and alwayson_script_args to api Added 2 additional possible entries in the api request: alwayson_script_name, a string list, and, alwayson_script_args, a list of list containing the args of each script. This allows us to send args to always on script and keep backwards compatibility with old script_name and script_arg api params --- modules/api/api.py | 111 +++++++++++++++++++++++++++++++++++++----- modules/api/models.py | 4 +- 2 files changed, 100 insertions(+), 15 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 5a9ac5f1a..a1cdebb8d 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -163,20 +163,26 @@ class Api: raise HTTPException(status_code=401, detail="Incorrect username or password", headers={"WWW-Authenticate": "Basic"}) - def get_script(self, script_name, script_runner): - if script_name is None: + def get_selectable_script(self, script_name, script_runner): + if script_name is None or script_name == "": return None, None - if not script_runner.scripts: - script_runner.initialize_scripts(False) - ui.create_ui() - script_idx = script_name_to_index(script_name, script_runner.selectable_scripts) script = script_runner.selectable_scripts[script_idx] return script, script_idx + def get_script(self, script_name, script_runner): + for script in script_runner.scripts: + if script_name.lower() == script.title().lower(): + return script + return None + def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): - script, script_idx = self.get_script(txt2imgreq.script_name, scripts.scripts_txt2img) + script_runner = scripts.scripts_txt2img + if not script_runner.scripts: + script_runner.initialize_scripts(False) + ui.create_ui() + api_selectable_scripts, api_selectable_script_idx = self.get_selectable_script(txt2imgreq.script_name, script_runner) populate = txt2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(txt2imgreq.sampler_name or txt2imgreq.sampler_index), @@ -184,22 +190,59 @@ class Api: "do_not_save_grid": True } ) + if populate.sampler_name: populate.sampler_index = None # prevent a warning later on args = vars(populate) args.pop('script_name', None) + args.pop('script_args', None) # will refeed them later with script_args + args.pop('alwayson_script_name', None) + args.pop('alwayson_script_args', None) + + #find max idx from the scripts in runner and generate a none array to init script_args + last_arg_index = 1 + for script in script_runner.scripts: + if last_arg_index < script.args_to: + last_arg_index = script.args_to + # None everywhere exepct position 0 to initialize script args + script_args = [None]*last_arg_index + # position 0 in script_arg is the idx+1 of the selectable script that is going to be run + if api_selectable_scripts: + script_args[api_selectable_scripts.args_from:api_selectable_scripts.args_to] = txt2imgreq.script_args + script_args[0] = api_selectable_script_idx + 1 + else: + # if 0 then none + script_args[0] = 0 + + # Now check for always on scripts + if len(txt2imgreq.alwayson_script_name) > 0: + # always on script with no arg should always run, but if you include their name in the api request, send an empty list for there args + if len(txt2imgreq.alwayson_script_name) != len(txt2imgreq.alwayson_script_args): + raise HTTPException(status_code=422, detail=f"Number of script names and number of script arg lists doesn't match") + + for alwayson_script_name, alwayson_script_args in zip(txt2imgreq.alwayson_script_name, txt2imgreq.alwayson_script_args): + alwayson_script = self.get_script(alwayson_script_name, script_runner) + if alwayson_script == None: + raise HTTPException(status_code=422, detail=f"always on script {alwayson_script_name} not found") + # Selectable script in always on script param check + if alwayson_script.alwayson == False: + raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") + if alwayson_script_args != []: + script_args[alwayson_script.args_from:alwayson_script.args_to] = alwayson_script_args with self.queue_lock: p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args) + p.scripts = script_runner shared.state.begin() - if script is not None: + if api_selectable_scripts != None: + p.script_args = script_args p.outpath_grids = opts.outdir_txt2img_grids p.outpath_samples = opts.outdir_txt2img_samples - p.script_args = [script_idx + 1] + [None] * (script.args_from - 1) + p.script_args processed = scripts.scripts_txt2img.run(p, *p.script_args) else: + p.script_args = tuple(script_args) processed = process_images(p) shared.state.end() @@ -212,12 +255,16 @@ class Api: if init_images is None: raise HTTPException(status_code=404, detail="Init image not found") - script, script_idx = self.get_script(img2imgreq.script_name, scripts.scripts_img2img) - mask = img2imgreq.mask if mask: mask = decode_base64_to_image(mask) + script_runner = scripts.scripts_img2img + if not script_runner.scripts: + script_runner.initialize_scripts(True) + ui.create_ui() + api_selectable_scripts, api_selectable_script_idx = self.get_selectable_script(img2imgreq.script_name, script_runner) + populate = img2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(img2imgreq.sampler_name or img2imgreq.sampler_index), "do_not_save_samples": True, @@ -225,24 +272,62 @@ class Api: "mask": mask } ) + if populate.sampler_name: populate.sampler_index = None # prevent a warning later on args = vars(populate) args.pop('include_init_images', None) # this is meant to be done by "exclude": True in model, but it's for a reason that I cannot determine. args.pop('script_name', None) + args.pop('script_args', None) # will refeed them later with script_args + args.pop('alwayson_script_name', None) + args.pop('alwayson_script_args', None) + + #find max idx from the scripts in runner and generate a none array to init script_args + last_arg_index = 1 + for script in script_runner.scripts: + if last_arg_index < script.args_to: + last_arg_index = script.args_to + # None everywhere exepct position 0 to initialize script args + script_args = [None]*last_arg_index + # position 0 in script_arg is the idx+1 of the selectable script that is going to be run + if api_selectable_scripts: + script_args[api_selectable_scripts.args_from:api_selectable_scripts.args_to] = img2imgreq.script_args + script_args[0] = api_selectable_script_idx + 1 + else: + # if 0 then none + script_args[0] = 0 + + # Now check for always on scripts + if len(img2imgreq.alwayson_script_name) > 0: + # always on script with no arg should always run, but if you include their name in the api request, send an empty list for there args + if len(img2imgreq.alwayson_script_name) != len(img2imgreq.alwayson_script_args): + raise HTTPException(status_code=422, detail=f"Number of script names and number of script arg lists doesn't match") + + for alwayson_script_name, alwayson_script_args in zip(img2imgreq.alwayson_script_name, img2imgreq.alwayson_script_args): + alwayson_script = self.get_script(alwayson_script_name, script_runner) + if alwayson_script == None: + raise HTTPException(status_code=422, detail=f"always on script {alwayson_script_name} not found") + # Selectable script in always on script param check + if alwayson_script.alwayson == False: + raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") + if alwayson_script_args != []: + script_args[alwayson_script.args_from:alwayson_script.args_to] = alwayson_script_args + with self.queue_lock: p = StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args) p.init_images = [decode_base64_to_image(x) for x in init_images] + p.scripts = script_runner shared.state.begin() - if script is not None: + if api_selectable_scripts != None: + p.script_args = script_args p.outpath_grids = opts.outdir_img2img_grids p.outpath_samples = opts.outdir_img2img_samples - p.script_args = [script_idx + 1] + [None] * (script.args_from - 1) + p.script_args processed = scripts.scripts_img2img.run(p, *p.script_args) else: + p.script_args = tuple(script_args) processed = process_images(p) shared.state.end() diff --git a/modules/api/models.py b/modules/api/models.py index cba43d3b1..86c701780 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -100,13 +100,13 @@ class PydanticModelGenerator: StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( "StableDiffusionProcessingTxt2Img", StableDiffusionProcessingTxt2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}] + [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, {"key": "alwayson_script_name", "type": list, "default": []}, {"key": "alwayson_script_args", "type": list, "default": []}] ).generate_model() StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( "StableDiffusionProcessingImg2Img", StableDiffusionProcessingImg2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, {"key": "denoising_strength", "type": float, "default": 0.75}, {"key": "mask", "type": str, "default": None}, {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}] + [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, {"key": "denoising_strength", "type": float, "default": 0.75}, {"key": "mask", "type": str, "default": None}, {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, {"key": "alwayson_script_name", "type": list, "default": []}, {"key": "alwayson_script_args", "type": list, "default": []}] ).generate_model() class TextToImageResponse(BaseModel): From a39c4cf766a9b0f18972fc52bcf5189173f434c6 Mon Sep 17 00:00:00 2001 From: Vespinian Date: Mon, 27 Feb 2023 23:27:33 -0500 Subject: [PATCH 03/22] small refactor of api.py --- modules/api/api.py | 125 ++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 74 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index a1cdebb8d..d4c0c1521 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -172,17 +172,53 @@ class Api: return script, script_idx def get_script(self, script_name, script_runner): + if script_name is None or script_name == "": + return None, None + + script_idx = script_name_to_index(script_name, script_runner.scripts) + return script_runner.scripts[script_idx] + + def init_script_args(self, request, selectable_scripts, selectable_idx, script_runner): + #find max idx from the scripts in runner and generate a none array to init script_args + last_arg_index = 1 for script in script_runner.scripts: - if script_name.lower() == script.title().lower(): - return script - return None + if last_arg_index < script.args_to: + last_arg_index = script.args_to + # None everywhere except position 0 to initialize script args + script_args = [None]*last_arg_index + # position 0 in script_arg is the idx+1 of the selectable script that is going to be run when using scripts.scripts_*2img.run() + if selectable_scripts: + script_args[selectable_scripts.args_from:selectable_scripts.args_to] = request.script_args + script_args[0] = selectable_idx + 1 + else: + # if 0 then none + script_args[0] = 0 + + # Now check for always on scripts + if request.alwayson_script_name and (len(request.alwayson_script_name) > 0): + # always on script with no arg should always run, but if you include their name in the api request, send an empty list for there args + if not request.alwayson_script_args: + raise HTTPException(status_code=422, detail=f"Script {request.alwayson_script_name} has no arg list") + if len(request.alwayson_script_name) != len(request.alwayson_script_args): + raise HTTPException(status_code=422, detail=f"Number of script names and number of script arg lists doesn't match") + + for alwayson_script_name, alwayson_script_args in zip(request.alwayson_script_name, request.alwayson_script_args): + alwayson_script = self.get_script(alwayson_script_name, script_runner) + if alwayson_script == None: + raise HTTPException(status_code=422, detail=f"always on script {alwayson_script_name} not found") + # Selectable script in always on script param check + if alwayson_script.alwayson == False: + raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") + if alwayson_script_args != []: + script_args[alwayson_script.args_from:alwayson_script.args_to] = alwayson_script_args + return script_args def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): script_runner = scripts.scripts_txt2img if not script_runner.scripts: script_runner.initialize_scripts(False) ui.create_ui() - api_selectable_scripts, api_selectable_script_idx = self.get_selectable_script(txt2imgreq.script_name, script_runner) + selectable_scripts, selectable_script_idx = self.get_selectable_script(txt2imgreq.script_name, script_runner) populate = txt2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(txt2imgreq.sampler_name or txt2imgreq.sampler_index), @@ -196,53 +232,24 @@ class Api: args = vars(populate) args.pop('script_name', None) - args.pop('script_args', None) # will refeed them later with script_args + args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them args.pop('alwayson_script_name', None) args.pop('alwayson_script_args', None) - #find max idx from the scripts in runner and generate a none array to init script_args - last_arg_index = 1 - for script in script_runner.scripts: - if last_arg_index < script.args_to: - last_arg_index = script.args_to - # None everywhere exepct position 0 to initialize script args - script_args = [None]*last_arg_index - # position 0 in script_arg is the idx+1 of the selectable script that is going to be run - if api_selectable_scripts: - script_args[api_selectable_scripts.args_from:api_selectable_scripts.args_to] = txt2imgreq.script_args - script_args[0] = api_selectable_script_idx + 1 - else: - # if 0 then none - script_args[0] = 0 - - # Now check for always on scripts - if len(txt2imgreq.alwayson_script_name) > 0: - # always on script with no arg should always run, but if you include their name in the api request, send an empty list for there args - if len(txt2imgreq.alwayson_script_name) != len(txt2imgreq.alwayson_script_args): - raise HTTPException(status_code=422, detail=f"Number of script names and number of script arg lists doesn't match") - - for alwayson_script_name, alwayson_script_args in zip(txt2imgreq.alwayson_script_name, txt2imgreq.alwayson_script_args): - alwayson_script = self.get_script(alwayson_script_name, script_runner) - if alwayson_script == None: - raise HTTPException(status_code=422, detail=f"always on script {alwayson_script_name} not found") - # Selectable script in always on script param check - if alwayson_script.alwayson == False: - raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") - if alwayson_script_args != []: - script_args[alwayson_script.args_from:alwayson_script.args_to] = alwayson_script_args + script_args = self.init_script_args(txt2imgreq, selectable_scripts, selectable_script_idx, script_runner) with self.queue_lock: p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args) p.scripts = script_runner shared.state.begin() - if api_selectable_scripts != None: + if selectable_scripts != None: p.script_args = script_args p.outpath_grids = opts.outdir_txt2img_grids p.outpath_samples = opts.outdir_txt2img_samples - processed = scripts.scripts_txt2img.run(p, *p.script_args) + processed = scripts.scripts_txt2img.run(p, *p.script_args) # Need to pass args as list here else: - p.script_args = tuple(script_args) + p.script_args = tuple(script_args) # Need to pass args as tuple here processed = process_images(p) shared.state.end() @@ -263,7 +270,7 @@ class Api: if not script_runner.scripts: script_runner.initialize_scripts(True) ui.create_ui() - api_selectable_scripts, api_selectable_script_idx = self.get_selectable_script(img2imgreq.script_name, script_runner) + selectable_scripts, selectable_script_idx = self.get_selectable_script(img2imgreq.script_name, script_runner) populate = img2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(img2imgreq.sampler_name or img2imgreq.sampler_index), @@ -279,41 +286,11 @@ class Api: args = vars(populate) args.pop('include_init_images', None) # this is meant to be done by "exclude": True in model, but it's for a reason that I cannot determine. args.pop('script_name', None) - args.pop('script_args', None) # will refeed them later with script_args + args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them args.pop('alwayson_script_name', None) args.pop('alwayson_script_args', None) - #find max idx from the scripts in runner and generate a none array to init script_args - last_arg_index = 1 - for script in script_runner.scripts: - if last_arg_index < script.args_to: - last_arg_index = script.args_to - # None everywhere exepct position 0 to initialize script args - script_args = [None]*last_arg_index - # position 0 in script_arg is the idx+1 of the selectable script that is going to be run - if api_selectable_scripts: - script_args[api_selectable_scripts.args_from:api_selectable_scripts.args_to] = img2imgreq.script_args - script_args[0] = api_selectable_script_idx + 1 - else: - # if 0 then none - script_args[0] = 0 - - # Now check for always on scripts - if len(img2imgreq.alwayson_script_name) > 0: - # always on script with no arg should always run, but if you include their name in the api request, send an empty list for there args - if len(img2imgreq.alwayson_script_name) != len(img2imgreq.alwayson_script_args): - raise HTTPException(status_code=422, detail=f"Number of script names and number of script arg lists doesn't match") - - for alwayson_script_name, alwayson_script_args in zip(img2imgreq.alwayson_script_name, img2imgreq.alwayson_script_args): - alwayson_script = self.get_script(alwayson_script_name, script_runner) - if alwayson_script == None: - raise HTTPException(status_code=422, detail=f"always on script {alwayson_script_name} not found") - # Selectable script in always on script param check - if alwayson_script.alwayson == False: - raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") - if alwayson_script_args != []: - script_args[alwayson_script.args_from:alwayson_script.args_to] = alwayson_script_args - + script_args = self.init_script_args(img2imgreq, selectable_scripts, selectable_script_idx, script_runner) with self.queue_lock: p = StableDiffusionProcessingImg2Img(sd_model=shared.sd_model, **args) @@ -321,13 +298,13 @@ class Api: p.scripts = script_runner shared.state.begin() - if api_selectable_scripts != None: + if selectable_scripts != None: p.script_args = script_args p.outpath_grids = opts.outdir_img2img_grids p.outpath_samples = opts.outdir_img2img_samples - processed = scripts.scripts_img2img.run(p, *p.script_args) + processed = scripts.scripts_img2img.run(p, *p.script_args) # Need to pass args as list here else: - p.script_args = tuple(script_args) + p.script_args = tuple(script_args) # Need to pass args as tuple here processed = process_images(p) shared.state.end() From c6c2a59333c77dffff49a748bfed8c54af6e2abd Mon Sep 17 00:00:00 2001 From: Vespinian Date: Mon, 27 Feb 2023 23:45:59 -0500 Subject: [PATCH 04/22] comment clarification --- modules/api/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/api/api.py b/modules/api/api.py index d4c0c1521..248922d29 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -191,7 +191,7 @@ class Api: script_args[selectable_scripts.args_from:selectable_scripts.args_to] = request.script_args script_args[0] = selectable_idx + 1 else: - # if 0 then none + # when [0] = 0 no selectable script to run script_args[0] = 0 # Now check for always on scripts From d006108d75d74b3237ccbb60a9373bf28c2b85d7 Mon Sep 17 00:00:00 2001 From: Zhang Hua Date: Fri, 10 Mar 2023 16:35:55 +0800 Subject: [PATCH 05/22] webui.sh: remove all `cd` related code This may be helpful for https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/7028, because we won't change working directory to the repo now, instead, we will use any working directory. If we set working directory to a path contains repo and the custom --data-dir, the problem in this issue should be solved. Howewer, this may be treated as an incompatible change if some code assume the working directory is always the repo. Also, there may be another solution that always let --data-dir be the subdirectory of the repo, but personally I think this may not be what we actually need. As this issue mainly influent on Docker and I am not familiar with .bat files, updating webui.bat is skipped. webui.sh: source env from repo instead $PWD --- webui.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/webui.sh b/webui.sh index 8cdad22d3..7b6e0568d 100755 --- a/webui.sh +++ b/webui.sh @@ -6,19 +6,18 @@ # If run from macOS, load defaults from webui-macos-env.sh if [[ "$OSTYPE" == "darwin"* ]]; then - if [[ -f webui-macos-env.sh ]] + if [[ -f "$(dirname $0)/webui-macos-env.sh" ]] then - source ./webui-macos-env.sh + source "$(dirname $0)/webui-macos-env.sh" fi fi # Read variables from webui-user.sh # shellcheck source=/dev/null -if [[ -f webui-user.sh ]] +if [[ -f "$(dirname $0)/webui-user.sh" ]] then - source ./webui-user.sh + source "$(dirname $0)/webui-user.sh" fi - # Set defaults # Install directory without trailing slash if [[ -z "${install_dir}" ]] @@ -47,12 +46,12 @@ fi # python3 venv without trailing slash (defaults to ${install_dir}/${clone_dir}/venv) if [[ -z "${venv_dir}" ]] then - venv_dir="venv" + venv_dir="${install_dir}/${clone_dir}/venv" fi if [[ -z "${LAUNCH_SCRIPT}" ]] then - LAUNCH_SCRIPT="launch.py" + LAUNCH_SCRIPT="${install_dir}/${clone_dir}/launch.py" fi # this script cannot be run as root by default @@ -140,22 +139,23 @@ then exit 1 fi -cd "${install_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/, aborting...\e[0m" "${install_dir}"; exit 1; } -if [[ -d "${clone_dir}" ]] +if [[ ! -d "${install_dir}/${clone_dir}" ]] then - cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } -else printf "\n%s\n" "${delimiter}" printf "Clone stable-diffusion-webui" printf "\n%s\n" "${delimiter}" - "${GIT}" clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git "${clone_dir}" - cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } + mkdir -p "${install_dir}" + "${GIT}" clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git "${install_dir}/${clone_dir}" fi printf "\n%s\n" "${delimiter}" printf "Create and activate python venv" printf "\n%s\n" "${delimiter}" -cd "${install_dir}"/"${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } +# Make venv_dir absolute +if [[ "${venv_dir}" != /* ]] +then + venv_dir="${install_dir}/${clone_dir}/${venv_dir}" +fi if [[ ! -d "${venv_dir}" ]] then "${python_cmd}" -m venv "${venv_dir}" From 1fa1ab5249ccc7acaafa5f3e11c6925f91543f8b Mon Sep 17 00:00:00 2001 From: Zhang Hua Date: Fri, 10 Mar 2023 21:38:35 +0800 Subject: [PATCH 06/22] launch.py: fix failure because webui.sh's changes launch.py: using getcwd() instead curdir launch.py: use absolute path for preparing also remove chdir() launch.py: use absolute path for test launch.py: add default script_path and data_path --- launch.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/launch.py b/launch.py index 0868f8a90..e1908496a 100644 --- a/launch.py +++ b/launch.py @@ -7,6 +7,11 @@ import shlex import platform import argparse import json +try: + from modules.paths import script_path, data_path +except ModuleNotFoundError: + script_path = os.path.dirname(__file__) + data_path = os.getcwd() dir_repos = "repositories" dir_extensions = "extensions" @@ -122,7 +127,7 @@ def is_installed(package): def repo_dir(name): - return os.path.join(dir_repos, name) + return os.path.join(script_path, dir_repos, name) def run_python(code, desc=None, errdesc=None): @@ -215,7 +220,7 @@ def list_extensions(settings_file): disabled_extensions = set(settings.get('disabled_extensions', [])) - return [x for x in os.listdir(dir_extensions) if x not in disabled_extensions] + return [x for x in os.listdir(os.path.join(data_path, dir_extensions)) if x not in disabled_extensions] def run_extensions_installers(settings_file): @@ -306,7 +311,7 @@ def prepare_environment(): if not is_installed("pyngrok") and ngrok: run_pip("install pyngrok", "ngrok") - os.makedirs(dir_repos, exist_ok=True) + os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True) git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash) git_clone(taming_transformers_repo, repo_dir('taming-transformers'), "Taming Transformers", taming_transformers_commit_hash) @@ -317,7 +322,7 @@ def prepare_environment(): if not is_installed("lpips"): run_pip(f"install -r {os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}", "requirements for CodeFormer") - run_pip(f"install -r {requirements_file}", "requirements for Web UI") + run_pip(f"install -r {os.path.join(script_path, requirements_file)}", "requirements for Web UI") run_extensions_installers(settings_file=args.ui_settings_file) @@ -325,7 +330,7 @@ def prepare_environment(): version_check(commit) if update_all_extensions: - git_pull_recursive(dir_extensions) + git_pull_recursive(os.path.join(data_path, dir_extensions)) if "--exit" in sys.argv: print("Exiting because of --exit argument") @@ -341,7 +346,7 @@ def tests(test_dir): sys.argv.append("--api") if "--ckpt" not in sys.argv: sys.argv.append("--ckpt") - sys.argv.append("./test/test_files/empty.pt") + sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt")) if "--skip-torch-cuda-test" not in sys.argv: sys.argv.append("--skip-torch-cuda-test") if "--disable-nan-check" not in sys.argv: @@ -350,7 +355,7 @@ def tests(test_dir): print(f"Launching Web UI in another process for testing with arguments: {' '.join(sys.argv[1:])}") os.environ['COMMANDLINE_ARGS'] = "" - with open('test/stdout.txt', "w", encoding="utf8") as stdout, open('test/stderr.txt', "w", encoding="utf8") as stderr: + with open(os.path.join(script_path, 'test/stdout.txt'), "w", encoding="utf8") as stdout, open(os.path.join(script_path, 'test/stderr.txt'), "w", encoding="utf8") as stderr: proc = subprocess.Popen([sys.executable, *sys.argv], stdout=stdout, stderr=stderr) import test.server_poll From 8106117a474a5e62dbd73687dadc6e7b7637267d Mon Sep 17 00:00:00 2001 From: Zhang Hua Date: Sat, 11 Mar 2023 09:18:08 +0800 Subject: [PATCH 07/22] models/ui.py: make the path of script.js absolute --- modules/ui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ui.py b/modules/ui.py index 0516c6436..56b55f293 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1745,7 +1745,8 @@ def create_ui(): def reload_javascript(): - head = f'\n' + script_js = os.path.join(script_path, "script.js") + head = f'\n' inline = f"{localization.localization_js(shared.opts.localization)};" if cmd_opts.theme is not None: From 8e0d16e746759b1f9b4bf1b5abfc30f3d985415e Mon Sep 17 00:00:00 2001 From: Zhang Hua Date: Sat, 11 Mar 2023 12:22:59 +0800 Subject: [PATCH 08/22] modules/sd_vae_approx.py: fix VAE-approx path --- modules/sd_vae_approx.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/sd_vae_approx.py b/modules/sd_vae_approx.py index 0027343a7..e2f004683 100644 --- a/modules/sd_vae_approx.py +++ b/modules/sd_vae_approx.py @@ -35,8 +35,11 @@ def model(): global sd_vae_approx_model if sd_vae_approx_model is None: + model_path = os.path.join(paths.models_path, "VAE-approx", "model.pt") sd_vae_approx_model = VAEApprox() - sd_vae_approx_model.load_state_dict(torch.load(os.path.join(paths.models_path, "VAE-approx", "model.pt"), map_location='cpu' if devices.device.type != 'cuda' else None)) + if not os.path.exists(model_path): + model_path = os.path.join(paths.script_path, "models", "VAE-approx", "model.pt") + sd_vae_approx_model.load_state_dict(torch.load(model_path, map_location='cpu' if devices.device.type != 'cuda' else None)) sd_vae_approx_model.eval() sd_vae_approx_model.to(devices.device, devices.dtype) From 9abe2f5e74f02d4c8e47d2d4e03464179e9f0aa2 Mon Sep 17 00:00:00 2001 From: Zhang Hua Date: Sat, 11 Mar 2023 13:27:10 +0800 Subject: [PATCH 09/22] test/server_poll.py: use absolute path for test test/server_poll.py: fix absolute path --- test/server_poll.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/server_poll.py b/test/server_poll.py index 42d56a4ca..c732630f1 100644 --- a/test/server_poll.py +++ b/test/server_poll.py @@ -1,6 +1,8 @@ import unittest import requests import time +import os +from modules.paths import script_path def run_tests(proc, test_dir): @@ -15,8 +17,8 @@ def run_tests(proc, test_dir): break if proc.poll() is None: if test_dir is None: - test_dir = "test" - suite = unittest.TestLoader().discover(test_dir, pattern="*_test.py", top_level_dir="test") + test_dir = os.path.join(script_path, "test") + suite = unittest.TestLoader().discover(test_dir, pattern="*_test.py", top_level_dir=test_dir) result = unittest.TextTestRunner(verbosity=2).run(suite) return len(result.failures) + len(result.errors) else: From d25c4b13e4be0c89401637c769e3634e7ee456a7 Mon Sep 17 00:00:00 2001 From: Zhang Hua Date: Sat, 11 Mar 2023 14:42:31 +0800 Subject: [PATCH 10/22] test/basic_features/{extras,img2img}_test.py: use absolute path --- test/basic_features/extras_test.py | 8 +++++--- test/basic_features/img2img_test.py | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/test/basic_features/extras_test.py b/test/basic_features/extras_test.py index 0170c511f..8ed98747d 100644 --- a/test/basic_features/extras_test.py +++ b/test/basic_features/extras_test.py @@ -1,7 +1,9 @@ +import os import unittest import requests from gradio.processing_utils import encode_pil_to_base64 from PIL import Image +from modules.paths import script_path class TestExtrasWorking(unittest.TestCase): def setUp(self): @@ -19,7 +21,7 @@ class TestExtrasWorking(unittest.TestCase): "upscaler_1": "None", "upscaler_2": "None", "extras_upscaler_2_visibility": 0, - "image": encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png")) + "image": encode_pil_to_base64(Image.open(os.path.join(script_path, r"test/test_files/img2img_basic.png"))) } def test_simple_upscaling_performed(self): @@ -31,7 +33,7 @@ class TestPngInfoWorking(unittest.TestCase): def setUp(self): self.url_png_info = "http://localhost:7860/sdapi/v1/extra-single-image" self.png_info = { - "image": encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png")) + "image": encode_pil_to_base64(Image.open(os.path.join(script_path, r"test/test_files/img2img_basic.png"))) } def test_png_info_performed(self): @@ -42,7 +44,7 @@ class TestInterrogateWorking(unittest.TestCase): def setUp(self): self.url_interrogate = "http://localhost:7860/sdapi/v1/extra-single-image" self.interrogate = { - "image": encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png")), + "image": encode_pil_to_base64(Image.open(os.path.join(script_path, r"test/test_files/img2img_basic.png"))), "model": "clip" } diff --git a/test/basic_features/img2img_test.py b/test/basic_features/img2img_test.py index 08c5c903e..5240ec36a 100644 --- a/test/basic_features/img2img_test.py +++ b/test/basic_features/img2img_test.py @@ -1,14 +1,16 @@ +import os import unittest import requests from gradio.processing_utils import encode_pil_to_base64 from PIL import Image +from modules.paths import script_path class TestImg2ImgWorking(unittest.TestCase): def setUp(self): self.url_img2img = "http://localhost:7860/sdapi/v1/img2img" self.simple_img2img = { - "init_images": [encode_pil_to_base64(Image.open(r"test/test_files/img2img_basic.png"))], + "init_images": [encode_pil_to_base64(Image.open(os.path.join(script_path, r"test/test_files/img2img_basic.png")))], "resize_mode": 0, "denoising_strength": 0.75, "mask": None, @@ -47,11 +49,11 @@ class TestImg2ImgWorking(unittest.TestCase): self.assertEqual(requests.post(self.url_img2img, json=self.simple_img2img).status_code, 200) def test_inpainting_masked_performed(self): - self.simple_img2img["mask"] = encode_pil_to_base64(Image.open(r"test/test_files/mask_basic.png")) + self.simple_img2img["mask"] = encode_pil_to_base64(Image.open(os.path.join(script_path, r"test/test_files/img2img_basic.png"))) self.assertEqual(requests.post(self.url_img2img, json=self.simple_img2img).status_code, 200) def test_inpainting_with_inverted_masked_performed(self): - self.simple_img2img["mask"] = encode_pil_to_base64(Image.open(r"test/test_files/mask_basic.png")) + self.simple_img2img["mask"] = encode_pil_to_base64(Image.open(os.path.join(script_path, r"test/test_files/img2img_basic.png"))) self.simple_img2img["inpainting_mask_invert"] = True self.assertEqual(requests.post(self.url_img2img, json=self.simple_img2img).status_code, 200) From 29ce0bf4f2e708cbd58ee4b9c89d6f27c2f36baa Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 11 Mar 2023 12:01:08 -0500 Subject: [PATCH 11/22] allow usage of latest fastapi --- requirements_versions.txt | 2 +- webui.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/requirements_versions.txt b/requirements_versions.txt index 41e0ccc53..0031c6162 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -27,4 +27,4 @@ GitPython==3.1.30 torchsde==0.2.5 safetensors==0.2.7 httpcore<=0.15 -fastapi==0.90.1 +fastapi==0.94.0 diff --git a/webui.py b/webui.py index 325618772..1a4175afb 100644 --- a/webui.py +++ b/webui.py @@ -183,13 +183,16 @@ def initialize(): signal.signal(signal.SIGINT, sigint_handler) -def setup_cors(app): +def setup_middleware(app): + app.middleware_stack = None # reset current middleware to allow modifying user provided list + app.add_middleware(GZipMiddleware, minimum_size=1000) if cmd_opts.cors_allow_origins and cmd_opts.cors_allow_origins_regex: app.add_middleware(CORSMiddleware, allow_origins=cmd_opts.cors_allow_origins.split(','), allow_origin_regex=cmd_opts.cors_allow_origins_regex, allow_methods=['*'], allow_credentials=True, allow_headers=['*']) elif cmd_opts.cors_allow_origins: app.add_middleware(CORSMiddleware, allow_origins=cmd_opts.cors_allow_origins.split(','), allow_methods=['*'], allow_credentials=True, allow_headers=['*']) elif cmd_opts.cors_allow_origins_regex: app.add_middleware(CORSMiddleware, allow_origin_regex=cmd_opts.cors_allow_origins_regex, allow_methods=['*'], allow_credentials=True, allow_headers=['*']) + app.build_middleware_stack() # rebuild middleware stack on-the-fly def create_api(app): @@ -213,8 +216,7 @@ def api_only(): initialize() app = FastAPI() - setup_cors(app) - app.add_middleware(GZipMiddleware, minimum_size=1000) + setup_middleware(app) api = create_api(app) modules.script_callbacks.app_started_callback(None, app) @@ -271,9 +273,7 @@ def webui(): # running its code. We disable this here. Suggested by RyotaK. app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware'] - setup_cors(app) - - app.add_middleware(GZipMiddleware, minimum_size=1000) + setup_middleware(app) modules.progress.setup_progress_api(app) From 2174f58daee1e077eec1125e196d34cc93dbaf23 Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 11 Mar 2023 12:21:33 -0500 Subject: [PATCH 12/22] Changed alwayson_script_name and alwayson_script_args api params to 1 alwayson_scripts param dict --- modules/api/api.py | 23 +++++++---------------- modules/api/models.py | 4 ++-- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 248922d29..8a17017bc 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -195,22 +195,17 @@ class Api: script_args[0] = 0 # Now check for always on scripts - if request.alwayson_script_name and (len(request.alwayson_script_name) > 0): - # always on script with no arg should always run, but if you include their name in the api request, send an empty list for there args - if not request.alwayson_script_args: - raise HTTPException(status_code=422, detail=f"Script {request.alwayson_script_name} has no arg list") - if len(request.alwayson_script_name) != len(request.alwayson_script_args): - raise HTTPException(status_code=422, detail=f"Number of script names and number of script arg lists doesn't match") - - for alwayson_script_name, alwayson_script_args in zip(request.alwayson_script_name, request.alwayson_script_args): + if request.alwayson_scripts and (len(request.alwayson_scripts) > 0): + for alwayson_script_name in request.alwayson_scripts.keys(): alwayson_script = self.get_script(alwayson_script_name, script_runner) if alwayson_script == None: raise HTTPException(status_code=422, detail=f"always on script {alwayson_script_name} not found") # Selectable script in always on script param check if alwayson_script.alwayson == False: raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") - if alwayson_script_args != []: - script_args[alwayson_script.args_from:alwayson_script.args_to] = alwayson_script_args + # always on script with no arg should always run so you don't really need to add them to the requests + if "args" in request.alwayson_scripts[alwayson_script_name]: + script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] return script_args def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): @@ -226,15 +221,13 @@ class Api: "do_not_save_grid": True } ) - if populate.sampler_name: populate.sampler_index = None # prevent a warning later on args = vars(populate) args.pop('script_name', None) args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them - args.pop('alwayson_script_name', None) - args.pop('alwayson_script_args', None) + args.pop('alwayson_scripts', None) script_args = self.init_script_args(txt2imgreq, selectable_scripts, selectable_script_idx, script_runner) @@ -279,7 +272,6 @@ class Api: "mask": mask } ) - if populate.sampler_name: populate.sampler_index = None # prevent a warning later on @@ -287,8 +279,7 @@ class Api: args.pop('include_init_images', None) # this is meant to be done by "exclude": True in model, but it's for a reason that I cannot determine. args.pop('script_name', None) args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them - args.pop('alwayson_script_name', None) - args.pop('alwayson_script_args', None) + args.pop('alwayson_scripts', None) script_args = self.init_script_args(img2imgreq, selectable_scripts, selectable_script_idx, script_runner) diff --git a/modules/api/models.py b/modules/api/models.py index 86c701780..e273469d2 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -100,13 +100,13 @@ class PydanticModelGenerator: StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( "StableDiffusionProcessingTxt2Img", StableDiffusionProcessingTxt2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, {"key": "alwayson_script_name", "type": list, "default": []}, {"key": "alwayson_script_args", "type": list, "default": []}] + [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, {"key": "alwayson_scripts", "type": dict, "default": {}}] ).generate_model() StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( "StableDiffusionProcessingImg2Img", StableDiffusionProcessingImg2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, {"key": "denoising_strength", "type": float, "default": 0.75}, {"key": "mask", "type": str, "default": None}, {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, {"key": "alwayson_script_name", "type": list, "default": []}, {"key": "alwayson_script_args", "type": list, "default": []}] + [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, {"key": "denoising_strength", "type": float, "default": 0.75}, {"key": "mask", "type": str, "default": None}, {"key": "include_init_images", "type": bool, "default": False, "exclude" : True}, {"key": "script_name", "type": str, "default": None}, {"key": "script_args", "type": list, "default": []}, {"key": "alwayson_scripts", "type": dict, "default": {}}] ).generate_model() class TextToImageResponse(BaseModel): From 5546e71a105033989adacc2df9dfb53f81a0534c Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 11 Mar 2023 12:35:20 -0500 Subject: [PATCH 13/22] Fixed whitespace --- modules/api/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/api/models.py b/modules/api/models.py index 6c1c55467..4a70f440c 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -113,7 +113,6 @@ StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( "StableDiffusionProcessingImg2Img", StableDiffusionProcessingImg2Img, - [ {"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, From 27e319dc4f09a2f040043948e5c52965976f8491 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 11 Mar 2023 21:22:52 +0300 Subject: [PATCH 14/22] alternative solution for #8089 --- modules/shared.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index 4e229353b..2fb9e3b5a 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -116,7 +116,10 @@ parser.add_argument("--no-download-sd-model", action='store_true', help="don't d script_loading.preload_extensions(extensions.extensions_dir, parser) script_loading.preload_extensions(extensions.extensions_builtin_dir, parser) -cmd_opts = parser.parse_args() +if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None: + cmd_opts = parser.parse_args() +else: + cmd_opts, _ = parser.parse_known_args() restricted_opts = { "samples_filename_pattern", From 247a34498b337798a371d69483bbcab49b5c320c Mon Sep 17 00:00:00 2001 From: Kilvoctu Date: Sat, 11 Mar 2023 13:11:26 -0600 Subject: [PATCH 15/22] restore text, remove 'close' don't use emojis for extra network buttons; remove 'close' --- javascript/extraNetworks.js | 2 -- modules/ui_extra_networks.py | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 17bf20004..8f7cee035 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -5,12 +5,10 @@ function setupExtraNetworksForTab(tabname){ var tabs = gradioApp().querySelector('#'+tabname+'_extra_tabs > div') var search = gradioApp().querySelector('#'+tabname+'_extra_search textarea') var refresh = gradioApp().getElementById(tabname+'_extra_refresh') - var close = gradioApp().getElementById(tabname+'_extra_close') search.classList.add('search') tabs.appendChild(search) tabs.appendChild(refresh) - tabs.appendChild(close) search.addEventListener("input", function(evt){ searchTerm = search.value.toLowerCase() diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 8786fde6b..3f56bf635 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -13,10 +13,6 @@ from modules.generation_parameters_copypaste import image_from_url_text extra_pages = [] allowed_dirs = set() -# Using constants for these since the variation selector isn't visible. -# Important that they exactly match script.js for tooltip to work. -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""" @@ -186,8 +182,7 @@ def create_ui(container, button, 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_symbol, elem_id=tabname+"_extra_refresh") - button_close = gr.Button(close_symbol, elem_id=tabname+"_extra_close") + button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh") 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) @@ -198,7 +193,6 @@ def create_ui(container, button, tabname): 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]) def refresh(): res = [] From 49bbdbe4476a7325e184f3022166c3dc02d086ba Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 11 Mar 2023 14:34:56 -0500 Subject: [PATCH 16/22] small diff whitespace cleanup --- modules/api/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/api/api.py b/modules/api/api.py index abdbb6a7a..35e17afc9 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -274,7 +274,7 @@ class Api: ui.create_ui() selectable_scripts, selectable_script_idx = self.get_selectable_script(img2imgreq.script_name, script_runner) - populate = img2imgreq.copy(update={ # Override __init__ params + populate = img2imgreq.copy(update={ # Override __init__ params "sampler_name": validate_sampler_name(img2imgreq.sampler_name or img2imgreq.sampler_index), "do_not_save_samples": not img2imgreq.save_images, "do_not_save_grid": not img2imgreq.save_images, From 48f4abd2e61e545104f72eb50c9ab9b100726948 Mon Sep 17 00:00:00 2001 From: EllangoK Date: Sat, 11 Mar 2023 15:52:14 -0500 Subject: [PATCH 17/22] fix dims typo in unipc --- modules/models/diffusion/uni_pc/uni_pc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/models/diffusion/uni_pc/uni_pc.py b/modules/models/diffusion/uni_pc/uni_pc.py index df63d1bcf..e9a093a2b 100644 --- a/modules/models/diffusion/uni_pc/uni_pc.py +++ b/modules/models/diffusion/uni_pc/uni_pc.py @@ -719,7 +719,7 @@ class UniPC: x_t = x_t_ - expand_dims(alpha_t * B_h, dims) * (corr_res + rhos_c[-1] * D1_t) else: x_t_ = ( - expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dimss) * x + expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dims) * x - expand_dims(sigma_t * h_phi_1, dims) * model_prev_0 ) if x_t is None: From a4cb96d4ae82741be9f0d072a37af3ae39521379 Mon Sep 17 00:00:00 2001 From: brkirch Date: Sat, 11 Mar 2023 17:35:17 -0500 Subject: [PATCH 18/22] Remove test, use bool tensor fix by default The test isn't working correctly on macOS 13.3 and the bool tensor fix for cumsum is currently always needed anyway, so enable the fix by default. --- modules/mac_specific.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/mac_specific.py b/modules/mac_specific.py index ddcea53b9..18e6ff720 100644 --- a/modules/mac_specific.py +++ b/modules/mac_specific.py @@ -23,7 +23,7 @@ def cumsum_fix(input, cumsum_func, *args, **kwargs): output_dtype = kwargs.get('dtype', input.dtype) if output_dtype == torch.int64: return cumsum_func(input.cpu(), *args, **kwargs).to(input.device) - elif cumsum_needs_bool_fix and output_dtype == torch.bool or cumsum_needs_int_fix and (output_dtype == torch.int8 or output_dtype == torch.int16): + elif output_dtype == torch.bool or cumsum_needs_int_fix and (output_dtype == torch.int8 or output_dtype == torch.int16): return cumsum_func(input.to(torch.int32), *args, **kwargs).to(torch.int64) return cumsum_func(input, *args, **kwargs) @@ -45,7 +45,6 @@ if has_mps: CondFunc('torch.Tensor.numpy', lambda orig_func, self, *args, **kwargs: orig_func(self.detach(), *args, **kwargs), lambda _, self, *args, **kwargs: self.requires_grad) elif version.parse(torch.__version__) > version.parse("1.13.1"): cumsum_needs_int_fix = not torch.Tensor([1,2]).to(torch.device("mps")).equal(torch.ShortTensor([1,1]).to(torch.device("mps")).cumsum(0)) - cumsum_needs_bool_fix = not torch.BoolTensor([True,True]).to(device=torch.device("mps"), dtype=torch.int64).equal(torch.BoolTensor([True,False]).to(torch.device("mps")).cumsum(0)) cumsum_fix_func = lambda orig_func, input, *args, **kwargs: cumsum_fix(input, orig_func, *args, **kwargs) CondFunc('torch.cumsum', cumsum_fix_func, None) CondFunc('torch.Tensor.cumsum', cumsum_fix_func, None) From 5ed5e95fb8a0a4a3292eff22dd1b25e960b066a9 Mon Sep 17 00:00:00 2001 From: high_byte Date: Sun, 12 Mar 2023 03:29:07 +0200 Subject: [PATCH 19/22] add face restoration option to xyz_grid --- scripts/xyz_grid.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 9a0678fa7..ce584981b 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -132,6 +132,20 @@ def apply_uni_pc_order(p, x, xs): opts.data["uni_pc_order"] = min(x, p.steps - 1) +def apply_face_restore(p, opt, x): + opt = opt.lower() + if opt == 'codeformer': + is_active = True + p.face_restoration_model = 'CodeFormer' + elif opt == 'gfpgan': + is_active = True + p.face_restoration_model = 'GFPGAN' + else: + is_active = opt in ('true', 'yes', 'y', '1') + + p.restore_faces = is_active + + def format_value_add_label(p, opt, x): if type(x) == float: x = round(x, 8) @@ -210,6 +224,7 @@ axis_options = [ AxisOption("VAE", str, apply_vae, cost=0.7, choices=lambda: list(sd_vae.vae_dict)), AxisOption("Styles", str, apply_styles, choices=lambda: list(shared.prompt_styles.styles)), AxisOption("UniPC Order", int, apply_uni_pc_order, cost=0.5), + AxisOption("Face restore", str, apply_face_restore, format_value=format_value), ] From db85421da18cfaaf1ed0361bc2a9ee40b5796344 Mon Sep 17 00:00:00 2001 From: bluelovers Date: Mon, 27 Feb 2023 09:52:55 +0800 Subject: [PATCH 20/22] feat: better lightbox when not enable zoom --- javascript/imageviewer.js | 2 +- style.css | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index aac2ee823..28e748b74 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -11,7 +11,7 @@ function showModal(event) { if (modalImage.style.display === 'none') { lb.style.setProperty('background-image', 'url(' + source.src + ')'); } - lb.style.display = "block"; + lb.style.display = "flex"; lb.focus() const tabTxt2Img = gradioApp().getElementById("tab_txt2img") diff --git a/style.css b/style.css index 05572f662..4695cc299 100644 --- a/style.css +++ b/style.css @@ -436,9 +436,7 @@ input[type="range"]{ #modalImage { display: block; - margin-left: auto; - margin-right: auto; - margin-top: auto; + margin: auto; width: auto; } From 5c9f2bbb7473c7085dc961bbf81d5248a4859e90 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 12 Mar 2023 08:58:58 +0300 Subject: [PATCH 21/22] do not import modules.paths in launch.py --- launch.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/launch.py b/launch.py index e1908496a..4873da606 100644 --- a/launch.py +++ b/launch.py @@ -7,11 +7,14 @@ import shlex import platform import argparse import json -try: - from modules.paths import script_path, data_path -except ModuleNotFoundError: - script_path = os.path.dirname(__file__) - data_path = os.getcwd() + +parser = argparse.ArgumentParser(add_help=False) +parser.add_argument("--ui-settings-file", type=str, default='config.json') +parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.realpath(__file__))) +args, _ = parser.parse_known_args(sys.argv) + +script_path = os.path.dirname(__file__) +data_path = os.getcwd() dir_repos = "repositories" dir_extensions = "extensions" @@ -257,10 +260,6 @@ def prepare_environment(): sys.argv += shlex.split(commandline_args) - parser = argparse.ArgumentParser(add_help=False) - parser.add_argument("--ui-settings-file", type=str, help="filename to use for ui settings", default='config.json') - args, _ = parser.parse_known_args(sys.argv) - sys.argv, _ = extract_arg(sys.argv, '-f') sys.argv, update_all_extensions = extract_arg(sys.argv, '--update-all-extensions') sys.argv, skip_torch_cuda_test = extract_arg(sys.argv, '--skip-torch-cuda-test') From 3c922d983bf60ba187b5422b3690e6b7fb07777e Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 12 Mar 2023 12:11:51 +0300 Subject: [PATCH 22/22] fix #8492 breaking the program when the directory with code contains spaces. --- launch.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/launch.py b/launch.py index 4873da606..b943fed22 100644 --- a/launch.py +++ b/launch.py @@ -319,9 +319,11 @@ def prepare_environment(): git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash) if not is_installed("lpips"): - run_pip(f"install -r {os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}", "requirements for CodeFormer") + run_pip(f"install -r \"{os.path.join(repo_dir('CodeFormer'), 'requirements.txt')}\"", "requirements for CodeFormer") - run_pip(f"install -r {os.path.join(script_path, requirements_file)}", "requirements for Web UI") + if not os.path.isfile(requirements_file): + requirements_file = os.path.join(script_path, requirements_file) + run_pip(f"install -r \"{requirements_file}\"", "requirements for Web UI") run_extensions_installers(settings_file=args.ui_settings_file)