mirror of
https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
synced 2026-02-04 23:01:32 -08:00
Merge branch 'dev' into extra-networks-performance-updates
This commit is contained in:
commit
0853c2b42c
19 changed files with 331 additions and 74 deletions
|
|
@ -50,7 +50,7 @@ class FaceRestorerCodeFormer(face_restoration_utils.CommonFaceRestoration):
|
|||
|
||||
def restore_face(cropped_face_t):
|
||||
assert self.net is not None
|
||||
return self.net(cropped_face_t, w=w, adain=True)[0]
|
||||
return self.net(cropped_face_t, weight=w, adain=True)[0]
|
||||
|
||||
return self.restore_with_helper(np_image, restore_face)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import sys
|
|||
|
||||
import gradio as gr
|
||||
from modules.paths import data_path
|
||||
from modules import shared, ui_tempdir, script_callbacks, processing, infotext_versions, images, prompt_parser
|
||||
from modules import shared, ui_tempdir, script_callbacks, processing, infotext_versions, images, prompt_parser, errors
|
||||
from PIL import Image
|
||||
|
||||
sys.modules['modules.generation_parameters_copypaste'] = sys.modules[__name__] # alias for old name
|
||||
|
|
@ -488,7 +488,11 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component,
|
|||
|
||||
for output, key in paste_fields:
|
||||
if callable(key):
|
||||
v = key(params)
|
||||
try:
|
||||
v = key(params)
|
||||
except Exception:
|
||||
errors.report(f"Error executing {key}", exc_info=True)
|
||||
v = None
|
||||
else:
|
||||
v = params.get(key, None)
|
||||
|
||||
|
|
|
|||
|
|
@ -131,13 +131,15 @@ def run_postprocessing_webui(id_task, *args, **kwargs):
|
|||
return run_postprocessing(*args, **kwargs)
|
||||
|
||||
|
||||
def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_dir, show_extras_results, gfpgan_visibility, codeformer_visibility, codeformer_weight, upscaling_resize, upscaling_resize_w, upscaling_resize_h, upscaling_crop, extras_upscaler_1, extras_upscaler_2, extras_upscaler_2_visibility, upscale_first: bool, save_output: bool = True):
|
||||
def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_dir, show_extras_results, gfpgan_visibility, codeformer_visibility, codeformer_weight, upscaling_resize, upscaling_resize_w, upscaling_resize_h, upscaling_crop, extras_upscaler_1, extras_upscaler_2, extras_upscaler_2_visibility, upscale_first: bool, save_output: bool = True, max_side_length: int = 0):
|
||||
"""old handler for API"""
|
||||
|
||||
args = scripts.scripts_postproc.create_args_for_run({
|
||||
"Upscale": {
|
||||
"upscale_enabled": True,
|
||||
"upscale_mode": resize_mode,
|
||||
"upscale_by": upscaling_resize,
|
||||
"max_side_length": max_side_length,
|
||||
"upscale_to_width": upscaling_resize_w,
|
||||
"upscale_to_height": upscaling_resize_h,
|
||||
"upscale_crop": upscaling_crop,
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ class Processed:
|
|||
"version": self.version,
|
||||
}
|
||||
|
||||
return json.dumps(obj)
|
||||
return json.dumps(obj, default=lambda o: None)
|
||||
|
||||
def infotext(self, p: StableDiffusionProcessing, index):
|
||||
return create_infotext(p, self.all_prompts, self.all_seeds, self.all_subseeds, comments=[], position_in_batch=index % self.batch_size, iteration=index // self.batch_size)
|
||||
|
|
@ -703,8 +703,54 @@ def program_version():
|
|||
return res
|
||||
|
||||
|
||||
def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0, use_main_prompt=False, index=None, all_negative_prompts=None, all_hr_prompts=None, all_hr_negative_prompts=None):
|
||||
if index is None:
|
||||
def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0, use_main_prompt=False, index=None, all_negative_prompts=None):
|
||||
"""
|
||||
this function is used to generate the infotext that is stored in the generated images, it's contains the parameters that are required to generate the imagee
|
||||
Args:
|
||||
p: StableDiffusionProcessing
|
||||
all_prompts: list[str]
|
||||
all_seeds: list[int]
|
||||
all_subseeds: list[int]
|
||||
comments: list[str]
|
||||
iteration: int
|
||||
position_in_batch: int
|
||||
use_main_prompt: bool
|
||||
index: int
|
||||
all_negative_prompts: list[str]
|
||||
|
||||
Returns: str
|
||||
|
||||
Extra generation params
|
||||
p.extra_generation_params dictionary allows for additional parameters to be added to the infotext
|
||||
this can be use by the base webui or extensions.
|
||||
To add a new entry, add a new key value pair, the dictionary key will be used as the key of the parameter in the infotext
|
||||
the value generation_params can be defined as:
|
||||
- str | None
|
||||
- List[str|None]
|
||||
- callable func(**kwargs) -> str | None
|
||||
|
||||
When defined as a string, it will be used as without extra processing; this is this most common use case.
|
||||
|
||||
Defining as a list allows for parameter that changes across images in the job, for example, the 'Seed' parameter.
|
||||
The list should have the same length as the total number of images in the entire job.
|
||||
|
||||
Defining as a callable function allows parameter cannot be generated earlier or when extra logic is required.
|
||||
For example 'Hires prompt', due to reasons the hr_prompt might be changed by process in the pipeline or extensions
|
||||
and may vary across different images, defining as a static string or list would not work.
|
||||
|
||||
The function takes locals() as **kwargs, as such will have access to variables like 'p' and 'index'.
|
||||
the base signature of the function should be:
|
||||
func(**kwargs) -> str | None
|
||||
optionally it can have additional arguments that will be used in the function:
|
||||
func(p, index, **kwargs) -> str | None
|
||||
note: for better future compatibility even though this function will have access to all variables in the locals(),
|
||||
it is recommended to only use the arguments present in the function signature of create_infotext.
|
||||
For actual implementation examples, see StableDiffusionProcessingTxt2Img.init > get_hr_prompt.
|
||||
"""
|
||||
|
||||
if use_main_prompt:
|
||||
index = 0
|
||||
elif index is None:
|
||||
index = position_in_batch + iteration * p.batch_size
|
||||
|
||||
if all_negative_prompts is None:
|
||||
|
|
@ -715,6 +761,9 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter
|
|||
token_merging_ratio = p.get_token_merging_ratio()
|
||||
token_merging_ratio_hr = p.get_token_merging_ratio(for_hr=True)
|
||||
|
||||
prompt_text = p.main_prompt if use_main_prompt else all_prompts[index]
|
||||
negative_prompt = p.main_negative_prompt if use_main_prompt else all_negative_prompts[index]
|
||||
|
||||
uses_ensd = opts.eta_noise_seed_delta != 0
|
||||
if uses_ensd:
|
||||
uses_ensd = sd_samplers_common.is_sampler_using_eta_noise_seed_delta(p)
|
||||
|
|
@ -747,22 +796,24 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter
|
|||
"RNG": opts.randn_source if opts.randn_source != "GPU" else None,
|
||||
"NGMS": None if p.s_min_uncond == 0 else p.s_min_uncond,
|
||||
"Tiling": "True" if p.tiling else None,
|
||||
"Hires prompt": None, # This is set later, insert here to keep order
|
||||
"Hires negative prompt": None, # This is set later, insert here to keep order
|
||||
**p.extra_generation_params,
|
||||
"Version": program_version() if opts.add_version_to_infotext else None,
|
||||
"User": p.user if opts.add_user_name_to_info else None,
|
||||
}
|
||||
|
||||
if all_hr_prompts := all_hr_prompts or getattr(p, 'all_hr_prompts', None):
|
||||
generation_params['Hires prompt'] = all_hr_prompts[index] if all_hr_prompts[index] != all_prompts[index] else None
|
||||
if all_hr_negative_prompts := all_hr_negative_prompts or getattr(p, 'all_hr_negative_prompts', None):
|
||||
generation_params['Hires negative prompt'] = all_hr_negative_prompts[index] if all_hr_negative_prompts[index] != all_negative_prompts[index] else None
|
||||
for key, value in generation_params.items():
|
||||
try:
|
||||
if isinstance(value, list):
|
||||
generation_params[key] = value[index]
|
||||
elif callable(value):
|
||||
generation_params[key] = value(**locals())
|
||||
except Exception:
|
||||
errors.report(f'Error creating infotext for key "{key}"', exc_info=True)
|
||||
generation_params[key] = None
|
||||
|
||||
generation_params_text = ", ".join([k if k == v else f'{k}: {infotext_utils.quote(v)}' for k, v in generation_params.items() if v is not None])
|
||||
|
||||
prompt_text = p.main_prompt if use_main_prompt else all_prompts[index]
|
||||
negative_prompt_text = f"\nNegative prompt: {p.main_negative_prompt if use_main_prompt else all_negative_prompts[index]}" if all_negative_prompts[index] else ""
|
||||
negative_prompt_text = f"\nNegative prompt: {negative_prompt}" if negative_prompt else ""
|
||||
|
||||
return f"{prompt_text}{negative_prompt_text}\n{generation_params_text}".strip()
|
||||
|
||||
|
|
@ -1204,6 +1255,17 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
|||
if self.hr_sampler_name is not None and self.hr_sampler_name != self.sampler_name:
|
||||
self.extra_generation_params["Hires sampler"] = self.hr_sampler_name
|
||||
|
||||
def get_hr_prompt(p, index, prompt_text, **kwargs):
|
||||
hr_prompt = p.all_hr_prompts[index]
|
||||
return hr_prompt if hr_prompt != prompt_text else None
|
||||
|
||||
def get_hr_negative_prompt(p, index, negative_prompt, **kwargs):
|
||||
hr_negative_prompt = p.all_hr_negative_prompts[index]
|
||||
return hr_negative_prompt if hr_negative_prompt != negative_prompt else None
|
||||
|
||||
self.extra_generation_params["Hires prompt"] = get_hr_prompt
|
||||
self.extra_generation_params["Hires negative prompt"] = get_hr_negative_prompt
|
||||
|
||||
self.extra_generation_params["Hires schedule type"] = None # to be set in sd_samplers_kdiffusion.py
|
||||
|
||||
if self.hr_scheduler is None:
|
||||
|
|
|
|||
|
|
@ -439,6 +439,9 @@ def remove_current_script_callbacks():
|
|||
for callback_list in callback_map.values():
|
||||
for callback_to_remove in [cb for cb in callback_list if cb.script == filename]:
|
||||
callback_list.remove(callback_to_remove)
|
||||
for ordered_callbacks_list in ordered_callbacks_map.values():
|
||||
for callback_to_remove in [cb for cb in ordered_callbacks_list if cb.script == filename]:
|
||||
ordered_callbacks_list.remove(callback_to_remove)
|
||||
|
||||
|
||||
def remove_callbacks_for_function(callback_func):
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ import os
|
|||
import importlib.util
|
||||
|
||||
from modules import errors
|
||||
import sys
|
||||
|
||||
|
||||
loaded_scripts = {}
|
||||
|
||||
|
||||
def load_module(path):
|
||||
|
|
@ -9,6 +13,11 @@ def load_module(path):
|
|||
module = importlib.util.module_from_spec(module_spec)
|
||||
module_spec.loader.exec_module(module)
|
||||
|
||||
loaded_scripts[path] = module
|
||||
|
||||
module_name, _ = os.path.splitext(os.path.basename(path))
|
||||
sys.modules["scripts." + module_name] = module
|
||||
|
||||
return module
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -739,12 +739,17 @@ class ScriptRunner:
|
|||
def onload_script_visibility(params):
|
||||
title = params.get('Script', None)
|
||||
if title:
|
||||
title_index = self.titles.index(title)
|
||||
visibility = title_index == self.script_load_ctr
|
||||
self.script_load_ctr = (self.script_load_ctr + 1) % len(self.titles)
|
||||
return gr.update(visible=visibility)
|
||||
else:
|
||||
return gr.update(visible=False)
|
||||
try:
|
||||
title_index = self.titles.index(title)
|
||||
visibility = title_index == self.script_load_ctr
|
||||
self.script_load_ctr = (self.script_load_ctr + 1) % len(self.titles)
|
||||
return gr.update(visible=visibility)
|
||||
except ValueError:
|
||||
params['Script'] = None
|
||||
massage = f'Cannot find Script: "{title}"'
|
||||
print(massage)
|
||||
gr.Warning(massage)
|
||||
return gr.update(visible=False)
|
||||
|
||||
self.infotext_fields.append((dropdown, lambda x: gr.update(value=x.get('Script', 'None'))))
|
||||
self.infotext_fields.extend([(script.group, onload_script_visibility) for script in self.selectable_scripts])
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ class ScriptPostprocessingRunner:
|
|||
self.initialize_scripts(modules.scripts.postprocessing_scripts_data)
|
||||
|
||||
scripts_order = shared.opts.postprocessing_operation_order
|
||||
scripts_filter_out = set(shared.opts.postprocessing_disable_in_extras)
|
||||
|
||||
def script_score(name):
|
||||
for i, possible_match in enumerate(scripts_order):
|
||||
|
|
@ -151,9 +152,10 @@ class ScriptPostprocessingRunner:
|
|||
|
||||
return len(self.scripts)
|
||||
|
||||
script_scores = {script.name: (script_score(script.name), script.order, script.name, original_index) for original_index, script in enumerate(self.scripts)}
|
||||
filtered_scripts = [script for script in self.scripts if script.name not in scripts_filter_out]
|
||||
script_scores = {script.name: (script_score(script.name), script.order, script.name, original_index) for original_index, script in enumerate(filtered_scripts)}
|
||||
|
||||
return sorted(self.scripts, key=lambda x: script_scores[x.name])
|
||||
return sorted(filtered_scripts, key=lambda x: script_scores[x.name])
|
||||
|
||||
def setup_ui(self):
|
||||
inputs = []
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import collections
|
||||
import os.path
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
|
||||
|
|
@ -7,7 +7,6 @@ import torch
|
|||
import re
|
||||
import safetensors.torch
|
||||
from omegaconf import OmegaConf, ListConfig
|
||||
from os import mkdir
|
||||
from urllib import request
|
||||
import ldm.modules.midas as midas
|
||||
|
||||
|
|
@ -151,7 +150,7 @@ def list_models():
|
|||
if shared.cmd_opts.no_download_sd_model or cmd_ckpt != shared.sd_model_file or os.path.exists(cmd_ckpt):
|
||||
model_url = None
|
||||
else:
|
||||
model_url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors"
|
||||
model_url = f"{shared.hf_endpoint}/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors"
|
||||
|
||||
model_list = modelloader.load_models(model_path=model_path, model_url=model_url, command_path=shared.cmd_opts.ckpt_dir, ext_filter=[".ckpt", ".safetensors"], download_name="v1-5-pruned-emaonly.safetensors", ext_blacklist=[".vae.ckpt", ".vae.safetensors"])
|
||||
|
||||
|
|
@ -508,7 +507,7 @@ def enable_midas_autodownload():
|
|||
path = midas.api.ISL_PATHS[model_type]
|
||||
if not os.path.exists(path):
|
||||
if not os.path.exists(midas_path):
|
||||
mkdir(midas_path)
|
||||
os.mkdir(midas_path)
|
||||
|
||||
print(f"Downloading midas model weights for {model_type} to {path}")
|
||||
request.urlretrieve(midas_urls[model_type], path)
|
||||
|
|
@ -787,6 +786,13 @@ def reuse_model_from_already_loaded(sd_model, checkpoint_info, timer):
|
|||
Additionally deletes loaded models that are over the limit set in settings (sd_checkpoints_limit).
|
||||
"""
|
||||
|
||||
if sd_model is not None and sd_model.sd_checkpoint_info.filename == checkpoint_info.filename:
|
||||
return sd_model
|
||||
|
||||
if shared.opts.sd_checkpoints_keep_in_cpu:
|
||||
send_model_to_cpu(sd_model)
|
||||
timer.record("send model to cpu")
|
||||
|
||||
already_loaded = None
|
||||
for i in reversed(range(len(model_data.loaded_sd_models))):
|
||||
loaded_model = model_data.loaded_sd_models[i]
|
||||
|
|
@ -796,14 +802,10 @@ def reuse_model_from_already_loaded(sd_model, checkpoint_info, timer):
|
|||
|
||||
if len(model_data.loaded_sd_models) > shared.opts.sd_checkpoints_limit > 0:
|
||||
print(f"Unloading model {len(model_data.loaded_sd_models)} over the limit of {shared.opts.sd_checkpoints_limit}: {loaded_model.sd_checkpoint_info.title}")
|
||||
model_data.loaded_sd_models.pop()
|
||||
del model_data.loaded_sd_models[i]
|
||||
send_model_to_trash(loaded_model)
|
||||
timer.record("send model to trash")
|
||||
|
||||
if shared.opts.sd_checkpoints_keep_in_cpu:
|
||||
send_model_to_cpu(sd_model)
|
||||
timer.record("send model to cpu")
|
||||
|
||||
if already_loaded is not None:
|
||||
send_model_to_device(already_loaded)
|
||||
timer.record("send model to device")
|
||||
|
|
|
|||
|
|
@ -90,3 +90,5 @@ list_checkpoint_tiles = shared_items.list_checkpoint_tiles
|
|||
refresh_checkpoints = shared_items.refresh_checkpoints
|
||||
list_samplers = shared_items.list_samplers
|
||||
reload_hypernetworks = shared_items.reload_hypernetworks
|
||||
|
||||
hf_endpoint = os.getenv('HF_ENDPOINT', 'https://huggingface.co')
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ restricted_opts = {
|
|||
"outdir_grids",
|
||||
"outdir_txt2img_grids",
|
||||
"outdir_save",
|
||||
"outdir_init_images"
|
||||
"outdir_init_images",
|
||||
"temp_dir",
|
||||
"clean_temp_dir_at_start",
|
||||
}
|
||||
|
||||
categories.register_category("saving", "Saving images")
|
||||
|
|
@ -384,6 +386,7 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters"
|
|||
|
||||
options_templates.update(options_section(('postprocessing', "Postprocessing", "postprocessing"), {
|
||||
'postprocessing_enable_in_main_ui': OptionInfo([], "Enable postprocessing operations in txt2img and img2img tabs", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
|
||||
'postprocessing_disable_in_extras': OptionInfo([], "Disable postprocessing operations in extras tab", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
|
||||
'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
|
||||
'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
|
||||
'postprocessing_existing_caption_action': OptionInfo("Ignore", "Action for existing captions", gr.Radio, {"choices": ["Ignore", "Keep", "Prepend", "Append"]}).info("when generating captions using postprocessing; Ignore = use generated; Keep = use original; Prepend/Append = combine both"),
|
||||
|
|
|
|||
|
|
@ -3,13 +3,10 @@ import dataclasses
|
|||
import json
|
||||
import html
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import gradio as gr
|
||||
import subprocess as sp
|
||||
|
||||
from modules import call_queue, shared, ui_tempdir
|
||||
from modules import call_queue, shared, ui_tempdir, util
|
||||
from modules.infotext_utils import image_from_url_text
|
||||
import modules.images
|
||||
from modules.ui_components import ToolButton
|
||||
|
|
@ -176,31 +173,7 @@ def create_output_panel(tabname, outdir, toprow=None):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
if not os.path.exists(f):
|
||||
msg = f'Folder "{f}" does not exist. After you create an image, the folder will be created.'
|
||||
print(msg)
|
||||
gr.Info(msg)
|
||||
return
|
||||
elif not os.path.isdir(f):
|
||||
msg = f"""
|
||||
WARNING
|
||||
An open_folder request was made with an argument that is not a folder.
|
||||
This could be an error or a malicious attempt to run code on your computer.
|
||||
Requested path was: {f}
|
||||
"""
|
||||
print(msg, file=sys.stderr)
|
||||
gr.Warning(msg)
|
||||
return
|
||||
|
||||
path = os.path.normpath(f)
|
||||
if platform.system() == "Windows":
|
||||
os.startfile(path)
|
||||
elif platform.system() == "Darwin":
|
||||
sp.Popen(["open", path])
|
||||
elif "microsoft-standard-WSL2" in platform.uname().release:
|
||||
sp.Popen(["wsl-open", path])
|
||||
else:
|
||||
sp.Popen(["xdg-open", path])
|
||||
util.open_folder(f)
|
||||
|
||||
with gr.Column(elem_id=f"{tabname}_results"):
|
||||
if toprow:
|
||||
|
|
|
|||
|
|
@ -58,8 +58,9 @@ def apply_and_restart(disable_list, update_list, disable_all):
|
|||
|
||||
def save_config_state(name):
|
||||
current_config_state = config_states.get_config()
|
||||
if not name:
|
||||
name = "Config"
|
||||
|
||||
name = os.path.basename(name or "Config")
|
||||
|
||||
current_config_state["name"] = name
|
||||
timestamp = datetime.now().strftime('%Y_%m_%d-%H_%M_%S')
|
||||
filename = os.path.join(config_states_dir, f"{timestamp}_{name}.json")
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ class Upscaler:
|
|||
if img.width >= dest_w and img.height >= dest_h:
|
||||
break
|
||||
|
||||
if shared.state.interrupted:
|
||||
break
|
||||
|
||||
shape = (img.width, img.height)
|
||||
|
||||
img = self.do_upscale(img, selected_model)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ def upscale_with_model(
|
|||
for y, h, row in grid.tiles:
|
||||
newrow = []
|
||||
for x, w, tile in row:
|
||||
if shared.state.interrupted:
|
||||
return img
|
||||
output = upscale_pil_patch(model, tile)
|
||||
scale_factor = output.width // tile.width
|
||||
newrow.append([x * scale_factor, w * scale_factor, output])
|
||||
|
|
|
|||
|
|
@ -148,6 +148,11 @@ class MassFileLister:
|
|||
"""Clear the cache of all directories."""
|
||||
self.cached_dirs.clear()
|
||||
|
||||
def update_file_entry(self, path):
|
||||
"""Update the cache for a specific directory."""
|
||||
dirname, filename = os.path.split(path)
|
||||
if cached_dir := self.cached_dirs.get(dirname):
|
||||
cached_dir.update_entry(filename)
|
||||
|
||||
def topological_sort(dependencies):
|
||||
"""Accepts a dictionary mapping name to its dependencies, returns a list of names ordered according to dependencies.
|
||||
|
|
@ -171,3 +176,38 @@ def topological_sort(dependencies):
|
|||
inner(depname)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def open_folder(path):
|
||||
"""Open a folder in the file manager of the respect OS."""
|
||||
# import at function level to avoid potential issues
|
||||
import gradio as gr
|
||||
import platform
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
if not os.path.exists(path):
|
||||
msg = f'Folder "{path}" does not exist. after you save an image, the folder will be created.'
|
||||
print(msg)
|
||||
gr.Info(msg)
|
||||
return
|
||||
elif not os.path.isdir(path):
|
||||
msg = f"""
|
||||
WARNING
|
||||
An open_folder request was made with an path that is not a folder.
|
||||
This could be an error or a malicious attempt to run code on your computer.
|
||||
Requested path was: {path}
|
||||
"""
|
||||
print(msg, file=sys.stderr)
|
||||
gr.Warning(msg)
|
||||
return
|
||||
|
||||
path = os.path.normpath(path)
|
||||
if platform.system() == "Windows":
|
||||
os.startfile(path)
|
||||
elif platform.system() == "Darwin":
|
||||
subprocess.Popen(["open", path])
|
||||
elif "microsoft-standard-WSL2" in platform.uname().release:
|
||||
subprocess.Popen(["wsl-open", path])
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", path])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue