diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 483e0de1..5b9cded6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -21,6 +21,7 @@ body: 5. Try a fresh installation of Fooocus in a different directory - see if a clean installation solves the issue Before making a issue report please, check that the issue hasn't been reported recently. options: + - label: The issue has not been resolved by following the [troubleshooting guide](https://github.com/lllyasviel/Fooocus/blob/main/troubleshoot.md) - label: The issue exists on a clean installation of Fooocus - label: The issue exists in the current version of Fooocus - label: The issue has not been reported before recently diff --git a/Dockerfile b/Dockerfile index 2aea2810..b969cd0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN apt-get update -y && \ COPY requirements_docker.txt requirements_versions.txt /tmp/ RUN pip install --no-cache-dir -r /tmp/requirements_docker.txt -r /tmp/requirements_versions.txt && \ rm -f /tmp/requirements_docker.txt /tmp/requirements_versions.txt -RUN pip install --no-cache-dir xformers==0.0.22 --no-dependencies +RUN pip install --no-cache-dir xformers==0.0.23 --no-dependencies RUN curl -fsL -o /usr/local/lib/python3.10/dist-packages/gradio/frpc_linux_amd64_v0.2 https://cdn-media.huggingface.co/frpc-gradio-0.2/frpc_linux_amd64 && \ chmod +x /usr/local/lib/python3.10/dist-packages/gradio/frpc_linux_amd64_v0.2 diff --git a/args_manager.py b/args_manager.py index 32a3f0d7..6a3ae9dc 100644 --- a/args_manager.py +++ b/args_manager.py @@ -52,7 +52,4 @@ if args_parser.args.disable_analytics: if args_parser.args.disable_in_browser: args_parser.args.in_browser = False -if args_parser.args.temp_path is None: - args_parser.args.temp_path = os.path.join(gettempdir(), 'Fooocus') - args = args_parser.args diff --git a/css/style.css b/css/style.css index 62600069..c702a725 100644 --- a/css/style.css +++ b/css/style.css @@ -1,5 +1,136 @@ /* based on https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/v1.6.0/style.css */ +.loader-container { + display: flex; /* Use flex to align items horizontally */ + align-items: center; /* Center items vertically within the container */ + white-space: nowrap; /* Prevent line breaks within the container */ +} + +.loader { + border: 8px solid #f3f3f3; /* Light grey */ + border-top: 8px solid #3498db; /* Blue */ + border-radius: 50%; + width: 30px; + height: 30px; + animation: spin 2s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Style the progress bar */ +progress { + appearance: none; /* Remove default styling */ + height: 20px; /* Set the height of the progress bar */ + border-radius: 5px; /* Round the corners of the progress bar */ + background-color: #f3f3f3; /* Light grey background */ + width: 100%; +} + +/* Style the progress bar container */ +.progress-container { + margin-left: 20px; + margin-right: 20px; + flex-grow: 1; /* Allow the progress container to take up remaining space */ +} + +/* Set the color of the progress bar fill */ +progress::-webkit-progress-value { + background-color: #3498db; /* Blue color for the fill */ +} + +progress::-moz-progress-bar { + background-color: #3498db; /* Blue color for the fill in Firefox */ +} + +/* Style the text on the progress bar */ +progress::after { + content: attr(value '%'); /* Display the progress value followed by '%' */ + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; /* Set text color */ + font-size: 14px; /* Set font size */ +} + +/* Style other texts */ +.loader-container > span { + margin-left: 5px; /* Add spacing between the progress bar and the text */ +} + +.progress-bar > .generating { + display: none !important; +} + +.progress-bar{ + height: 30px !important; +} + +.type_row{ + height: 80px !important; +} + +.type_row_half{ + height: 32px !important; +} + +.scroll-hide{ + resize: none !important; +} + +.refresh_button{ + border: none !important; + background: none !important; + font-size: none !important; + box-shadow: none !important; +} + +.advanced_check_row{ + width: 250px !important; +} + +.min_check{ + min-width: min(1px, 100%) !important; +} + +.resizable_area { + resize: vertical; + overflow: auto !important; +} + +.aspect_ratios label { + width: 140px !important; +} + +.aspect_ratios label span { + white-space: nowrap !important; +} + +.aspect_ratios label input { + margin-left: -5px !important; +} + +.lora_enable label { + height: 100%; +} + +.lora_enable label input { + margin: auto; +} + +.lora_enable label span { + display: none; +} + +@-moz-document url-prefix() { + .lora_weight input[type=number] { + width: 80px; + } +} + #context-menu{ z-index:9999; position:absolute; @@ -217,4 +348,49 @@ #stylePreviewOverlay.lower-half { transform: translate(-140px, -140px); +} + +/* scrollable box for style selections */ +.contain .tabs { + height: 100%; +} + +.contain .tabs .tabitem.style_selections_tab { + height: 100%; +} + +.contain .tabs .tabitem.style_selections_tab > div:first-child { + height: 100%; +} + +.contain .tabs .tabitem.style_selections_tab .style_selections { + min-height: 200px; + height: 100%; +} + +.contain .tabs .tabitem.style_selections_tab .style_selections .wrap[data-testid="checkbox-group"] { + position: absolute; /* remove this to disable scrolling within the checkbox-group */ + overflow: auto; + padding-right: 2px; + max-height: 100%; +} + +.contain .tabs .tabitem.style_selections_tab .style_selections .wrap[data-testid="checkbox-group"] label { + /* max-width: calc(35% - 15px) !important; */ /* add this to enable 3 columns layout */ + flex: calc(50% - 5px) !important; +} + +.contain .tabs .tabitem.style_selections_tab .style_selections .wrap[data-testid="checkbox-group"] label span { + /* white-space:nowrap; */ /* add this to disable text wrapping (better choice for 3 columns layout) */ + overflow: hidden; + text-overflow: ellipsis; +} + +/* styles preview tooltip */ +.preview-tooltip { + background-color: #fff8; + font-family: monospace; + text-align: center; + border-radius-top: 5px; + display: none; /* remove this to enable tooltip in preview image */ } \ No newline at end of file diff --git a/fooocus_colab.ipynb b/fooocus_colab.ipynb index f6a989e9..de392916 100644 --- a/fooocus_colab.ipynb +++ b/fooocus_colab.ipynb @@ -35,7 +35,7 @@ { "cell_type": "code", "source": [ - "!python entry_with_update.py --share --attention-split --disable-offload-from-vram --always-high-vram" + "!python entry_with_update.py --share --always-high-vram" ], "metadata": { "id": "gPZEYpsfMDxQ" diff --git a/fooocus_version.py b/fooocus_version.py index 448ea944..404c9223 100644 --- a/fooocus_version.py +++ b/fooocus_version.py @@ -1 +1 @@ -version = '2.2.1 (mashb1t)' +version = '2.3.0 (mashb1t)' diff --git a/javascript/script.js b/javascript/script.js index 8f4cac58..9aa0b5c1 100644 --- a/javascript/script.js +++ b/javascript/script.js @@ -150,9 +150,12 @@ function initStylePreviewOverlay() { let overlayVisible = false; const samplesPath = document.querySelector("meta[name='samples-path']").getAttribute("content") const overlay = document.createElement('div'); + const tooltip = document.createElement('div'); + tooltip.className = 'preview-tooltip'; + overlay.appendChild(tooltip); overlay.id = 'stylePreviewOverlay'; document.body.appendChild(overlay); - document.addEventListener('mouseover', function(e) { + document.addEventListener('mouseover', function (e) { const label = e.target.closest('.style_selections label'); if (!label) return; label.removeEventListener("mouseout", onMouseLeave); @@ -162,9 +165,12 @@ function initStylePreviewOverlay() { const originalText = label.querySelector("span").getAttribute("data-original-text"); const name = originalText || label.querySelector("span").textContent; overlay.style.backgroundImage = `url("${samplesPath.replace( - "fooocus_v2", - name.toLowerCase().replaceAll(" ", "_") + "fooocus_v2", + name.toLowerCase().replaceAll(" ", "_") ).replaceAll("\\", "\\\\")}")`; + + tooltip.textContent = name; + function onMouseLeave() { overlayVisible = false; overlay.style.opacity = "0"; @@ -172,8 +178,8 @@ function initStylePreviewOverlay() { label.removeEventListener("mouseout", onMouseLeave); } }); - document.addEventListener('mousemove', function(e) { - if(!overlayVisible) return; + document.addEventListener('mousemove', function (e) { + if (!overlayVisible) return; overlay.style.left = `${e.clientX}px`; overlay.style.top = `${e.clientY}px`; overlay.className = e.clientY > window.innerHeight / 2 ? "lower-half" : "upper-half"; diff --git a/language/en.json b/language/en.json index 20e224db..11816e78 100644 --- a/language/en.json +++ b/language/en.json @@ -47,9 +47,12 @@ "Generate mask from image": "Generate mask from image", "Setting": "Setting", "Style": "Style", + "Preset": "Preset", "Performance": "Performance", "Speed": "Speed", "Quality": "Quality", + "Extreme Speed": "Extreme Speed", + "Lightning": "Lightning", "Aspect Ratios": "Aspect Ratios", "width \u00d7 height": "width \u00d7 height", "Image Number": "Image Number", @@ -60,6 +63,7 @@ "Seed": "Seed", "Disable seed increment": "Disable seed increment", "Disable automatic seed increment when image number is > 1.": "Disable automatic seed increment when image number is > 1.", + "Read wildcards in order": "Read wildcards in order", "\ud83d\udcda History Log": "\uD83D\uDCDA History Log", "Image Style": "Image Style", "Fooocus V2": "Fooocus V2", @@ -377,7 +381,6 @@ "B2": "B2", "S1": "S1", "S2": "S2", - "Extreme Speed": "Extreme Speed", "\uD83D\uDD0E Type here to search styles ...": "\uD83D\uDD0E Type here to search styles ...", "Type prompt here.": "Type prompt here.", "Outpaint Expansion Direction:": "Outpaint Expansion Direction:", @@ -391,5 +394,6 @@ "Metadata Scheme": "Metadata Scheme", "Image Prompt parameters are not included. Use png and a1111 for compatibility with Civitai.": "Image Prompt parameters are not included. Use png and a1111 for compatibility with Civitai.", "fooocus (json)": "fooocus (json)", - "a1111 (plain text)": "a1111 (plain text)" + "a1111 (plain text)": "a1111 (plain text)", + "Unsupported image type in input": "Unsupported image type in input" } \ No newline at end of file diff --git a/launch.py b/launch.py index 778fb3ab..79416761 100644 --- a/launch.py +++ b/launch.py @@ -1,6 +1,6 @@ import os -import sys import ssl +import sys print('[System ARGV] ' + str(sys.argv)) @@ -15,12 +15,11 @@ if "GRADIO_SERVER_PORT" not in os.environ: ssl._create_default_https_context = ssl._create_unverified_context - import platform import fooocus_version 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, delete_folder_content from modules.model_loader import load_file_from_url from modules import config @@ -42,7 +41,7 @@ def prepare_environment(): if TRY_INSTALL_XFORMERS: if REINSTALL_ALL or not is_installed("xformers"): - xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20') + xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.23') if platform.system() == "Windows": if platform.python_version().startswith("3.10"): run_pip(f"install -U -I --no-deps {xformers_package}", "xformers", live=True) @@ -68,6 +67,7 @@ vae_approx_filenames = [ 'https://huggingface.co/lllyasviel/misc/resolve/main/xl-to-v1_interposer-v3.1.safetensors') ] + def ini_args(): from args_manager import args return args @@ -84,6 +84,15 @@ if args.gpu_device_id is not None: from modules import config os.environ["U2NET_HOME"] = config.path_inpaint +os.environ['GRADIO_TEMP_DIR'] = config.temp_path + +if config.temp_path_cleanup_on_launch: + print(f'[Cleanup] Attempting to delete content of temp dir {config.temp_path}') + result = delete_folder_content(config.temp_path, '[Cleanup] ') + if result: + print("[Cleanup] Cleanup successful") + else: + print(f"[Cleanup] Failed to delete content of temp dir.") def download_models(default_model, previous_default_models, checkpoint_downloads, embeddings_downloads, lora_downloads): for file_name, url in vae_approx_filenames: diff --git a/modules/async_worker.py b/modules/async_worker.py index ca3a70a0..681c2ca7 100644 --- a/modules/async_worker.py +++ b/modules/async_worker.py @@ -1,5 +1,6 @@ import threading import os +import re from modules.patch import PatchSettings, patch_settings, patch_all patch_all() @@ -49,8 +50,8 @@ def worker(): from modules.sdxl_styles import apply_style, apply_wildcards, fooocus_expansion, apply_arrays from modules.private_logger import log from extras.expansion import safe_str - 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 + 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_enabled_loras from modules.upscaler import perform_upscale from modules.flags import Performance from modules.meta_parser import get_metadata_parser, MetadataScheme @@ -131,14 +132,6 @@ def worker(): async_task.results = async_task.results + [wall] return - def apply_enabled_loras(loras): - enabled_loras = [] - for lora_enabled, lora_model, lora_weight in loras: - if lora_enabled: - enabled_loras.append([lora_model, lora_weight]) - - return enabled_loras - @torch.no_grad() @torch.inference_mode() def handler(async_task): @@ -157,12 +150,13 @@ def worker(): image_number = args.pop() output_format = args.pop() image_seed = args.pop() + read_wildcards_in_order = args.pop() sharpness = args.pop() guidance_scale = args.pop() base_model_name = args.pop() refiner_model_name = args.pop() refiner_switch = args.pop() - loras = apply_enabled_loras([[bool(args.pop()), str(args.pop()), float(args.pop()), ] for _ in range(modules.config.default_max_lora_number)]) + loras = get_enabled_loras([[bool(args.pop()), str(args.pop()), float(args.pop())] for _ in range(modules.config.default_max_lora_number)]) input_image_checkbox = args.pop() current_tab = args.pop() uov_method = args.pop() @@ -385,7 +379,7 @@ def worker(): print(f'[Inpaint] Current inpaint model is {inpaint_patch_model_path}') if refiner_model_name == 'None': use_synthetic_refiner = True - refiner_switch = 0.5 + refiner_switch = 0.8 else: inpaint_head_model_path, inpaint_patch_model_path = None, None print(f'[Inpaint] Parameterized inpaint is disabled.') @@ -460,16 +454,16 @@ def worker(): for i in range(image_number): if disable_seed_increment: - task_seed = seed + task_seed = seed % (constants.MAX_SEED + 1) else: task_seed = (seed + i) % (constants.MAX_SEED + 1) # randint is inclusive, % is not task_rng = random.Random(task_seed) # may bind to inpaint noise in the future - task_prompt = apply_wildcards(prompt, task_rng) + task_prompt = apply_wildcards(prompt, task_rng, i, read_wildcards_in_order) task_prompt = apply_arrays(task_prompt, i) - task_negative_prompt = apply_wildcards(negative_prompt, task_rng) - task_extra_positive_prompts = [apply_wildcards(pmt, task_rng) for pmt in extra_positive_prompts] - task_extra_negative_prompts = [apply_wildcards(pmt, task_rng) for pmt in extra_negative_prompts] + task_negative_prompt = apply_wildcards(negative_prompt, task_rng, i, read_wildcards_in_order) + task_extra_positive_prompts = [apply_wildcards(pmt, task_rng, i, read_wildcards_in_order) for pmt in extra_positive_prompts] + task_extra_negative_prompts = [apply_wildcards(pmt, task_rng, i, read_wildcards_in_order) for pmt in extra_negative_prompts] positive_basic_workloads = [] negative_basic_workloads = [] @@ -827,7 +821,7 @@ def worker(): try: if async_task.last_stop is not False: - ldm_patched.model_management.interrupt_current_processing() + ldm_patched.modules.model_management.interrupt_current_processing() positive_cond, negative_cond = task['c'], task['uc'] if 'cn' in goals: @@ -902,7 +896,7 @@ def worker(): d.append(('Sampler', 'sampler', sampler_name)) d.append(('Scheduler', 'scheduler', scheduler_name)) - d.append(('Seed', 'seed', task['task_seed'])) + d.append(('Seed', 'seed', str(task['task_seed']))) if freeu_enabled: d.append(('FreeU', 'freeu', str((freeu_b1, freeu_b2, freeu_s1, freeu_s2)))) diff --git a/modules/config.py b/modules/config.py index 1266033f..4987c437 100644 --- a/modules/config.py +++ b/modules/config.py @@ -3,12 +3,14 @@ import json import math import numbers import args_manager +import tempfile import modules.flags import modules.sdxl_styles from modules.model_loader import load_file_from_url from modules.util import get_files_from_folder, makedirs_with_log -from modules.flags import Performance, MetadataScheme +from modules.flags import OutputFormat, Performance, MetadataScheme + def get_config_path(key, default_value): env = os.getenv(key) @@ -18,6 +20,7 @@ def get_config_path(key, default_value): else: return os.path.abspath(default_value) + config_path = get_config_path('config_path', "./config.txt") config_example_path = get_config_path('config_example_path', "config_modification_tutorial.txt") config_dict = {} @@ -94,7 +97,7 @@ def try_load_deprecated_user_path_config(): try_load_deprecated_user_path_config() -def list_presets(): +def get_presets(): preset_folder = 'presets' presets = ['initial'] if not os.path.exists(preset_folder): @@ -103,11 +106,11 @@ def list_presets(): return presets + [f[:f.index(".json")] for f in os.listdir(preset_folder) if f.endswith('.json')] -available_presets = list_presets() +available_presets = get_presets() def update_presets(): global available_presets - available_presets = list_presets() + available_presets = get_presets() def try_get_preset_content(preset): if isinstance(preset, str): @@ -125,26 +128,16 @@ def try_get_preset_content(preset): print(e) return {} -def try_load_preset_global(preset): - global config_dict - - if isinstance(preset, str): - preset_path = os.path.abspath(f'./presets/{preset}.json') - try: - if os.path.exists(preset_path): - with open(preset_path, "r", encoding="utf-8") as json_file: - config_dict.update(json.load(json_file)) - print(f'Loaded preset: {preset_path}') - else: - raise FileNotFoundError - except Exception as e: - print(f'Load preset [{preset_path}] failed') - print(e) - +try: + with open(os.path.abspath(f'./presets/default.json'), "r", encoding="utf-8") as json_file: + config_dict.update(json.load(json_file)) +except Exception as e: + print(f'Load default preset failed.') + print(e) +available_presets = get_presets() preset = args_manager.args.preset -try_load_preset_global(preset) - +config_dict.update(try_get_preset_content(preset)) def get_path_output() -> str: """ @@ -153,7 +146,7 @@ def get_path_output() -> str: global config_dict path_output = get_dir_or_set_default('path_outputs', '../outputs/', make_directory=True) if args_manager.args.output_path: - print(f'[CONFIG] Overriding config value path_outputs with {args_manager.args.output_path}') + print(f'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 @@ -213,6 +206,7 @@ path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlne 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_safety_checker_models = get_dir_or_set_default('path_safety_checker_models', '../models/safety_checker_models/') +path_wildcards = get_dir_or_set_default('path_wildcards', '../wildcards/') path_outputs = get_path_output() def get_config_item_or_set_default(key, default_value, validator, disable_empty_as_none=False): @@ -243,6 +237,36 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_ return default_value +def init_temp_path(path: str | None, default_path: str) -> str: + if args_manager.args.temp_path: + path = args_manager.args.temp_path + + if path != '' and path != default_path: + try: + if not os.path.isabs(path): + path = os.path.abspath(path) + os.makedirs(path, exist_ok=True) + print(f'Using temp path {path}') + return path + except Exception as e: + print(f'Could not create temp path {path}. Reason: {e}') + print(f'Using default temp path {default_path} instead.') + + os.makedirs(default_path, exist_ok=True) + return default_path + + +default_temp_path = os.path.join(tempfile.gettempdir(), 'fooocus') +temp_path = init_temp_path(get_config_item_or_set_default( + key='temp_path', + default_value=default_temp_path, + validator=lambda x: isinstance(x, str), +), default_temp_path) +temp_path_cleanup_on_launch = get_config_item_or_set_default( + key='temp_path_cleanup_on_launch', + default_value=True, + validator=lambda x: isinstance(x, bool) +) default_base_model_name = default_model = get_config_item_or_set_default( key='default_model', default_value='model.safetensors', @@ -277,28 +301,37 @@ default_loras = get_config_item_or_set_default( key='default_loras', default_value=[ [ + True, "None", 1.0 ], [ + True, "None", 1.0 ], [ + True, "None", 1.0 ], [ + True, "None", 1.0 ], [ + True, "None", 1.0 ] ], - validator=lambda x: isinstance(x, list) and all(len(y) == 2 and isinstance(y[0], str) and isinstance(y[1], numbers.Number) for y in x) + validator=lambda x: isinstance(x, list) and all( + len(y) == 3 and isinstance(y[0], bool) and isinstance(y[1], str) and isinstance(y[2], numbers.Number) + or len(y) == 2 and isinstance(y[0], str) and isinstance(y[1], numbers.Number) + for y in x) ) +default_loras = [(y[0], y[1], y[2]) if len(y) == 3 else (True, y[0], y[1]) for y in default_loras] default_max_lora_number = get_config_item_or_set_default( key='default_max_lora_number', default_value=len(default_loras) if isinstance(default_loras, list) and len(default_loras) > 0 else 5, @@ -363,7 +396,7 @@ default_max_image_number = get_config_item_or_set_default( default_output_format = get_config_item_or_set_default( key='default_output_format', default_value='png', - validator=lambda x: x in modules.flags.output_formats + validator=lambda x: x in OutputFormat.list() ) default_image_number = get_config_item_or_set_default( key='default_image_number', @@ -540,22 +573,27 @@ with open(config_example_path, "w", encoding="utf-8") as json_file: model_filenames = [] lora_filenames = [] +wildcard_filenames = [] + sdxl_lcm_lora = 'sdxl_lcm_lora.safetensors' sdxl_lightning_lora = 'sdxl_lightning_4step_lora.safetensors' -def get_model_filenames(folder_paths, name_filter=None): - extensions = ['.pth', '.ckpt', '.bin', '.safetensors', '.fooocus.patch'] +def get_model_filenames(folder_paths, extensions=None, name_filter=None): + if extensions is None: + extensions = ['.pth', '.ckpt', '.bin', '.safetensors', '.fooocus.patch'] files = [] for folder in folder_paths: files += get_files_from_folder(folder, extensions, name_filter) return files -def update_all_model_names(): - global model_filenames, lora_filenames +def update_files(): + global model_filenames, lora_filenames, wildcard_filenames, available_presets model_filenames = get_model_filenames(paths_checkpoints) lora_filenames = get_model_filenames(paths_loras) + wildcard_filenames = get_files_from_folder(path_wildcards, ['.txt']) + available_presets = get_presets() return @@ -679,4 +717,4 @@ def downloading_upscale_model(): return os.path.join(path_upscale_models, 'fooocus_upscaler_s409985e5.bin') -update_all_model_names() +update_files() diff --git a/modules/core.py b/modules/core.py index bfc44966..38ee8e8d 100644 --- a/modules/core.py +++ b/modules/core.py @@ -73,14 +73,14 @@ class StableDiffusionModel: loras_to_load = [] - for name, weight in loras: - if name == 'None': + for filename, weight in loras: + if filename == 'None': continue - if os.path.exists(name): - lora_filename = name + if os.path.exists(filename): + lora_filename = filename else: - lora_filename = get_file_from_folder_list(name, modules.config.paths_loras) + lora_filename = get_file_from_folder_list(filename, modules.config.paths_loras) if not os.path.exists(lora_filename): print(f'Lora file not found: {lora_filename}') diff --git a/modules/default_pipeline.py b/modules/default_pipeline.py index f8edfae1..190601ec 100644 --- a/modules/default_pipeline.py +++ b/modules/default_pipeline.py @@ -11,7 +11,7 @@ from extras.expansion import FooocusExpansion from ldm_patched.modules.model_base import SDXL, SDXLRefiner from modules.sample_hijack import clip_separate -from modules.util import get_file_from_folder_list +from modules.util import get_file_from_folder_list, get_enabled_loras model_base = core.StableDiffusionModel() @@ -254,7 +254,7 @@ def refresh_everything(refiner_model_name, base_model_name, loras, refresh_everything( refiner_model_name=modules.config.default_refiner_model_name, base_model_name=modules.config.default_base_model_name, - loras=modules.config.default_loras + loras=get_enabled_loras(modules.config.default_loras) ) diff --git a/modules/flags.py b/modules/flags.py index df11c8d7..d3f558c5 100644 --- a/modules/flags.py +++ b/modules/flags.py @@ -67,7 +67,7 @@ default_parameters = { cn_ip: (0.5, 0.6), cn_ip_face: (0.9, 0.75), cn_canny: (0.5, 1.0), cn_cpds: (0.5, 1.0) } # stop, weight -output_formats = ['png', 'jpg', 'webp'] +output_formats = ['png', 'jpeg', 'webp'] inpaint_mask_models = ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta', 'isnet-general-use', 'isnet-anime', 'sam'] inpaint_mask_cloth_category = ['full', 'upper', 'lower'] @@ -96,6 +96,16 @@ metadata_scheme = [ controlnet_image_count = 4 +class OutputFormat(Enum): + PNG = 'png' + JPEG = 'jpeg' + WEBP = 'webp' + + @classmethod + def list(cls) -> list: + return list(map(lambda c: c.value, cls)) + + class Steps(IntEnum): QUALITY = 60 SPEED = 30 @@ -120,6 +130,12 @@ class Performance(Enum): def list(cls) -> list: return list(map(lambda c: c.value, cls)) + @classmethod + def has_restricted_features(cls, x) -> bool: + if isinstance(x, Performance): + x = x.value + return x in [cls.EXTREME_SPEED.value, cls.LIGHTNING.value] + def steps(self) -> int | None: return Steps[self.name].value if Steps[self.name] else None diff --git a/modules/gradio_hijack.py b/modules/gradio_hijack.py index 181429ec..35df81c0 100644 --- a/modules/gradio_hijack.py +++ b/modules/gradio_hijack.py @@ -17,7 +17,7 @@ from gradio_client.documentation import document, set_documentation_group from gradio_client.serializing import ImgSerializable from PIL import Image as _Image # using _ to minimize namespace pollution -from gradio import processing_utils, utils +from gradio import processing_utils, utils, Error from gradio.components.base import IOComponent, _Keywords, Block from gradio.deprecation import warn_style_method_deprecation from gradio.events import ( @@ -275,7 +275,10 @@ class Image( x, mask = x["image"], x["mask"] assert isinstance(x, str) - im = processing_utils.decode_base64_to_image(x) + try: + im = processing_utils.decode_base64_to_image(x) + except PIL.UnidentifiedImageError: + raise Error("Unsupported image type in input") with warnings.catch_warnings(): warnings.simplefilter("ignore") im = im.convert(self.image_mode) diff --git a/modules/html.py b/modules/html.py index 769151a9..25771cb9 100644 --- a/modules/html.py +++ b/modules/html.py @@ -1,136 +1,3 @@ -css = ''' -.loader-container { - display: flex; /* Use flex to align items horizontally */ - align-items: center; /* Center items vertically within the container */ - white-space: nowrap; /* Prevent line breaks within the container */ -} - -.loader { - border: 8px solid #f3f3f3; /* Light grey */ - border-top: 8px solid #3498db; /* Blue */ - border-radius: 50%; - width: 30px; - height: 30px; - animation: spin 2s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -/* Style the progress bar */ -progress { - appearance: none; /* Remove default styling */ - height: 20px; /* Set the height of the progress bar */ - border-radius: 5px; /* Round the corners of the progress bar */ - background-color: #f3f3f3; /* Light grey background */ - width: 100%; -} - -/* Style the progress bar container */ -.progress-container { - margin-left: 20px; - margin-right: 20px; - flex-grow: 1; /* Allow the progress container to take up remaining space */ -} - -/* Set the color of the progress bar fill */ -progress::-webkit-progress-value { - background-color: #3498db; /* Blue color for the fill */ -} - -progress::-moz-progress-bar { - background-color: #3498db; /* Blue color for the fill in Firefox */ -} - -/* Style the text on the progress bar */ -progress::after { - content: attr(value '%'); /* Display the progress value followed by '%' */ - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: white; /* Set text color */ - font-size: 14px; /* Set font size */ -} - -/* Style other texts */ -.loader-container > span { - margin-left: 5px; /* Add spacing between the progress bar and the text */ -} - -.progress-bar > .generating { - display: none !important; -} - -.progress-bar{ - height: 30px !important; -} - -.type_row{ - height: 80px !important; -} - -.type_row_half{ - height: 32px !important; -} - -.scroll-hide{ - resize: none !important; -} - -.refresh_button{ - border: none !important; - background: none !important; - font-size: none !important; - box-shadow: none !important; -} - -.advanced_check_row{ - width: 250px !important; -} - -.min_check{ - min-width: min(1px, 100%) !important; -} - -.resizable_area { - resize: vertical; - overflow: auto !important; -} - -.aspect_ratios label { - width: 140px !important; -} - -.aspect_ratios label span { - white-space: nowrap !important; -} - -.aspect_ratios label input { - margin-left: -5px !important; -} - -.lora_enable label { - height: 100%; -} - -.lora_enable label input { - margin: auto; -} - -.lora_enable label span { - display: none; -} - -@-moz-document url-prefix() { - .lora_weight input[type=number] { - width: 80px; - } -} - -''' progress_html = '''