Merge branch 'lllyasviel:main' into main

This commit is contained in:
Guido Bartoli 2024-02-12 09:15:47 +01:00 committed by GitHub
commit cf99255d2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 64 additions and 39 deletions

View File

@ -9,10 +9,10 @@ assignees: ''
**Read Troubleshoot** **Read Troubleshoot**
[x] I admit that I have read the [Troubleshoot](https://github.com/lllyasviel/Fooocus/blob/main/troubleshoot.md) before making this issue. [x] I confirm that I have read the [Troubleshoot](https://github.com/lllyasviel/Fooocus/blob/main/troubleshoot.md) guide before making this issue.
**Describe the problem** **Describe the problem**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
**Full Console Log** **Full Console Log**
Paste **full** console log here. You will make our job easier if you give a **full** log. Paste the **full** console log here. You will make our job easier if you give a **full** log.

View File

@ -1 +1 @@
version = '2.1.864' version = '2.1.865'

View File

@ -154,12 +154,8 @@ let cancelGenerateForever = function() {
let generateOnRepeatForButtons = function() { let generateOnRepeatForButtons = function() {
generateOnRepeat('#generate_button', '#stop_button'); generateOnRepeat('#generate_button', '#stop_button');
}; };
appendContextMenuOption('#generate_button', 'Generate forever', generateOnRepeatForButtons); appendContextMenuOption('#generate_button', 'Generate forever', generateOnRepeatForButtons);
// appendContextMenuOption('#stop_button', 'Generate forever', generateOnRepeatForButtons);
// appendContextMenuOption('#stop_button', 'Cancel generate forever', cancelGenerateForever);
// appendContextMenuOption('#generate_button', 'Cancel generate forever', cancelGenerateForever);
})(); })();
//End example Context Menu Items //End example Context Menu Items

View File

@ -10,6 +10,7 @@ os.chdir(root)
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
os.environ["PYTORCH_MPS_HIGH_WATERMARK_RATIO"] = "0.0" os.environ["PYTORCH_MPS_HIGH_WATERMARK_RATIO"] = "0.0"
if "GRADIO_SERVER_PORT" not in os.environ:
os.environ["GRADIO_SERVER_PORT"] = "7865" os.environ["GRADIO_SERVER_PORT"] = "7865"
ssl._create_default_https_context = ssl._create_unverified_context ssl._create_default_https_context = ssl._create_unverified_context
@ -21,7 +22,6 @@ import fooocus_version
from build_launcher import build_launcher from build_launcher import build_launcher
from modules.launch_util import is_installed, run, python, run_pip, requirements_met from modules.launch_util import is_installed, run, python, run_pip, requirements_met
from modules.model_loader import load_file_from_url from modules.model_loader import load_file_from_url
from modules import config
REINSTALL_ALL = False REINSTALL_ALL = False
@ -84,6 +84,8 @@ if args.gpu_device_id is not None:
print("Set device to:", args.gpu_device_id) print("Set device to:", args.gpu_device_id)
from modules import config
def download_models(): def download_models():
for file_name, url in vae_approx_filenames: for file_name, url in vae_approx_filenames:
load_file_from_url(url=url, model_dir=config.path_vae_approx, file_name=file_name) load_file_from_url(url=url, model_dir=config.path_vae_approx, file_name=file_name)

View File

