Upgrade to v1.0.6
This commit is contained in:
+9
-1
@@ -29,7 +29,15 @@ After installing the node package, the UI interface will be automatically switch
|
||||
|
||||
## Changelog
|
||||
|
||||
**v1.0.5 (2024-02-07)**
|
||||
**v1.0.6 (2024-02-16)**
|
||||
|
||||
- Added `easy XYInputs: Checkpoint`
|
||||
- Added `easy XYInputs: Lora`
|
||||
- `easy seed` can manually switch the random seed when increasing the fixed seed value
|
||||
- Fixed `easy fullLoader` and all loaders to automatically adjust the node size when switching LoRa
|
||||
- Removed the original ttn image saving logic and adapted to the default image saving format extension of ComfyUI
|
||||
|
||||
- **v1.0.5**
|
||||
|
||||
- Added `easy isSDXL`
|
||||
- Added prompt word control on `easy svdLoader`, which can be used with open_clip model
|
||||
|
||||
@@ -37,7 +37,15 @@
|
||||
|
||||
## 更新日志
|
||||
|
||||
**v1.0.5 (2024-02-07)**
|
||||
**v1.0.6 (2024-02-16)**
|
||||
|
||||
- 增加 `easy XYInputs: Checkpoint`
|
||||
- 增加 `easy XYInputs: Lora`
|
||||
- `easy seed` 增加固定种子值时可手动切换随机种
|
||||
- 修复 `easy fullLoader`等加载器切换lora时自动调整节点大小的问题
|
||||
- 去除原有ttn的图片保存逻辑并适配ComfyUI默认的图片保存格式化扩展
|
||||
|
||||
**v1.0.5**
|
||||
|
||||
- 增加 `easy isSDXL`
|
||||
- `easy svdLoader` 增加提示词控制, 可配合open_clip模型进行使用
|
||||
|
||||
+1
-1
@@ -87,4 +87,4 @@ WEB_DIRECTORY = "./web"
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', "WEB_DIRECTORY"]
|
||||
|
||||
|
||||
print('\033[34mComfy-Easy-Use (v1.0.5): \033[92mLoaded\033[0m')
|
||||
print('\033[34mComfy-Easy-Use (v1.0.6): \033[92mLoaded\033[0m')
|
||||
+275
-210
@@ -28,7 +28,7 @@ from typing import Dict, List, Optional, Tuple, Union, Any
|
||||
from .adv_encode import advanced_encode, advanced_encode_XL
|
||||
|
||||
from server import PromptServer
|
||||
from nodes import VAELoader, MAX_RESOLUTION, RepeatLatentBatch, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS, ConditioningSetMask, ConditioningConcat
|
||||
from nodes import VAELoader, MAX_RESOLUTION, RepeatLatentBatch, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS, ConditioningSetMask, ConditioningConcat, PreviewImage, SaveImage
|
||||
from comfy_extras.nodes_mask import LatentCompositeMasked
|
||||
from .config import BASE_RESOLUTIONS, RESOURCES_DIR, INPAINT_DIR, FOOOCUS_STYLES_DIR, FOOOCUS_INPAINT_HEAD, FOOOCUS_INPAINT_PATCH
|
||||
from .log import log_node_info, log_node_error, log_node_warn, log_node_success
|
||||
@@ -196,8 +196,7 @@ class easyLoader:
|
||||
output_clipvision = True if load_vision else False
|
||||
if config_name not in [None, "Default"]:
|
||||
config_path = folder_paths.get_full_path("configs", config_name)
|
||||
loaded_ckpt = comfy.sd.load_checkpoint(config_path, ckpt_path, output_vae=True, output_clip=output_clip, output_clipvision=output_clipvision,
|
||||
embedding_directory=folder_paths.get_folder_paths("embeddings"))
|
||||
loaded_ckpt = comfy.sd.load_checkpoint(config_path, ckpt_path, output_vae=True, output_clip=output_clip, embedding_directory=folder_paths.get_folder_paths("embeddings"))
|
||||
else:
|
||||
loaded_ckpt = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=output_clip, output_clipvision=output_clipvision, embedding_directory=folder_paths.get_folder_paths("embeddings"))
|
||||
|
||||
@@ -493,6 +492,9 @@ class easyXYPlot:
|
||||
if "ControlNet" in value_type:
|
||||
value_label = f"ControlNet {index + 1}"
|
||||
|
||||
if value_type in ['Lora', 'Checkpoint']:
|
||||
value_label = f"{os.path.basename(os.path.splitext(value.split(',')[0])[0])}"
|
||||
|
||||
if value_type in ["ModelMergeBlocks"]:
|
||||
if ":" in value:
|
||||
line = value.split(':')
|
||||
@@ -733,6 +735,101 @@ class easyXYPlot:
|
||||
if plot_image_vars['clip_skip'] != 0:
|
||||
clip.clip_layer(plot_image_vars['clip_skip'])
|
||||
|
||||
# CheckPoint
|
||||
if self.x_type == "Checkpoint" or self.y_type == "Checkpoint":
|
||||
xy_values = x_value if self.x_type == "Checkpoint" else y_value
|
||||
ckpt_name, clip_skip, vae_name = xy_values.split(",")
|
||||
ckpt_name = ckpt_name.replace('*', ',')
|
||||
vae_name = vae_name.replace('*', ',')
|
||||
model, clip, vae = easyCache.load_checkpoint(ckpt_name)
|
||||
if vae_name != 'None':
|
||||
vae = easyCache.load_vae(vae_name)
|
||||
|
||||
# 如果存在lora_stack叠加lora
|
||||
optional_lora_stack = plot_image_vars['lora_stack']
|
||||
if optional_lora_stack is not None and optional_lora_stack != []:
|
||||
for lora in optional_lora_stack:
|
||||
lora_name = lora["lora_name"]
|
||||
model = model if model is not None else lora["model"]
|
||||
clip = clip if clip is not None else lora["clip"]
|
||||
lora_model_strength = lora["lora_model_strength"]
|
||||
lora_clip_strength = lora["lora_clip_strength"]
|
||||
if "lbw" in lora:
|
||||
lbw = lora["lbw"]
|
||||
lbw_a = lora["lbw_a"]
|
||||
lbw_b = lora["lbw_b"]
|
||||
cls = ALL_NODE_CLASS_MAPPINGS['LoraLoaderBlockWeight //Inspire']
|
||||
model, clip, _ = cls().doit(model, clip, lora_name, lora_model_strength,
|
||||
lora_clip_strength, False, 0,
|
||||
lbw_a, lbw_b, "", lbw)
|
||||
model, clip = easyCache.load_lora(lora_name, model, clip, lora_model_strength,
|
||||
lora_clip_strength)
|
||||
# 处理clip
|
||||
clip = clip.clone()
|
||||
if clip_skip != 'None':
|
||||
clip.clip_layer(int(clip_skip))
|
||||
positive = plot_image_vars['positive']
|
||||
negative = plot_image_vars['negative']
|
||||
if plot_image_vars['a1111_prompt_style']:
|
||||
if "smZ CLIPTextEncode" in ALL_NODE_CLASS_MAPPINGS:
|
||||
cls = ALL_NODE_CLASS_MAPPINGS['smZ CLIPTextEncode']
|
||||
steps = plot_image_vars['steps']
|
||||
positive, = cls().encode(clip, positive, "A1111", True, True, False, False, 6,
|
||||
1024, 1024, 0, 0, 1024, 1024, '', '', steps)
|
||||
negative, = cls().encode(clip, negative, "A1111", True, True, False, False, 6,
|
||||
1024, 1024, 0, 0, 1024, 1024, '', '', steps)
|
||||
else:
|
||||
raise Exception(
|
||||
f"[ERROR] To use clip text encode same as webui, you need to install 'smzNodes'")
|
||||
else:
|
||||
clip = clip if clip is not None else plot_image_vars["clip"]
|
||||
positive = advanced_encode(clip, positive,
|
||||
plot_image_vars['positive_token_normalization'],
|
||||
plot_image_vars[
|
||||
'positive_weight_interpretation'],
|
||||
w_max=1.0,
|
||||
apply_to_pooled="enable")
|
||||
|
||||
negative = advanced_encode(clip, negative,
|
||||
plot_image_vars['negative_token_normalization'],
|
||||
plot_image_vars[
|
||||
'negative_weight_interpretation'],
|
||||
w_max=1.0,
|
||||
apply_to_pooled="enable")
|
||||
if "positive_cond" in plot_image_vars:
|
||||
positive = positive + plot_image_vars["positive_cond"]
|
||||
if "negative_cond" in plot_image_vars:
|
||||
negative = negative + plot_image_vars["negative_cond"]
|
||||
|
||||
# Lora
|
||||
if self.x_type == "Lora" or self.y_type == "Lora":
|
||||
model = model if model is not None else plot_image_vars["model"]
|
||||
clip = clip if clip is not None else plot_image_vars["clip"]
|
||||
|
||||
xy_values = x_value if self.x_type == "Lora" else y_value
|
||||
lora_name, lora_model_strength, lora_clip_strength = xy_values.split(",")
|
||||
lora_stack = [{"lora_name": lora_name, "model": model, "clip" :clip, "lora_model_strength": float(lora_model_strength), "lora_clip_strength": float(lora_clip_strength)}]
|
||||
if 'lora_stack' in plot_image_vars:
|
||||
lora_stack = lora_stack + plot_image_vars['lora_stack']
|
||||
|
||||
if lora_stack is not None and lora_stack != []:
|
||||
for lora in lora_stack:
|
||||
lora_name = lora["lora_name"]
|
||||
model = model if model is not None else lora["model"]
|
||||
clip = clip if clip is not None else lora["clip"]
|
||||
lora_model_strength = lora["lora_model_strength"]
|
||||
lora_clip_strength = lora["lora_clip_strength"]
|
||||
if "lbw" in lora:
|
||||
lbw = lora["lbw"]
|
||||
lbw_a = lora["lbw_a"]
|
||||
lbw_b = lora["lbw_b"]
|
||||
cls = ALL_NODE_CLASS_MAPPINGS['LoraLoaderBlockWeight //Inspire']
|
||||
model, clip, _ = cls().doit(model, clip, lora_name, lora_model_strength, lora_clip_strength,
|
||||
False, 0,
|
||||
lbw_a, lbw_b, "", lbw)
|
||||
model, clip = easyCache.load_lora(lora_name, model, clip, lora_model_strength,
|
||||
lora_clip_strength)
|
||||
|
||||
# 提示词
|
||||
if "Positive" in self.x_type or "Positive" in self.y_type:
|
||||
if self.x_type == 'Positive Prompt S/R' or self.y_type == 'Positive Prompt S/R':
|
||||
@@ -847,7 +944,6 @@ class easyXYPlot:
|
||||
apply_to_pooled="enable")
|
||||
|
||||
model = model if model is not None else plot_image_vars["model"]
|
||||
clip = clip if clip is not None else plot_image_vars["clip"]
|
||||
vae = vae if vae is not None else plot_image_vars["vae"]
|
||||
positive = positive if positive is not None else plot_image_vars["positive_cond"]
|
||||
negative = negative if negative is not None else plot_image_vars["negative_cond"]
|
||||
@@ -874,8 +970,7 @@ class easyXYPlot:
|
||||
image = vae.decode(latent).cpu()
|
||||
|
||||
if self.output_individuals in [True, "True"]:
|
||||
easy_save = easySave(self.my_unique_id, self.prompt, self.extra_pnginfo)
|
||||
easy_save.images(image, self.save_prefix, self.image_output, group_id=self.num)
|
||||
easySave(image, self.save_prefix, self.image_output)
|
||||
|
||||
# Convert the image from tensor to PIL Image and add it to the list
|
||||
pil_image = easySampler.tensor2pil(image)
|
||||
@@ -989,6 +1084,17 @@ class easyXYPlot:
|
||||
easyCache = easyLoader()
|
||||
sampler = easySampler()
|
||||
|
||||
def easySave(images, filename_prefix, output_type, prompt=None, extra_pnginfo=None):
|
||||
|
||||
if output_type == "Hide":
|
||||
return list()
|
||||
if output_type == "Preview":
|
||||
filename_prefix = 'easyPreview'
|
||||
results = PreviewImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
|
||||
return results['ui']['images']
|
||||
else:
|
||||
results = SaveImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
|
||||
return results['ui']['images']
|
||||
|
||||
def check_link_to_clip(node_id, clip_id, visited=None, node=None):
|
||||
"""Check if a given node links directly or indirectly to a loader node."""
|
||||
@@ -1044,180 +1150,6 @@ def find_wildcards_seed(clip_id, text, prompt):
|
||||
else:
|
||||
return None
|
||||
|
||||
class easySave:
|
||||
def __init__(self, my_unique_id=0, prompt=None, extra_pnginfo=None, number_padding=5, overwrite_existing=False,
|
||||
output_dir=folder_paths.get_temp_directory()):
|
||||
self.number_padding = int(number_padding) if number_padding not in [None, "None", 0] else None
|
||||
self.overwrite_existing = overwrite_existing
|
||||
self.my_unique_id = my_unique_id
|
||||
self.prompt = prompt
|
||||
self.extra_pnginfo = extra_pnginfo
|
||||
self.type = 'temp'
|
||||
self.output_dir = output_dir
|
||||
if self.output_dir != folder_paths.get_temp_directory():
|
||||
self.output_dir = self.folder_parser(self.output_dir, self.prompt, self.my_unique_id)
|
||||
if not os.path.exists(self.output_dir):
|
||||
self._create_directory(self.output_dir)
|
||||
|
||||
@staticmethod
|
||||
def _create_directory(folder: str):
|
||||
"""Try to create the directory and log the status."""
|
||||
log_node_warn(f"Folder {folder} does not exist. Attempting to create...")
|
||||
if not os.path.exists(folder):
|
||||
try:
|
||||
os.makedirs(folder)
|
||||
log_node_success(f"{folder} Created Successfully")
|
||||
except OSError:
|
||||
log_node_error(f"Failed to create folder {folder}")
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _map_filename(filename: str, filename_prefix: str) -> Tuple[int, str, Optional[int]]:
|
||||
"""Utility function to map filename to its parts."""
|
||||
|
||||
# Get the prefix length and extract the prefix
|
||||
prefix_len = len(os.path.basename(filename_prefix))
|
||||
prefix = filename[:prefix_len]
|
||||
|
||||
# Search for the primary digits
|
||||
digits = re.search(r'(\d+)', filename[prefix_len:])
|
||||
|
||||
# Search for the number in brackets after the primary digits
|
||||
group_id = re.search(r'\((\d+)\)', filename[prefix_len:])
|
||||
|
||||
return (int(digits.group()) if digits else 0, prefix, int(group_id.group(1)) if group_id else 0)
|
||||
|
||||
@staticmethod
|
||||
def _format_date(text: str, date: datetime.datetime) -> str:
|
||||
"""Format the date according to specific patterns."""
|
||||
date_formats = {
|
||||
'd': lambda d: d.day,
|
||||
'dd': lambda d: '{:02d}'.format(d.day),
|
||||
'M': lambda d: d.month,
|
||||
'MM': lambda d: '{:02d}'.format(d.month),
|
||||
'h': lambda d: d.hour,
|
||||
'hh': lambda d: '{:02d}'.format(d.hour),
|
||||
'm': lambda d: d.minute,
|
||||
'mm': lambda d: '{:02d}'.format(d.minute),
|
||||
's': lambda d: d.second,
|
||||
'ss': lambda d: '{:02d}'.format(d.second),
|
||||
'y': lambda d: d.year,
|
||||
'yy': lambda d: str(d.year)[2:],
|
||||
'yyy': lambda d: str(d.year)[1:],
|
||||
'yyyy': lambda d: d.year,
|
||||
}
|
||||
|
||||
# We need to sort the keys in reverse order to ensure we match the longest formats first
|
||||
for format_str in sorted(date_formats.keys(), key=len, reverse=True):
|
||||
if format_str in text:
|
||||
text = text.replace(format_str, str(date_formats[format_str](date)))
|
||||
return text
|
||||
|
||||
@staticmethod
|
||||
def _gather_all_inputs(prompt: Dict[str, dict], unique_id: str, linkInput: str = '',
|
||||
collected_inputs: Optional[Dict[str, Union[str, List[str]]]] = None) -> Dict[
|
||||
str, Union[str, List[str]]]:
|
||||
"""Recursively gather all inputs from the prompt dictionary."""
|
||||
if prompt == None:
|
||||
return None
|
||||
|
||||
collected_inputs = collected_inputs or {}
|
||||
prompt_inputs = prompt[str(unique_id)]["inputs"]
|
||||
|
||||
for p_input, p_input_value in prompt_inputs.items():
|
||||
a_input = f"{linkInput}>{p_input}" if linkInput else p_input
|
||||
|
||||
if isinstance(p_input_value, list):
|
||||
easySave._gather_all_inputs(prompt, p_input_value[0], a_input, collected_inputs)
|
||||
else:
|
||||
existing_value = collected_inputs.get(a_input)
|
||||
if existing_value is None:
|
||||
collected_inputs[a_input] = p_input_value
|
||||
elif p_input_value not in existing_value:
|
||||
collected_inputs[a_input] = existing_value + "; " + p_input_value
|
||||
|
||||
return collected_inputs
|
||||
|
||||
@staticmethod
|
||||
def _get_filename_with_padding(output_dir, filename, number_padding, group_id, ext):
|
||||
"""Return filename with proper padding."""
|
||||
try:
|
||||
filtered = list(filter(lambda a: a[1] == filename,
|
||||
map(lambda x: easySave._map_filename(x, filename), os.listdir(output_dir))))
|
||||
last = max(filtered)[0]
|
||||
|
||||
for f in filtered:
|
||||
if f[0] == last:
|
||||
if f[2] == 0 or f[2] == group_id:
|
||||
last += 1
|
||||
counter = last
|
||||
except (ValueError, FileNotFoundError):
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
counter = 1
|
||||
|
||||
if group_id == 0:
|
||||
return f"{filename}.{ext}" if number_padding is None else f"{filename}_{counter:0{number_padding}}.{ext}"
|
||||
else:
|
||||
return f"{filename}_({group_id}).{ext}" if number_padding is None else f"{filename}_{counter:0{number_padding}}_({group_id}).{ext}"
|
||||
|
||||
@staticmethod
|
||||
def folder_parser(output_dir: str, prompt: Dict[str, dict], my_unique_id: str):
|
||||
output_dir = re.sub(r'%date:(.*?)%', lambda m: easySave._format_date(m.group(1), datetime.datetime.now()),
|
||||
output_dir)
|
||||
all_inputs = easySave._gather_all_inputs(prompt, my_unique_id)
|
||||
|
||||
return re.sub(r'%(.*?)%', lambda m: str(all_inputs.get(m.group(1), '')), output_dir)
|
||||
|
||||
def images(self, images, filename_prefix, output_type, embed_workflow=True, ext="png", group_id=0):
|
||||
FORMAT_MAP = {
|
||||
"png": "PNG",
|
||||
"jpg": "JPEG",
|
||||
"jpeg": "JPEG",
|
||||
"bmp": "BMP",
|
||||
"tif": "TIFF",
|
||||
"tiff": "TIFF"
|
||||
}
|
||||
|
||||
if ext not in FORMAT_MAP:
|
||||
raise ValueError(f"Unsupported file extension {ext}")
|
||||
|
||||
if output_type == "Hide":
|
||||
return list()
|
||||
if output_type in ("Save", "Hide/Save"):
|
||||
output_dir = self.output_dir if self.output_dir != folder_paths.get_temp_directory() else folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
if output_type == "Preview":
|
||||
output_dir = self.output_dir
|
||||
filename_prefix = 'easyPreview'
|
||||
results = list()
|
||||
|
||||
filename_prefix = re.sub(r'%date:(.*?)%', lambda m: easySave._format_date(m.group(1), datetime.datetime.now()),
|
||||
filename_prefix)
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
||||
filename_prefix, output_dir, images[0].shape[1], images[0].shape[0])
|
||||
for image in images:
|
||||
img = Image.fromarray(np.clip(255. * image.cpu().numpy(), 0, 255).astype(np.uint8))
|
||||
filename = filename.replace("%width%", str(img.size[0])).replace("%height%", str(img.size[1]))
|
||||
metadata = None
|
||||
if embed_workflow in (True, "True"):
|
||||
metadata = PngInfo()
|
||||
if self.prompt is not None:
|
||||
metadata.add_text("prompt", json.dumps(self.prompt))
|
||||
if hasattr(self, 'extra_pnginfo') and self.extra_pnginfo is not None:
|
||||
for key, value in self.extra_pnginfo.items():
|
||||
metadata.add_text(key, json.dumps(value))
|
||||
|
||||
file = f"{filename}_{counter:05}_.png"
|
||||
img.save(os.path.join(full_output_folder, file), pnginfo=metadata)
|
||||
results.append({
|
||||
"filename": file,
|
||||
"subfolder": subfolder,
|
||||
"type": self.type
|
||||
})
|
||||
counter += 1
|
||||
|
||||
return results
|
||||
|
||||
# ---------------------------------------------------------------提示词 开始----------------------------------------------------------------------#
|
||||
|
||||
# 正面提示词
|
||||
@@ -1898,15 +1830,17 @@ class fullLoader:
|
||||
easyCache.update_loaded_objects(prompt)
|
||||
|
||||
log_node_warn("正在处理模型...")
|
||||
# 判断是否存在 模型叠加xyplot, 若存在优先缓存第一个模型
|
||||
xyinputs_id = next((x for x in prompt if str(prompt[x]["class_type"]) == "easy XYInputs: ModelMergeBlocks"), None)
|
||||
if xyinputs_id is not None:
|
||||
node = prompt[xyinputs_id]
|
||||
# 判断是否存在 模型或Lora叠加xyplot, 若存在优先缓存第一个模型
|
||||
xy_model_id = next((x for x in prompt if str(prompt[x]["class_type"]) in ["easy XYInputs: ModelMergeBlocks", "easy XYInputs: Checkpoint"]), None)
|
||||
xy_lora_id = next((x for x in prompt if str(prompt[x]["class_type"]) == "easy XYInputs: Lora"), None)
|
||||
if xy_lora_id is not None:
|
||||
can_load_lora = False
|
||||
if xy_model_id is not None:
|
||||
node = prompt[xy_model_id]
|
||||
if "ckpt_name_1" in node["inputs"]:
|
||||
ckpt_name_1 = node["inputs"]["ckpt_name_1"]
|
||||
model, clip, vae = easyCache.load_checkpoint(ckpt_name_1)
|
||||
can_load_lora = False
|
||||
|
||||
# Load models
|
||||
elif model_override is not None and clip_override is not None and vae_override is not None:
|
||||
model = model_override
|
||||
@@ -3106,18 +3040,6 @@ class samplerFull:
|
||||
# Clean loaded_objects
|
||||
easyCache.update_loaded_objects(prompt)
|
||||
|
||||
# my_unique_id = int(my_unique_id)
|
||||
|
||||
# if my_unique_id:
|
||||
# workflow = extra_pnginfo["workflow"]
|
||||
# node = next((x for x in workflow["nodes"] if str(x["id"]) == my_unique_id), None)
|
||||
# if node and 'seed_num' in prompt[my_unique_id]['inputs']:
|
||||
# seed_num = prompt[my_unique_id]['inputs']['seed_num']
|
||||
# length = len(node["widgets_values"])
|
||||
# node["widgets_values"][length - 2] = seed_num
|
||||
|
||||
easy_save = easySave(my_unique_id, prompt, extra_pnginfo)
|
||||
|
||||
samp_model = model if model is not None else pipe["model"]
|
||||
samp_positive = positive if positive is not None else pipe["positive"]
|
||||
samp_negative = negative if negative is not None else pipe["negative"]
|
||||
@@ -3204,7 +3126,7 @@ class samplerFull:
|
||||
end_decode_time = int(time.time() * 1000)
|
||||
spent_time = '扩散:' + str((end_time-start_time)/1000)+'秒, 解码:' + str((end_decode_time-end_time)/1000)+'秒'
|
||||
|
||||
results = easy_save.images(samp_images, save_prefix, image_output)
|
||||
results = easySave(samp_images, save_prefix, image_output, prompt, extra_pnginfo)
|
||||
sampler.update_value_by_id("results", my_unique_id, results)
|
||||
|
||||
# Clean loaded_objects
|
||||
@@ -3306,13 +3228,10 @@ class samplerFull:
|
||||
|
||||
images, image_list = sampleXYplot.plot_images_and_labels()
|
||||
|
||||
samp_images = images
|
||||
|
||||
results = easy_save.images(images, save_prefix, image_output)
|
||||
|
||||
# Generate output_images
|
||||
output_images = torch.stack([tensor.squeeze() for tensor in image_list])
|
||||
|
||||
results = easySave(images, save_prefix, image_output, prompt, extra_pnginfo)
|
||||
sampler.update_value_by_id("results", my_unique_id, results)
|
||||
|
||||
# Clean loaded_objects
|
||||
@@ -3626,8 +3545,6 @@ class samplerSDTurbo:
|
||||
|
||||
my_unique_id = int(my_unique_id)
|
||||
|
||||
easy_save = easySave(my_unique_id, prompt, extra_pnginfo)
|
||||
|
||||
samp_model = pipe["model"] if model is None else model
|
||||
samp_positive = pipe["positive"]
|
||||
samp_negative = pipe["negative"]
|
||||
@@ -3673,7 +3590,7 @@ class samplerSDTurbo:
|
||||
# Clean loaded_objects
|
||||
easyCache.update_loaded_objects(prompt)
|
||||
|
||||
results = easy_save.images(samp_images, save_prefix, image_output)
|
||||
results = easySave(samp_images, save_prefix, image_output, prompt, extra_pnginfo)
|
||||
sampler.update_value_by_id("results", my_unique_id, results)
|
||||
|
||||
new_pipe = {
|
||||
@@ -3931,8 +3848,7 @@ class hiresFix:
|
||||
else:
|
||||
new_pipe = {}
|
||||
|
||||
easy_save = easySave(my_unique_id, prompt, extra_pnginfo)
|
||||
results = easy_save.images(s, save_prefix, image_output)
|
||||
results = easySave(s, save_prefix, image_output, prompt, extra_pnginfo)
|
||||
|
||||
if image_output in ("Sender", "Sender/Save"):
|
||||
PromptServer.instance.send_sync("img-send", {"link_id": link_id, "images": results})
|
||||
@@ -4089,8 +4005,6 @@ class detailerFix:
|
||||
|
||||
my_unique_id = int(my_unique_id)
|
||||
|
||||
easy_save = easySave(my_unique_id, prompt, extra_pnginfo)
|
||||
|
||||
model = model or (pipe["model"] if "model" in pipe else None)
|
||||
if model is None:
|
||||
raise Exception(f"[ERROR] model or pipe['model'] is missing")
|
||||
@@ -4150,7 +4064,7 @@ class detailerFix:
|
||||
|
||||
spent_time = '细节修复:' + str((end_time - start_time) / 1000) + '秒'
|
||||
|
||||
results = easy_save.images(enhanced_img, save_prefix, image_output)
|
||||
results = easySave(enhanced_img, save_prefix, image_output, prompt, extra_pnginfo)
|
||||
sampler.update_value_by_id("results", my_unique_id, results)
|
||||
|
||||
# Clean loaded_objects
|
||||
@@ -4601,6 +4515,38 @@ class pipeXYPlotAdvanced:
|
||||
"vae_use": vae_use
|
||||
}
|
||||
|
||||
if x_axis in ['advanced: Lora', 'advanced: Checkpoint']:
|
||||
lora_stack = X.get('lora_stack')
|
||||
_lora_stack = []
|
||||
if lora_stack is not None:
|
||||
for lora in lora_stack:
|
||||
_lora_stack.append(
|
||||
{"lora_name": lora[0], "model": pipe['model'], "clip": pipe['clip'], "lora_model_strength": lora[1],
|
||||
"lora_clip_strength": lora[2]})
|
||||
del lora_stack
|
||||
x_values = "; ".join(x_values)
|
||||
lora_stack = pipe['lora_stack'] + _lora_stack if 'lora_stack' in pipe else _lora_stack
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"lora_stack": lora_stack,
|
||||
}
|
||||
|
||||
if y_axis in ['advanced: Lora', 'advanced: Checkpoint']:
|
||||
lora_stack = Y.get('lora_stack')
|
||||
_lora_stack = []
|
||||
if lora_stack is not None:
|
||||
for lora in lora_stack:
|
||||
_lora_stack.append(
|
||||
{"lora_name": lora[0], "model": pipe['model'], "clip": pipe['clip'], "lora_model_strength": lora[1],
|
||||
"lora_clip_strength": lora[2]})
|
||||
del lora_stack
|
||||
y_values = "; ".join(y_values)
|
||||
lora_stack = pipe['lora_stack'] + _lora_stack if 'lora_stack' in pipe else _lora_stack
|
||||
new_pipe['loader_settings'] = {
|
||||
**pipe['loader_settings'],
|
||||
"lora_stack": lora_stack,
|
||||
}
|
||||
|
||||
if x_axis == 'advanced: Seeds++ Batch':
|
||||
if new_pipe['seed']:
|
||||
value = x_values
|
||||
@@ -5173,6 +5119,121 @@ class XYplot_Control_Net:
|
||||
return ({"axis": axis, "values": values},)
|
||||
|
||||
|
||||
#Checkpoints
|
||||
class XYplot_Checkpoint:
|
||||
|
||||
modes = ["Ckpt Names", "Ckpt Names+ClipSkip", "Ckpt Names+ClipSkip+VAE"]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
|
||||
checkpoints = ["None"] + folder_paths.get_filename_list("checkpoints")
|
||||
vaes = ["Baked VAE"] + folder_paths.get_filename_list("vae")
|
||||
|
||||
inputs = {
|
||||
"required": {
|
||||
"input_mode": (cls.modes,),
|
||||
"ckpt_count": ("INT", {"default": 3, "min": 0, "max": 10, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
for i in range(1, 10 + 1):
|
||||
inputs["required"][f"ckpt_name_{i}"] = (checkpoints,)
|
||||
inputs["required"][f"clip_skip_{i}"] = ("INT", {"default": -1, "min": -24, "max": -1, "step": 1})
|
||||
inputs["required"][f"vae_name_{i}"] = (vaes,)
|
||||
|
||||
inputs["optional"] = {
|
||||
"optional_lora_stack": ("LORA_STACK",)
|
||||
}
|
||||
return inputs
|
||||
|
||||
RETURN_TYPES = ("X_Y",)
|
||||
RETURN_NAMES = ("X or Y",)
|
||||
FUNCTION = "xy_value"
|
||||
|
||||
CATEGORY = "EasyUse/XY Inputs"
|
||||
|
||||
def xy_value(self, input_mode, ckpt_count, **kwargs):
|
||||
|
||||
axis = "advanced: Checkpoint"
|
||||
|
||||
checkpoints = [kwargs.get(f"ckpt_name_{i}") for i in range(1, ckpt_count + 1)]
|
||||
clip_skips = [kwargs.get(f"clip_skip_{i}") for i in range(1, ckpt_count + 1)]
|
||||
vaes = [kwargs.get(f"vae_name_{i}") for i in range(1, ckpt_count + 1)]
|
||||
|
||||
# Set None for Clip Skip and/or VAE if not correct modes
|
||||
for i in range(ckpt_count):
|
||||
if "ClipSkip" not in input_mode:
|
||||
clip_skips[i] = 'None'
|
||||
if "VAE" not in input_mode:
|
||||
vaes[i] = 'None'
|
||||
|
||||
# Extend each sub-array with lora_stack if it's not None
|
||||
values = [checkpoint.replace(',', '*')+','+str(clip_skip)+','+vae.replace(',', '*') for checkpoint, clip_skip, vae in zip(checkpoints, clip_skips, vaes) if
|
||||
checkpoint != "None"]
|
||||
|
||||
optional_lora_stack = kwargs.get("optional_lora_stack") if "optional_lora_stack" in kwargs else []
|
||||
|
||||
xy_values = {"axis": axis, "values": values, "lora_stack": optional_lora_stack}
|
||||
return (xy_values,)
|
||||
|
||||
#Loras
|
||||
class XYplot_Lora:
|
||||
|
||||
modes = ["Lora Names", "Lora Names+Weights"]
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
loras = ["None"] + folder_paths.get_filename_list("loras")
|
||||
|
||||
inputs = {
|
||||
"required": {
|
||||
"input_mode": (cls.modes,),
|
||||
"lora_count": ("INT", {"default": 3, "min": 0, "max": 10, "step": 1}),
|
||||
"model_strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
||||
"clip_strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
||||
}
|
||||
}
|
||||
|
||||
for i in range(1, 10 + 1):
|
||||
inputs["required"][f"lora_name_{i}"] = (loras,)
|
||||
inputs["required"][f"model_str_{i}"] = ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01})
|
||||
inputs["required"][f"clip_str_{i}"] = ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01})
|
||||
|
||||
inputs["optional"] = {
|
||||
"optional_lora_stack": ("LORA_STACK",)
|
||||
}
|
||||
return inputs
|
||||
|
||||
RETURN_TYPES = ("X_Y",)
|
||||
RETURN_NAMES = ("X or Y",)
|
||||
FUNCTION = "xy_value"
|
||||
|
||||
CATEGORY = "EasyUse/XY Inputs"
|
||||
|
||||
def xy_value(self, input_mode, lora_count, model_strength, clip_strength, **kwargs):
|
||||
|
||||
axis = "advanced: Lora"
|
||||
# Extract values from kwargs
|
||||
loras = [kwargs.get(f"lora_name_{i}") for i in range(1, lora_count + 1)]
|
||||
model_strs = [kwargs.get(f"model_str_{i}", model_strength) for i in range(1, lora_count + 1)]
|
||||
clip_strs = [kwargs.get(f"clip_str_{i}", clip_strength) for i in range(1, lora_count + 1)]
|
||||
|
||||
# Use model_strength and clip_strength for the loras where values are not provided
|
||||
if "Weights" not in input_mode:
|
||||
for i in range(lora_count):
|
||||
model_strs[i] = model_strength
|
||||
clip_strs[i] = clip_strength
|
||||
|
||||
# Extend each sub-array with lora_stack if it's not None
|
||||
values = [lora.replace(',', '*')+','+str(model_str)+','+str(clip_str) for lora, model_str, clip_str
|
||||
in zip(loras, model_strs, clip_strs) if lora != "None"]
|
||||
|
||||
optional_lora_stack = kwargs.get("optional_lora_stack") if "optional_lora_stack" in kwargs else []
|
||||
|
||||
xy_values = {"axis": axis, "values": values, "lora_stack": optional_lora_stack}
|
||||
return (xy_values,)
|
||||
|
||||
# 模型叠加
|
||||
class XYplot_ModelMergeBlocks:
|
||||
|
||||
@@ -5348,6 +5409,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"easy XYInputs: CFG Scale": XYplot_CFG,
|
||||
"easy XYInputs: Sampler/Scheduler": XYplot_Sampler_Scheduler,
|
||||
"easy XYInputs: Denoise": XYplot_Denoise,
|
||||
"easy XYInputs: Checkpoint": XYplot_Checkpoint,
|
||||
"easy XYInputs: Lora": XYplot_Lora,
|
||||
"easy XYInputs: ModelMergeBlocks": XYplot_ModelMergeBlocks,
|
||||
"easy XYInputs: PromptSR": XYplot_PromptSR,
|
||||
"easy XYInputs: ControlNet": XYplot_Control_Net,
|
||||
@@ -5418,6 +5481,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy XYInputs: CFG Scale": "XY Inputs: CFG Scale //EasyUse",
|
||||
"easy XYInputs: Sampler/Scheduler": "XY Inputs: Sampler/Scheduler //EasyUse",
|
||||
"easy XYInputs: Denoise": "XY Inputs: Denoise //EasyUse",
|
||||
"easy XYInputs: Checkpoint": "XY Inputs: Checkpoint //EasyUse",
|
||||
"easy XYInputs: Lora": "XY Inputs: Lora //EasyUse",
|
||||
"easy XYInputs: ModelMergeBlocks": "XY Inputs: ModelMergeBlocks //EasyUse",
|
||||
"easy XYInputs: PromptSR": "XY Inputs: PromptSR //EasyUse",
|
||||
"easy XYInputs: ControlNet": "XY Inputs: Controlnet //EasyUse",
|
||||
|
||||
+4
-65
@@ -405,7 +405,7 @@ class imageToMask:
|
||||
return (image.squeeze().mean(2),)
|
||||
|
||||
# 图像保存 (简易)
|
||||
from comfy.cli_args import args
|
||||
from nodes import PreviewImage, SaveImage
|
||||
class imageSaveSimple:
|
||||
|
||||
def __init__(self):
|
||||
@@ -430,74 +430,13 @@ class imageSaveSimple:
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "EasyUse/Image"
|
||||
|
||||
@staticmethod
|
||||
def _format_date(text: str, date: datetime.datetime) -> str:
|
||||
"""Format the date according to specific patterns."""
|
||||
date_formats = {
|
||||
'd': lambda d: d.day,
|
||||
'dd': lambda d: '{:02d}'.format(d.day),
|
||||
'M': lambda d: d.month,
|
||||
'MM': lambda d: '{:02d}'.format(d.month),
|
||||
'h': lambda d: d.hour,
|
||||
'hh': lambda d: '{:02d}'.format(d.hour),
|
||||
'm': lambda d: d.minute,
|
||||
'mm': lambda d: '{:02d}'.format(d.minute),
|
||||
's': lambda d: d.second,
|
||||
'ss': lambda d: '{:02d}'.format(d.second),
|
||||
'y': lambda d: d.year,
|
||||
'yy': lambda d: str(d.year)[2:],
|
||||
'yyy': lambda d: str(d.year)[1:],
|
||||
'yyyy': lambda d: d.year,
|
||||
}
|
||||
|
||||
# We need to sort the keys in reverse order to ensure we match the longest formats first
|
||||
for format_str in sorted(date_formats.keys(), key=len, reverse=True):
|
||||
if format_str in text:
|
||||
text = text.replace(format_str, str(date_formats[format_str](date)))
|
||||
return text
|
||||
|
||||
def save(self, images, filename_prefix="ComfyUI", only_preview=False, prompt=None, extra_pnginfo=None):
|
||||
|
||||
if only_preview:
|
||||
self.output_dir = folder_paths.get_temp_directory()
|
||||
self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
|
||||
self.type = 'temp'
|
||||
self.compress_level = 1
|
||||
PreviewImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
|
||||
return ()
|
||||
else:
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = ""
|
||||
self.compress_level = 4
|
||||
return SaveImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
|
||||
|
||||
filename_prefix = re.sub(r'%date:(.*?)%', lambda m: self._format_date(m.group(1), datetime.datetime.now()),
|
||||
filename_prefix)
|
||||
|
||||
filename_prefix += self.prefix_append
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
||||
|
||||
results = list()
|
||||
for image in images:
|
||||
img = Image.fromarray(np.clip(255. * image.cpu().numpy(), 0, 255).astype(np.uint8))
|
||||
filename = filename.replace("%width%", str(img.size[0])).replace("%height%", str(img.size[1]))
|
||||
|
||||
metadata = None
|
||||
metadata = PngInfo()
|
||||
if prompt is not None:
|
||||
metadata.add_text("prompt", json.dumps(prompt))
|
||||
if extra_pnginfo is not None:
|
||||
for x in extra_pnginfo:
|
||||
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||
|
||||
file = f"{filename}_{counter:05}_.png"
|
||||
img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=self.compress_level)
|
||||
results.append({
|
||||
"filename": file,
|
||||
"subfolder": subfolder,
|
||||
"type": self.type
|
||||
})
|
||||
counter += 1
|
||||
|
||||
return { "ui": { "images": results } }
|
||||
|
||||
# 图像批次合并
|
||||
class JoinImageBatch:
|
||||
|
||||
+21
-2
@@ -46,7 +46,7 @@ class SeedGenerator:
|
||||
|
||||
|
||||
def control_seed(v, action, seed_is_global):
|
||||
action = action or v['inputs']['action']
|
||||
action = v['inputs']['action'] if seed_is_global else action
|
||||
value = v['inputs']['value'] if seed_is_global else v['inputs']['seed_num']
|
||||
|
||||
if action == 'increment' or action == 'increment for each node':
|
||||
@@ -147,10 +147,13 @@ def prompt_seed_update(json_data):
|
||||
else:
|
||||
action = widgets_value[widgets_length - 1]
|
||||
else:
|
||||
action = widgets_value[widgets_length - 1]
|
||||
control_index = widgets_length - 2 if cls == 'easy seed' else widgets_length - 1
|
||||
action = widgets_value[control_index]
|
||||
|
||||
# print(action)
|
||||
node = k, v
|
||||
value = control_seed(node[1], action, False)
|
||||
|
||||
if k not in seed_widget_map:
|
||||
continue
|
||||
|
||||
@@ -158,6 +161,22 @@ def prompt_seed_update(json_data):
|
||||
if isinstance(v['inputs']['seed_num'], int):
|
||||
v['inputs']['seed_num'] = value
|
||||
|
||||
# 修改和seed节点连接的节点 (没有作用,不生效)
|
||||
# if cls == 'easy seed':
|
||||
# outputs = extra_data.get('outputs')
|
||||
# if outputs and outputs[0] and 'links' in outputs[0]:
|
||||
# for id in outputs[0]['links']:
|
||||
# for x in workflow["nodes"]:
|
||||
# if "inputs" in x and x['inputs'] != []:
|
||||
# x_seed_num = next((i for i in x['inputs'] if i['name'] == 'seed_num' and i['type'] == 'INT'), None)
|
||||
# if x_seed_num is not None and "link" in x_seed_num and id == x_seed_num['link']:
|
||||
# widgets_values = x['widgets_values']
|
||||
# if widgets_values:
|
||||
# widgets_values[len(widgets_values)-1] = action
|
||||
# widgets_values[len(widgets_values)-2] = value
|
||||
# print(x)
|
||||
|
||||
|
||||
return value is not None
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { app } from "/scripts/app.js";
|
||||
import { api } from "/scripts/api.js";
|
||||
import { ComfyWidgets } from "/scripts/widgets.js";
|
||||
|
||||
|
||||
let origProps = {};
|
||||
|
||||
const findWidgetByName = (node, name) => node.widgets.find((w) => w.name === name);
|
||||
@@ -39,7 +38,6 @@ function widgetLogic(node, widget) {
|
||||
toggleWidget(node, findWidgetByName(node, 'lora_model_strength'), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'lora_clip_strength'), true)
|
||||
}
|
||||
updateNodeHeight(node)
|
||||
}
|
||||
if (widget.name === 'rescale') {
|
||||
let rescale_after_model = findWidgetByName(node, 'rescale_after_model').value
|
||||
@@ -137,6 +135,7 @@ function widgetLogic(node, widget) {
|
||||
}
|
||||
updateNodeHeight(node)
|
||||
}
|
||||
|
||||
if (widget.name === 'resolution') {
|
||||
if (widget.value === "自定义 x 自定义") {
|
||||
toggleWidget(node, findWidgetByName(node, 'empty_latent_width'), true)
|
||||
@@ -315,6 +314,96 @@ function widgetLogic3(node, widget){
|
||||
}
|
||||
updateNodeHeight(node)
|
||||
}
|
||||
if (widget.name === 'lora_count') {
|
||||
let number_to_show = widget.value + 1
|
||||
const isWeight = findWidgetByName(node, 'input_mode').value.indexOf("Weights") == -1
|
||||
for (let i = 0; i < number_to_show; i++) {
|
||||
toggleWidget(node, findWidgetByName(node, 'lora_name_'+i), true)
|
||||
if (isWeight) {
|
||||
toggleWidget(node, findWidgetByName(node, 'lora_name_'+i), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'model_str_'+i))
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_str_'+i))
|
||||
} else {
|
||||
toggleWidget(node, findWidgetByName(node, 'lora_name_'+i), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'model_str_'+i),true)
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_str_'+i), true)
|
||||
}
|
||||
}
|
||||
for (let i = number_to_show; i < 11; i++) {
|
||||
toggleWidget(node, findWidgetByName(node, 'lora_name_'+i))
|
||||
toggleWidget(node, findWidgetByName(node, 'model_str_'+i))
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_str_'+i))
|
||||
}
|
||||
updateNodeHeight(node)
|
||||
}
|
||||
if (widget.name === 'ckpt_count') {
|
||||
let number_to_show = widget.value + 1
|
||||
const hasClipSkip = findWidgetByName(node, 'input_mode').value.indexOf("ClipSkip") != -1
|
||||
const hasVae = findWidgetByName(node, 'input_mode').value.indexOf("VAE") != -1
|
||||
for (let i = 0; i < number_to_show; i++) {
|
||||
toggleWidget(node, findWidgetByName(node, 'ckpt_name_'+i), true)
|
||||
if (hasClipSkip && hasVae) {
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_skip_'+i), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'vae_name_'+i), true)
|
||||
} else if (hasVae){
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_skip_' + i))
|
||||
toggleWidget(node, findWidgetByName(node, 'vae_name_' + i), true)
|
||||
}else{
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_skip_' + i))
|
||||
toggleWidget(node, findWidgetByName(node, 'vae_name_' + i))
|
||||
}
|
||||
}
|
||||
for (let i = number_to_show; i < 11; i++) {
|
||||
toggleWidget(node, findWidgetByName(node, 'ckpt_name_'+i))
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_skip_'+i))
|
||||
toggleWidget(node, findWidgetByName(node, 'vae_name_'+i))
|
||||
}
|
||||
updateNodeHeight(node)
|
||||
}
|
||||
|
||||
if (widget.name === 'input_mode') {
|
||||
if(node.comfyClass == 'easy XYInputs: Lora'){
|
||||
let number_to_show = findWidgetByName(node, 'lora_count').value + 1
|
||||
const hasWeight = widget.value.indexOf("Weights") != -1
|
||||
for (let i = 0; i < number_to_show; i++) {
|
||||
toggleWidget(node, findWidgetByName(node, 'lora_name_'+i), true)
|
||||
if (hasWeight) {
|
||||
toggleWidget(node, findWidgetByName(node, 'model_str_'+i), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_str_'+i), true)
|
||||
} else {
|
||||
toggleWidget(node, findWidgetByName(node, 'model_str_' + i))
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_str_' + i))
|
||||
}
|
||||
}
|
||||
if(hasWeight){
|
||||
toggleWidget(node, findWidgetByName(node, 'model_strength'))
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_strength'))
|
||||
}else{
|
||||
toggleWidget(node, findWidgetByName(node, 'model_strength'), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_strength'),true)
|
||||
}
|
||||
}
|
||||
else if(node.comfyClass == 'easy XYInputs: Checkpoint'){
|
||||
let number_to_show = findWidgetByName(node, 'ckpt_count').value + 1
|
||||
const hasClipSkip = widget.value.indexOf("ClipSkip") != -1
|
||||
const hasVae = widget.value.indexOf("VAE") != -1
|
||||
for (let i = 0; i < number_to_show; i++) {
|
||||
toggleWidget(node, findWidgetByName(node, 'ckpt_name_'+i), true)
|
||||
if (hasClipSkip && hasVae) {
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_skip_'+i), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'vae_name_'+i), true)
|
||||
} else if (hasClipSkip){
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_skip_' + i), true)
|
||||
toggleWidget(node, findWidgetByName(node, 'vae_name_' + i))
|
||||
}else{
|
||||
toggleWidget(node, findWidgetByName(node, 'clip_skip_' + i))
|
||||
toggleWidget(node, findWidgetByName(node, 'vae_name_' + i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNodeHeight(node)
|
||||
}
|
||||
|
||||
// if(widget.name == 'replace_count'){
|
||||
// let number_to_show = widget.value + 1
|
||||
@@ -352,6 +441,8 @@ app.registerExtension({
|
||||
case "easy imageRemoveBG":
|
||||
case "easy XYInputs: Steps":
|
||||
case "easy XYInputs: Sampler/Scheduler":
|
||||
case 'easy XYInputs: Checkpoint':
|
||||
case "easy XYInputs: Lora":
|
||||
case "easy XYInputs: PromptSR":
|
||||
case "easy XYInputs: ControlNet":
|
||||
case "easy rangeInt":
|
||||
@@ -664,6 +755,14 @@ app.registerExtension({
|
||||
serialize: false
|
||||
})
|
||||
seed_widget.linkedWidgets = [seed_control]
|
||||
if(nodeData.name == 'easy seed'){
|
||||
this.addWidget("button", "🎲 Manual Random Seed", null, _=>{
|
||||
if(seed_control.value != 'fixed'){
|
||||
seed_control.value = 'fixed'
|
||||
}
|
||||
seed_widget.value = Math.floor(Math.random() * 1125899906842624)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,7 +863,7 @@ const getSetWidgets = ['rescale_after_model', 'rescale', 'image_output',
|
||||
'refiner_lora1_name', 'refiner_lora2_name', 'upscale_method',
|
||||
'image_output', 'add_noise', 'info', 'sampler_name',
|
||||
'ckpt_B_name', 'ckpt_C_name', 'save_model', 'refiner_ckpt_name',
|
||||
'num_loras', 'mode', 'toggle', 'resolution', 'target_parameter', 'input_count', 'replace_count', 'downscale_mode', 'range_mode','text_combine_mode']
|
||||
'num_loras', 'mode', 'toggle', 'resolution', 'target_parameter', 'input_count', 'replace_count', 'downscale_mode', 'range_mode','text_combine_mode', 'input_mode','lora_count','ckpt_count']
|
||||
|
||||
function getSetters(node) {
|
||||
if (node.widgets)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { app } from "/scripts/app.js";
|
||||
import { applyTextReplacements } from "/scripts/utils.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Easy.SaveImageExtraOutput",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (["easy imageSave", "easy fullkSampler", "easy kSampler", "easy kSamplerTiled","easy kSamplerInpainting", "easy kSamplerDownscaleUnet", "easy kSamplerSDTurbo"].includes(nodeData.name)) {
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
// When the SaveImage node is created we want to override the serialization of the output name widget to run our S&R
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
||||
|
||||
const widget = this.widgets.find((w) => w.name === "filename_prefix" || w.name === 'save_prefix');
|
||||
widget.serializeValue = () => {
|
||||
return applyTextReplacements(app, widget.value);
|
||||
};
|
||||
|
||||
return r;
|
||||
};
|
||||
} else {
|
||||
// When any other node is created add a property to alias the node
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
||||
|
||||
if (!this.properties || !("Node name for S&R" in this.properties)) {
|
||||
this.addProperty("Node name for S&R", this.constructor.type, "string");
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user