@ -78,7 +78,7 @@ def spatial_gradient(input, normalized: bool = True):
Return: Return:
the derivatives of the input feature map. with shape :math:`(B, C, 2, H, W)`. the derivatives of the input feature map. with shape :math:`(B, C, 2, H, W)`.
.. note:: .. note::
See a working example `here <https://kornia-tutorials.readthedocs.io/en/latest/ See a working example `here <https://kornia.readthedocs.io/en/latest/
filtering_edges.html>`__. filtering_edges.html>`__.
Examples: Examples:
>>> input = torch.rand(1, 3, 4, 4) >>> input = torch.rand(1, 3, 4, 4)
@ -120,7 +120,7 @@ def rgb_to_grayscale(image, rgb_weights = None):
grayscale version of the image with shape :math:`(*,1,H,W)`. grayscale version of the image with shape :math:`(*,1,H,W)`.
.. note:: .. note::
See a working example `here <https://kornia-tutorials.readthedocs.io/en/latest/ See a working example `here <https://kornia.readthedocs.io/en/latest/
color_conversions.html>`__. color_conversions.html>`__.
Example: Example:
@ -176,7 +176,7 @@ def canny(
- the canny edge magnitudes map, shape of :math:`(B,1,H,W)`. - the canny edge magnitudes map, shape of :math:`(B,1,H,W)`.
- the canny edge detection filtered by thresholds and hysteresis, shape of :math:`(B,1,H,W)`. - the canny edge detection filtered by thresholds and hysteresis, shape of :math:`(B,1,H,W)`.
.. note:: .. note::
See a working example `here <https://kornia-tutorials.readthedocs.io/en/latest/ See a working example `here <https://kornia.readthedocs.io/en/latest/
canny.html>`__. canny.html>`__.
Example: Example:
>>> input = torch.rand(5, 3, 4, 4) >>> input = torch.rand(5, 3, 4, 4)

View File

@ -3,8 +3,6 @@ import math
import ldm_patched.modules.utils import ldm_patched.modules.utils
def lcm(a, b): #TODO: eventually replace by math.lcm (added in python3.9)
return abs(a*b) // math.gcd(a, b)
class CONDRegular: class CONDRegular:
def __init__(self, cond): def __init__(self, cond):
@ -41,7 +39,7 @@ class CONDCrossAttn(CONDRegular):
if s1[0] != s2[0] or s1[2] != s2[2]: #these 2 cases should not happen if s1[0] != s2[0] or s1[2] != s2[2]: #these 2 cases should not happen
return False return False
mult_min = lcm(s1[1], s2[1]) mult_min = math.lcm(s1[1], s2[1])
diff = mult_min // min(s1[1], s2[1]) diff = mult_min // min(s1[1], s2[1])
if diff > 4: #arbitrary limit on the padding because it's probably going to impact performance negatively if it's too much if diff > 4: #arbitrary limit on the padding because it's probably going to impact performance negatively if it's too much
return False return False
@ -52,7 +50,7 @@ class CONDCrossAttn(CONDRegular):
crossattn_max_len = self.cond.shape[1] crossattn_max_len = self.cond.shape[1]
for x in others: for x in others:
c = x.cond c = x.cond
crossattn_max_len = lcm(crossattn_max_len, c.shape[1]) crossattn_max_len = math.lcm(crossattn_max_len, c.shape[1])
conds.append(c) conds.append(c)
out = [] out = []

View File

@ -14,7 +14,7 @@ from .timm.weight_init import trunc_normal_
def drop_path(x, drop_prob: float = 0.0, training: bool = False): def drop_path(x, drop_prob: float = 0.0, training: bool = False):
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/drop.py From: https://github.com/huggingface/pytorch-image-models/blob/main/timm/layers/drop.py
""" """
if drop_prob == 0.0 or not training: if drop_prob == 0.0 or not training:
return x return x
@ -30,7 +30,7 @@ def drop_path(x, drop_prob: float = 0.0, training: bool = False):
class DropPath(nn.Module): class DropPath(nn.Module):
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/drop.py From: https://github.com/huggingface/pytorch-image-models/blob/main/timm/layers/drop.py
""" """
def __init__(self, drop_prob=None): def __init__(self, drop_prob=None):

View File

@ -13,7 +13,7 @@ import torch.nn.functional as F
from . import block as B from . import block as B
# Borrowed from https://github.com/rlaphoenix/VSGAN/blob/master/vsgan/archs/ESRGAN.py # Borrowed from https://github.com/rlaphoenix/VSGAN/blob/master/vsgan/archs/esrgan.py
# Which enhanced stuff that was already here # Which enhanced stuff that was already here
class RRDBNet(nn.Module): class RRDBNet(nn.Module):
def __init__( def __init__(

View File

@ -2,7 +2,7 @@
Modified from https://github.com/sczhou/CodeFormer Modified from https://github.com/sczhou/CodeFormer
VQGAN code, adapted from the original created by the Unleashing Transformers authors: VQGAN code, adapted from the original created by the Unleashing Transformers authors:
https://github.com/samb-t/unleashing-transformers/blob/master/models/vqgan.py https://github.com/samb-t/unleashing-transformers/blob/master/models/vqgan.py
This verison of the arch specifically was gathered from an old version of GFPGAN. If this is a problem, please contact me. This version of the arch specifically was gathered from an old version of GFPGAN. If this is a problem, please contact me.
""" """
import math import math
from typing import Optional from typing import Optional

View File

@ -40,7 +40,7 @@ def worker():
from modules.private_logger import log from modules.private_logger import log
from extras.expansion import safe_str from extras.expansion import safe_str
from modules.util import remove_empty_str, HWC3, resize_image, \ from modules.util import remove_empty_str, HWC3, resize_image, \
get_image_shape_ceil, set_image_shape_ceil, get_shape_ceil, resample_image, erode_or_dilate get_image_shape_ceil, set_image_shape_ceil, get_shape_ceil, resample_image, erode_or_dilate, ordinal_suffix
from modules.upscaler import perform_upscale from modules.upscaler import perform_upscale
try: try:
@ -335,11 +335,11 @@ def worker():
ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_path) ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_path)
ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_face_path) ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_face_path)
switch = int(round(steps * refiner_switch))
if advanced_parameters.overwrite_step > 0: if advanced_parameters.overwrite_step > 0:
steps = advanced_parameters.overwrite_step steps = advanced_parameters.overwrite_step
switch = int(round(steps * refiner_switch))
if advanced_parameters.overwrite_switch > 0: if advanced_parameters.overwrite_switch > 0:
switch = advanced_parameters.overwrite_switch switch = advanced_parameters.overwrite_switch
@ -732,8 +732,7 @@ def worker():
done_steps = current_task_id * steps + step done_steps = current_task_id * steps + step
async_task.yields.append(['preview', ( async_task.yields.append(['preview', (
int(15.0 + 85.0 * float(done_steps) / float(all_steps)), int(15.0 + 85.0 * float(done_steps) / float(all_steps)),
f'Step {step}/{total_steps} in the {current_task_id + 1}-th Sampling', f'Step {step}/{total_steps} in the {current_task_id + 1}{ordinal_suffix(current_task_id + 1)} Sampling', y)])
y)])
for current_task_id, task in enumerate(tasks): for current_task_id, task in enumerate(tasks):
execution_start_time = time.perf_counter() execution_start_time = time.perf_counter()

View File

@ -102,6 +102,18 @@ if isinstance(preset, str):
print(e) print(e)
def get_path_output() -> str:
"""
Checking output path argument and overriding default path.
"""
global config_dict
path_output = get_dir_or_set_default('path_outputs', '../outputs/')
if args_manager.args.output_path:
print(f'[CONFIG] Overriding config value path_outputs with {args_manager.args.output_path}')
config_dict['path_outputs'] = path_output = args_manager.args.output_path
return path_output
def get_dir_or_set_default(key, default_value): def get_dir_or_set_default(key, default_value):
global config_dict, visited_keys, always_save_keys global config_dict, visited_keys, always_save_keys
@ -132,7 +144,7 @@ path_inpaint = get_dir_or_set_default('path_inpaint', '../models/inpaint/')
path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlnet/') path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlnet/')
path_clip_vision = get_dir_or_set_default('path_clip_vision', '../models/clip_vision/') path_clip_vision = get_dir_or_set_default('path_clip_vision', '../models/clip_vision/')
path_fooocus_expansion = get_dir_or_set_default('path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion') path_fooocus_expansion = get_dir_or_set_default('path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion')
path_outputs = get_dir_or_set_default('path_outputs', '../outputs/') path_outputs = get_path_output()
def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False): def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False):

View File

@ -15,7 +15,7 @@ from packaging.requirements import Requirement
logging.getLogger("torch.distributed.nn").setLevel(logging.ERROR) # sshh... logging.getLogger("torch.distributed.nn").setLevel(logging.ERROR) # sshh...
logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage()) logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage())
re_requirement = re.compile(r"\s*([-_a-zA-Z0-9]+)\s*(?:==\s*([-+_.a-zA-Z0-9]+))?\s*") re_requirement = re.compile(r"\s*([-\w]+)\s*(?:==\s*([-+.\w]+))?\s*")
python = sys.executable python = sys.executable
default_command_live = (os.environ.get('LAUNCH_LIVE_OUTPUT') == "1") default_command_live = (os.environ.get('LAUNCH_LIVE_OUTPUT') == "1")

View File

@ -68,7 +68,7 @@ def log(img, dic):
</script>""" </script>"""
) )
begin_part = f"<html><head><title>Fooocus Log {date_string}</title>{css_styles}</head><body>{js}<p>Fooocus Log {date_string} (private)</p>\n<p>All images are clean, without any hidden data/meta, and safe to share with others.</p><!--fooocus-log-split-->\n\n" begin_part = f"<!DOCTYPE html><html><head><title>Fooocus Log {date_string}</title>{css_styles}</head><body>{js}<p>Fooocus Log {date_string} (private)</p>\n<p>All images are clean, without any hidden data/meta, and safe to share with others.</p><!--fooocus-log-split-->\n\n"
end_part = f'\n<!--fooocus-log-split--></body></html>' end_part = f'\n<!--fooocus-log-split--></body></html>'
middle_part = log_cache.get(html_name, "") middle_part = log_cache.get(html_name, "")
@ -83,15 +83,15 @@ def log(img, dic):
div_name = only_name.replace('.', '_') div_name = only_name.replace('.', '_')
item = f"<div id=\"{div_name}\" class=\"image-container\"><hr><table><tr>\n" item = f"<div id=\"{div_name}\" class=\"image-container\"><hr><table><tr>\n"
item += f"<td><a href=\"{only_name}\" target=\"_blank\"><img src='{only_name}' onerror=\"this.closest('.image-container').style.display='none';\" loading='lazy'></img></a><div>{only_name}</div></td>" item += f"<td><a href=\"{only_name}\" target=\"_blank\"><img src='{only_name}' onerror=\"this.closest('.image-container').style.display='none';\" loading='lazy'/></a><div>{only_name}</div></td>"
item += "<td><table class='metadata'>" item += "<td><table class='metadata'>"
for key, value in dic: for key, value in dic:
value_txt = str(value).replace('\n', ' </br> ') value_txt = str(value).replace('\n', ' <br/> ')
item += f"<tr><td class='key'>{key}</td><td class='value'>{value_txt}</td></tr>\n" item += f"<tr><td class='key'>{key}</td><td class='value'>{value_txt}</td></tr>\n"
item += "</table>" item += "</table>"
js_txt = urllib.parse.quote(json.dumps({k: v for k, v in dic}, indent=0), safe='') js_txt = urllib.parse.quote(json.dumps({k: v for k, v in dic}, indent=0), safe='')
item += f"</br><button onclick=\"to_clipboard('{js_txt}')\">Copy to Clipboard</button>" item += f"<br/><button onclick=\"to_clipboard('{js_txt}')\">Copy to Clipboard</button>"
item += "</td>" item += "</td>"
item += "</tr></table></div>\n\n" item += "</tr></table></div>\n\n"

View File

@ -164,14 +164,18 @@ def get_files_from_folder(folder_path, exensions=None, name_filter=None):
filenames = [] filenames = []
for root, dirs, files in os.walk(folder_path): for root, dirs, files in os.walk(folder_path, topdown=False):
relative_path = os.path.relpath(root, folder_path) relative_path = os.path.relpath(root, folder_path)
if relative_path == ".": if relative_path == ".":
relative_path = "" relative_path = ""
for filename in files: for filename in sorted(files):
_, file_extension = os.path.splitext(filename) _, file_extension = os.path.splitext(filename)
if (exensions == None or file_extension.lower() in exensions) and (name_filter == None or name_filter in _): if (exensions == None or file_extension.lower() in exensions) and (name_filter == None or name_filter in _):
path = os.path.join(relative_path, filename) path = os.path.join(relative_path, filename)
filenames.append(path) filenames.append(path)
return sorted(filenames, key=lambda x: -1 if os.sep in x else 1) return filenames
def ordinal_suffix(number: int) -> str:
return 'th' if 10 <= number % 100 <= 20 else {1: 'st', 2: 'nd', 3: 'rd'}.get(number % 10, 'th')

View File

@ -281,6 +281,13 @@ Given different goals, the default models and configs of Fooocus are different:
Note that the download is **automatic** - you do not need to do anything if the internet connection is okay. However, you can download them manually if you (or move them from somewhere else) have your own preparation. Note that the download is **automatic** - you do not need to do anything if the internet connection is okay. However, you can download them manually if you (or move them from somewhere else) have your own preparation.
## UI Access and Authentication
In addition to running on localhost, Fooocus can also expose its UI in two ways:
* Local UI listener: use `--listen` (specify port e.g. with `--port 8888`).
* API access: use `--share` (registers an endpoint at `.gradio.live`).
In both ways the access is unauthenticated by default. You can add basic authentication by creating a file called `auth.json` in the main directory, which contains a list of JSON objects with the keys `user` and `pass` (see example in [auth-example.json](./auth-example.json)).
## List of "Hidden" Tricks ## List of "Hidden" Tricks
<a name="tech_list"></a> <a name="tech_list"></a>
@ -288,7 +295,7 @@ The below things are already inside the software, and **users do not need to do
1. GPT2-based [prompt expansion as a dynamic style "Fooocus V2".](https://github.com/lllyasviel/Fooocus/discussions/117#raw) (similar to Midjourney's hidden pre-processsing and "raw" mode, or the LeonardoAI's Prompt Magic). 1. GPT2-based [prompt expansion as a dynamic style "Fooocus V2".](https://github.com/lllyasviel/Fooocus/discussions/117#raw) (similar to Midjourney's hidden pre-processsing and "raw" mode, or the LeonardoAI's Prompt Magic).
2. Native refiner swap inside one single k-sampler. The advantage is that the refiner model can now reuse the base model's momentum (or ODE's history parameters) collected from k-sampling to achieve more coherent sampling. In Automatic1111's high-res fix and ComfyUI's node system, the base model and refiner use two independent k-samplers, which means the momentum is largely wasted, and the sampling continuity is broken. Fooocus uses its own advanced k-diffusion sampling that ensures seamless, native, and continuous swap in a refiner setup. (Update Aug 13: Actually, I discussed this with Automatic1111 several days ago, and it seems that the “native refiner swap inside one single k-sampler” is [merged]( https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12371) into the dev branch of webui. Great!) 2. Native refiner swap inside one single k-sampler. The advantage is that the refiner model can now reuse the base model's momentum (or ODE's history parameters) collected from k-sampling to achieve more coherent sampling. In Automatic1111's high-res fix and ComfyUI's node system, the base model and refiner use two independent k-samplers, which means the momentum is largely wasted, and the sampling continuity is broken. Fooocus uses its own advanced k-diffusion sampling that ensures seamless, native, and continuous swap in a refiner setup. (Update Aug 13: Actually, I discussed this with Automatic1111 several days ago, and it seems that the “native refiner swap inside one single k-sampler” is [merged]( https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12371) into the dev branch of webui. Great!)
3. Negative ADM guidance. Because the highest resolution level of XL Base does not have cross attentions, the positive and negative signals for XL's highest resolution level cannot receive enough contrasts during the CFG sampling, causing the results to look a bit plastic or overly smooth in certain cases. Fortunately, since the XL's highest resolution level is still conditioned on image aspect ratios (ADM), we can modify the adm on the positive/negative side to compensate for the lack of CFG contrast in the highest resolution level. (Update Aug 16, the IOS App [Drawing Things](https://apps.apple.com/us/app/draw-things-ai-generation/id6444050820) will support Negative ADM Guidance. Great!) 3. Negative ADM guidance. Because the highest resolution level of XL Base does not have cross attentions, the positive and negative signals for XL's highest resolution level cannot receive enough contrasts during the CFG sampling, causing the results to look a bit plastic or overly smooth in certain cases. Fortunately, since the XL's highest resolution level is still conditioned on image aspect ratios (ADM), we can modify the adm on the positive/negative side to compensate for the lack of CFG contrast in the highest resolution level. (Update Aug 16, the IOS App [Draw Things](https://apps.apple.com/us/app/draw-things-ai-generation/id6444050820) will support Negative ADM Guidance. Great!)
4. We implemented a carefully tuned variation of Section 5.1 of ["Improving Sample Quality of Diffusion Models Using Self-Attention Guidance"](https://arxiv.org/pdf/2210.00939.pdf). The weight is set to very low, but this is Fooocus's final guarantee to make sure that the XL will never yield an overly smooth or plastic appearance (examples [here](https://github.com/lllyasviel/Fooocus/discussions/117#sharpness)). This can almost eliminate all cases for which XL still occasionally produces overly smooth results, even with negative ADM guidance. (Update 2023 Aug 18, the Gaussian kernel of SAG is changed to an anisotropic kernel for better structure preservation and fewer artifacts.) 4. We implemented a carefully tuned variation of Section 5.1 of ["Improving Sample Quality of Diffusion Models Using Self-Attention Guidance"](https://arxiv.org/pdf/2210.00939.pdf). The weight is set to very low, but this is Fooocus's final guarantee to make sure that the XL will never yield an overly smooth or plastic appearance (examples [here](https://github.com/lllyasviel/Fooocus/discussions/117#sharpness)). This can almost eliminate all cases for which XL still occasionally produces overly smooth results, even with negative ADM guidance. (Update 2023 Aug 18, the Gaussian kernel of SAG is changed to an anisotropic kernel for better structure preservation and fewer artifacts.)
5. We modified the style templates a bit and added the "cinematic-default". 5. We modified the style templates a bit and added the "cinematic-default".
6. We tested the "sd_xl_offset_example-lora_1.0.safetensors" and it seems that when the lora weight is below 0.5, the results are always better than XL without lora. 6. We tested the "sd_xl_offset_example-lora_1.0.safetensors" and it seems that when the lora weight is below 0.5, the results are always better than XL without lora.

View File

@ -255,8 +255,14 @@ with shared.gradio_root:
seed_random.change(random_checked, inputs=[seed_random], outputs=[image_seed], seed_random.change(random_checked, inputs=[seed_random], outputs=[image_seed],
queue=False, show_progress=False) queue=False, show_progress=False)
if not args_manager.args.disable_image_log: def update_history_link():
gr.HTML(f'<a href="file={get_current_html_path()}" target="_blank">\U0001F4DA History Log</a>') if args_manager.args.disable_image_log:
return gr.update(value='')
return gr.update(value=f'<a href="file={get_current_html_path()}" target="_blank">\U0001F4DA History Log</a>')
history_link = gr.HTML()
shared.gradio_root.load(update_history_link, outputs=history_link, queue=False, show_progress=False)
with gr.Tab(label='Style'): with gr.Tab(label='Style'):
style_sorter.try_load_sorted_styles( style_sorter.try_load_sorted_styles(
@ -586,6 +592,7 @@ with shared.gradio_root:
.then(fn=generate_clicked, inputs=ctrls, outputs=[progress_html, progress_window, progress_gallery, gallery]) \ .then(fn=generate_clicked, inputs=ctrls, outputs=[progress_html, progress_window, progress_gallery, gallery]) \
.then(lambda: (gr.update(visible=True, interactive=True), gr.update(visible=False, interactive=False), gr.update(visible=False, interactive=False), False), .then(lambda: (gr.update(visible=True, interactive=True), gr.update(visible=False, interactive=False), gr.update(visible=False, interactive=False), False),
outputs=[generate_button, stop_button, skip_button, state_is_generating]) \ outputs=[generate_button, stop_button, skip_button, state_is_generating]) \
.then(fn=update_history_link, outputs=history_link) \
.then(fn=lambda: None, _js='playNotification').then(fn=lambda: None, _js='refresh_grid_delayed') .then(fn=lambda: None, _js='playNotification').then(fn=lambda: None, _js='refresh_grid_delayed')
for notification_file in ['notification.ogg', 'notification.mp3']: for notification_file in ['notification.ogg', 'notification.mp3']:
@ -618,6 +625,6 @@ shared.gradio_root.launch(
server_name=args_manager.args.listen, server_name=args_manager.args.listen,
server_port=args_manager.args.port, server_port=args_manager.args.port,
share=args_manager.args.share, share=args_manager.args.share,
auth=check_auth if args_manager.args.share and auth_enabled else None, auth=check_auth if (args_manager.args.share or args_manager.args.listen) and auth_enabled else None,
blocked_paths=[constants.AUTH_FILENAME] blocked_paths=[constants.AUTH_FILENAME]
) )