mirror of
https://github.com/jags111/efficiency-nodes-comfyui.git
synced 2026-03-25 07:05:44 -03:00
Merge branch 'LucianoCirino:main' into optional-cnet_stack
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
Efficiency Nodes for ComfyUI
|
Efficiency Nodes for ComfyUI
|
||||||
=======
|
=======
|
||||||
### A collection of <a href="https://github.com/comfyanonymous/ComfyUI" >ComfyUI</a> custom nodes to help streamline workflows and reduce total node count.
|
### A collection of <a href="https://github.com/comfyanonymous/ComfyUI" >ComfyUI</a> custom nodes to help streamline workflows and reduce total node count.
|
||||||
## [Direct Download Link](https://github.com/LucianoCirino/efficiency-nodes-comfyui/releases/download/v1.92/efficiency-nodes-comfyui-.v192.7z)
|
## [Direct Download Link](https://github.com/LucianoCirino/efficiency-nodes-comfyui/releases/download/v1.92/efficiency-nodes-comfyui-v192.7z)
|
||||||
<!-------------------------------------------------------------------------------------------------------------------------------------------------------->
|
<!-------------------------------------------------------------------------------------------------------------------------------------------------------->
|
||||||
<details>
|
<details>
|
||||||
<summary>Efficient Loader</summary>
|
<summary>Efficient Loader</summary>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
from .efficiency_nodes import NODE_CLASS_MAPPINGS
|
from .efficiency_nodes import NODE_CLASS_MAPPINGS
|
||||||
|
WEB_DIRECTORY = "js"
|
||||||
__all__ = ['NODE_CLASS_MAPPINGS']
|
__all__ = ['NODE_CLASS_MAPPINGS']
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ font_path = os.path.join(my_dir, 'arial.ttf')
|
|||||||
|
|
||||||
# Append comfy_dir to sys.path & import files
|
# Append comfy_dir to sys.path & import files
|
||||||
sys.path.append(comfy_dir)
|
sys.path.append(comfy_dir)
|
||||||
from nodes import LatentUpscaleBy, KSampler, KSamplerAdvanced, VAEDecode, VAEDecodeTiled, CLIPSetLastLayer, CLIPTextEncode, ControlNetApplyAdvanced
|
from nodes import LatentUpscaleBy, KSampler, KSamplerAdvanced, VAEDecode, VAEDecodeTiled, VAEEncode, VAEEncodeTiled, \
|
||||||
|
ImageScaleBy, CLIPSetLastLayer, CLIPTextEncode, ControlNetLoader, ControlNetApply, ControlNetApplyAdvanced
|
||||||
from comfy_extras.nodes_clip_sdxl import CLIPTextEncodeSDXL, CLIPTextEncodeSDXLRefiner
|
from comfy_extras.nodes_clip_sdxl import CLIPTextEncodeSDXL, CLIPTextEncodeSDXLRefiner
|
||||||
import comfy.samplers
|
import comfy.samplers
|
||||||
import comfy.sd
|
import comfy.sd
|
||||||
@@ -81,11 +82,11 @@ def encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_cl
|
|||||||
empty_latent_height, negative_prompt)[0]
|
empty_latent_height, negative_prompt)[0]
|
||||||
# Return results based on return_type
|
# Return results based on return_type
|
||||||
if return_type == "base":
|
if return_type == "base":
|
||||||
return positive_encoded, negative_encoded
|
return positive_encoded, negative_encoded, clip
|
||||||
elif return_type == "refiner":
|
elif return_type == "refiner":
|
||||||
return refiner_positive_encoded, refiner_negative_encoded
|
return refiner_positive_encoded, refiner_negative_encoded, refiner_clip
|
||||||
elif return_type == "both":
|
elif return_type == "both":
|
||||||
return positive_encoded, negative_encoded, refiner_positive_encoded, refiner_negative_encoded
|
return positive_encoded, negative_encoded, clip, refiner_positive_encoded, refiner_negative_encoded, refiner_clip
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
# TSC Efficient Loader
|
# TSC Efficient Loader
|
||||||
@@ -162,7 +163,7 @@ class TSC_EfficientLoader:
|
|||||||
clip_skip = clip_skip[0] if loader_type == "sdxl" else clip_skip
|
clip_skip = clip_skip[0] if loader_type == "sdxl" else clip_skip
|
||||||
|
|
||||||
# Encode prompt based on loader_type
|
# Encode prompt based on loader_type
|
||||||
positive_encoded, negative_encoded, refiner_positive_encoded, refiner_negative_encoded = \
|
positive_encoded, negative_encoded, clip, refiner_positive_encoded, refiner_negative_encoded, refiner_clip = \
|
||||||
encode_prompts(positive, negative, clip, clip_skip, refiner_clip, refiner_clip_skip, ascore,
|
encode_prompts(positive, negative, clip, clip_skip, refiner_clip, refiner_clip_skip, ascore,
|
||||||
loader_type == "sdxl", empty_latent_width, empty_latent_height)
|
loader_type == "sdxl", empty_latent_width, empty_latent_height)
|
||||||
|
|
||||||
@@ -373,10 +374,11 @@ class TSC_Apply_ControlNet_Stack:
|
|||||||
|
|
||||||
for control_net_tuple in cnet_stack:
|
for control_net_tuple in cnet_stack:
|
||||||
control_net, image, strength, start_percent, end_percent = control_net_tuple
|
control_net, image, strength, start_percent, end_percent = control_net_tuple
|
||||||
controlnet_conditioning = ControlNetApplyAdvanced().apply_controlnet(positive, negative,
|
controlnet_conditioning = ControlNetApplyAdvanced().apply_controlnet(positive, negative, control_net, image,
|
||||||
control_net, image, strength,
|
strength, start_percent, end_percent)
|
||||||
start_percent, end_percent)
|
positive, negative = controlnet_conditioning[0], controlnet_conditioning[1]
|
||||||
return controlnet_conditioning
|
|
||||||
|
return (positive, negative, )
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
# TSC KSampler (Efficient)
|
# TSC KSampler (Efficient)
|
||||||
@@ -391,7 +393,7 @@ class TSC_KSampler:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {"required":
|
return {"required":
|
||||||
{"sampler_state": (["Sample", "Hold", "Script"], ),
|
{"sampler_state": (["Sample", "Script", "Hold"], ),
|
||||||
"model": ("MODEL",),
|
"model": ("MODEL",),
|
||||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||||
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
||||||
@@ -532,36 +534,23 @@ class TSC_KSampler:
|
|||||||
def vae_decode_latent(vae, samples, vae_decode):
|
def vae_decode_latent(vae, samples, vae_decode):
|
||||||
return VAEDecodeTiled().decode(vae,samples,512)[0] if "tiled" in vae_decode else VAEDecode().decode(vae,samples)[0]
|
return VAEDecodeTiled().decode(vae,samples,512)[0] if "tiled" in vae_decode else VAEDecode().decode(vae,samples)[0]
|
||||||
|
|
||||||
|
def vae_encode_image(vae, pixels, vae_decode):
|
||||||
|
return VAEEncodeTiled().encode(vae,pixels,512)[0] if "tiled" in vae_decode else VAEEncode().encode(vae,pixels)[0]
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------------------------------
|
||||||
def sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image,
|
def sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image,
|
||||||
denoise, sampler_type, add_noise, start_at_step, end_at_step, return_with_leftover_noise,
|
denoise, sampler_type, add_noise, start_at_step, end_at_step, return_with_leftover_noise,
|
||||||
refiner_model=None, refiner_positive=None, refiner_negative=None):
|
refiner_model, refiner_positive, refiner_negative, vae, vae_decode, sampler_state):
|
||||||
|
|
||||||
if keys_exist_in_script("tile"):
|
|
||||||
tile_width, tile_height, tiling_strategy, blenderneko_tiled_nodes = script["tile"]
|
|
||||||
TSampler = blenderneko_tiled_nodes.TiledKSampler
|
|
||||||
TSamplerAdvanced = blenderneko_tiled_nodes.TiledKSamplerAdvanced
|
|
||||||
|
|
||||||
# Sample the latent_image(s) using the Comfy KSampler nodes
|
# Sample the latent_image(s) using the Comfy KSampler nodes
|
||||||
if sampler_type == "regular":
|
if sampler_type == "regular":
|
||||||
if keys_exist_in_script("tile"):
|
samples = KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
||||||
samples = TSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg,
|
latent_image, denoise=denoise)[0]
|
||||||
sampler_name, scheduler, positive, negative,
|
|
||||||
latent_image, denoise=denoise)[0]
|
|
||||||
else:
|
|
||||||
samples = KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
|
||||||
latent_image, denoise=denoise)[0]
|
|
||||||
|
|
||||||
elif sampler_type == "advanced":
|
elif sampler_type == "advanced":
|
||||||
if keys_exist_in_script("tile"):
|
samples = KSamplerAdvanced().sample(model, add_noise, seed, steps, cfg, sampler_name, scheduler,
|
||||||
samples = TSamplerAdvanced().sample(model, add_noise, seed, tile_width, tile_height, tiling_strategy,
|
positive, negative, latent_image, start_at_step, end_at_step,
|
||||||
steps, cfg, sampler_name, scheduler,
|
return_with_leftover_noise, denoise=1.0)[0]
|
||||||
positive, negative, latent_image, start_at_step, end_at_step,
|
|
||||||
return_with_leftover_noise, "disabled", denoise=1.0)[0]
|
|
||||||
else:
|
|
||||||
samples = KSamplerAdvanced().sample(model, add_noise, seed, steps, cfg, sampler_name, scheduler,
|
|
||||||
positive, negative, latent_image, start_at_step, end_at_step,
|
|
||||||
return_with_leftover_noise, denoise=1.0)[0]
|
|
||||||
|
|
||||||
elif sampler_type == "sdxl":
|
elif sampler_type == "sdxl":
|
||||||
# Disable refiner if refine_at_step is -1
|
# Disable refiner if refine_at_step is -1
|
||||||
@@ -582,24 +571,35 @@ class TSC_KSampler:
|
|||||||
samples, end_at_step, steps,
|
samples, end_at_step, steps,
|
||||||
return_with_leftover_noise, denoise=1.0)[0]
|
return_with_leftover_noise, denoise=1.0)[0]
|
||||||
|
|
||||||
# Check if "hiresfix" exists in the script after main sampling has taken place
|
if sampler_state == "Script":
|
||||||
if keys_exist_in_script("hiresfix"):
|
|
||||||
|
|
||||||
# Unpack the tuple from the script's "hiresfix" key
|
# Check if "hiresfix" exists in the script after main sampling has taken place
|
||||||
latent_upscale_method, upscale_by, hires_steps, hires_denoise, iterations, upscale_function = script["hiresfix"]
|
if keys_exist_in_script("hiresfix"):
|
||||||
|
# Unpack the tuple from the script's "hiresfix" key
|
||||||
# Iterate for the given number of iterations
|
latent_upscale_method, upscale_by, hires_steps, hires_denoise, iterations, upscale_function = script["hiresfix"]
|
||||||
for _ in range(iterations):
|
# Iterate for the given number of iterations
|
||||||
upscaled_latent_image = upscale_function().upscale(samples, latent_upscale_method, upscale_by)[0]
|
for _ in range(iterations):
|
||||||
# Use the regular KSampler for each iteration
|
upscaled_latent_image = upscale_function().upscale(samples, latent_upscale_method, upscale_by)[0]
|
||||||
if False: #if keys_exist_in_script("tile"): # Disabled for HiResFix
|
|
||||||
samples = TSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg,
|
|
||||||
sampler_name, scheduler,
|
|
||||||
positive, negative, upscaled_latent_image, denoise=hires_denoise)[0]
|
|
||||||
else:
|
|
||||||
samples = KSampler().sample(model, seed, hires_steps, cfg, sampler_name, scheduler,
|
samples = KSampler().sample(model, seed, hires_steps, cfg, sampler_name, scheduler,
|
||||||
positive, negative, upscaled_latent_image, denoise=hires_denoise)[0]
|
positive, negative, upscaled_latent_image, denoise=hires_denoise)[0]
|
||||||
|
|
||||||
|
# Check if "tile" exists in the script after main sampling has taken place
|
||||||
|
if keys_exist_in_script("tile"):
|
||||||
|
# Unpack the tuple from the script's "tile" key
|
||||||
|
upscale_by, tile_controlnet, tile_size, tiling_strategy, tiling_steps, tiled_denoise,\
|
||||||
|
blenderneko_tiled_nodes = script["tile"]
|
||||||
|
# VAE Decode samples
|
||||||
|
image = vae_decode_latent(vae, samples, vae_decode)
|
||||||
|
# Upscale image
|
||||||
|
upscaled_image = ImageScaleBy().upscale(image, "nearest-exact", upscale_by)[0]
|
||||||
|
upscaled_latent = vae_encode_image(vae, upscaled_image, vae_decode)
|
||||||
|
# Apply Control Net using upscaled_image and loaded control_net
|
||||||
|
positive = ControlNetApply().apply_controlnet(positive, tile_controlnet, upscaled_image, 1)[0]
|
||||||
|
# Sample latent
|
||||||
|
TSampler = blenderneko_tiled_nodes.TiledKSampler
|
||||||
|
samples = TSampler().sample(model, seed-1, tile_size, tile_size, tiling_strategy, tiling_steps, cfg,
|
||||||
|
sampler_name, scheduler, positive, negative, upscaled_latent,
|
||||||
|
denoise=tiled_denoise)[0]
|
||||||
return samples
|
return samples
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------------------------------
|
||||||
@@ -643,8 +643,9 @@ class TSC_KSampler:
|
|||||||
send_command_to_frontend(startListening=True, maxCount=steps-1, sendBlob=False)
|
send_command_to_frontend(startListening=True, maxCount=steps-1, sendBlob=False)
|
||||||
|
|
||||||
samples = sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
samples = sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
||||||
latent_image, denoise, sampler_type, add_noise, start_at_step, end_at_step,
|
latent_image, denoise, sampler_type, add_noise, start_at_step, end_at_step,
|
||||||
return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative)
|
return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative,
|
||||||
|
vae, vae_decode, sampler_state)
|
||||||
|
|
||||||
# Cache samples in the 'last_helds' dictionary "latent"
|
# Cache samples in the 'last_helds' dictionary "latent"
|
||||||
update_value_by_id("latent", my_unique_id, samples)
|
update_value_by_id("latent", my_unique_id, samples)
|
||||||
@@ -854,7 +855,7 @@ class TSC_KSampler:
|
|||||||
else (os.path.basename(v[0]), v[1]) if v[2] is None
|
else (os.path.basename(v[0]), v[1]) if v[2] is None
|
||||||
else (os.path.basename(v[0]),) + v[1:] for v in value]
|
else (os.path.basename(v[0]),) + v[1:] for v in value]
|
||||||
|
|
||||||
elif type_ == "LoRA" and isinstance(value, list):
|
elif (type_ == "LoRA" or type_ == "LoRA Stacks") and isinstance(value, list):
|
||||||
# Return only the first Tuple of each inner array
|
# Return only the first Tuple of each inner array
|
||||||
return [[(os.path.basename(v[0][0]),) + v[0][1:], "..."] if len(v) > 1
|
return [[(os.path.basename(v[0][0]),) + v[0][1:], "..."] if len(v) > 1
|
||||||
else [(os.path.basename(v[0][0]),) + v[0][1:]] for v in value]
|
else [(os.path.basename(v[0][0]),) + v[0][1:]] for v in value]
|
||||||
@@ -955,6 +956,7 @@ class TSC_KSampler:
|
|||||||
"Checkpoint",
|
"Checkpoint",
|
||||||
"Refiner",
|
"Refiner",
|
||||||
"LoRA",
|
"LoRA",
|
||||||
|
"LoRA Stacks",
|
||||||
"VAE",
|
"VAE",
|
||||||
]
|
]
|
||||||
conditioners = {
|
conditioners = {
|
||||||
@@ -999,6 +1001,9 @@ class TSC_KSampler:
|
|||||||
# Create a list of tuples with types and values
|
# Create a list of tuples with types and values
|
||||||
type_value_pairs = [(X_type, X_value.copy()), (Y_type, Y_value.copy())]
|
type_value_pairs = [(X_type, X_value.copy()), (Y_type, Y_value.copy())]
|
||||||
|
|
||||||
|
# Replace "LoRA Stacks" with "LoRA"
|
||||||
|
type_value_pairs = [('LoRA' if t == 'LoRA Stacks' else t, v) for t, v in type_value_pairs]
|
||||||
|
|
||||||
# Iterate over type-value pairs
|
# Iterate over type-value pairs
|
||||||
for t, v in type_value_pairs:
|
for t, v in type_value_pairs:
|
||||||
if t in dict_map:
|
if t in dict_map:
|
||||||
@@ -1041,7 +1046,7 @@ class TSC_KSampler:
|
|||||||
elif X_type == "Refiner":
|
elif X_type == "Refiner":
|
||||||
ckpt_dict = []
|
ckpt_dict = []
|
||||||
lora_dict = []
|
lora_dict = []
|
||||||
elif X_type == "LoRA":
|
elif X_type in ("LoRA", "LoRA Stacks"):
|
||||||
ckpt_dict = []
|
ckpt_dict = []
|
||||||
refn_dict = []
|
refn_dict = []
|
||||||
|
|
||||||
@@ -1204,7 +1209,7 @@ class TSC_KSampler:
|
|||||||
text = f"RefClipSkip ({refiner_clip_skip[0]})"
|
text = f"RefClipSkip ({refiner_clip_skip[0]})"
|
||||||
|
|
||||||
elif "LoRA" in var_type:
|
elif "LoRA" in var_type:
|
||||||
if not lora_stack:
|
if not lora_stack or var_type == "LoRA Stacks":
|
||||||
lora_stack = var.copy()
|
lora_stack = var.copy()
|
||||||
else:
|
else:
|
||||||
# Updating the first tuple of lora_stack
|
# Updating the first tuple of lora_stack
|
||||||
@@ -1214,7 +1219,7 @@ class TSC_KSampler:
|
|||||||
lora_name, lora_model_wt, lora_clip_wt = lora_stack[0]
|
lora_name, lora_model_wt, lora_clip_wt = lora_stack[0]
|
||||||
lora_filename = os.path.splitext(os.path.basename(lora_name))[0]
|
lora_filename = os.path.splitext(os.path.basename(lora_name))[0]
|
||||||
|
|
||||||
if var_type == "LoRA":
|
if var_type == "LoRA" or var_type == "LoRA Stacks":
|
||||||
if len(lora_stack) == 1:
|
if len(lora_stack) == 1:
|
||||||
lora_model_wt = format(float(lora_model_wt), ".2f").rstrip('0').rstrip('.')
|
lora_model_wt = format(float(lora_model_wt), ".2f").rstrip('0').rstrip('.')
|
||||||
lora_clip_wt = format(float(lora_clip_wt), ".2f").rstrip('0').rstrip('.')
|
lora_clip_wt = format(float(lora_clip_wt), ".2f").rstrip('0').rstrip('.')
|
||||||
@@ -1337,7 +1342,7 @@ class TSC_KSampler:
|
|||||||
# Note: Index is held at 0 when Y_type == "Nothing"
|
# Note: Index is held at 0 when Y_type == "Nothing"
|
||||||
|
|
||||||
# Load Checkpoint if required. If Y_type is LoRA, required models will be loaded by load_lora func.
|
# Load Checkpoint if required. If Y_type is LoRA, required models will be loaded by load_lora func.
|
||||||
if (X_type == "Checkpoint" and index == 0 and Y_type != "LoRA"):
|
if (X_type == "Checkpoint" and index == 0 and Y_type not in ("LoRA", "LoRA Stacks")):
|
||||||
if lora_stack is None:
|
if lora_stack is None:
|
||||||
model, clip, _ = load_checkpoint(ckpt_name, xyplot_id, cache=cache[1])
|
model, clip, _ = load_checkpoint(ckpt_name, xyplot_id, cache=cache[1])
|
||||||
else: # Load Efficient Loader LoRA
|
else: # Load Efficient Loader LoRA
|
||||||
@@ -1346,11 +1351,11 @@ class TSC_KSampler:
|
|||||||
encode = True
|
encode = True
|
||||||
|
|
||||||
# Load LoRA if required
|
# Load LoRA if required
|
||||||
elif (X_type == "LoRA" and index == 0):
|
elif (X_type in ("LoRA", "LoRA Stacks") and index == 0):
|
||||||
# Don't cache Checkpoints
|
# Don't cache Checkpoints
|
||||||
model, clip = load_lora(lora_stack, ckpt_name, xyplot_id, cache=cache[2])
|
model, clip = load_lora(lora_stack, ckpt_name, xyplot_id, cache=cache[2])
|
||||||
encode = True
|
encode = True
|
||||||
elif Y_type == "LoRA": # X_type must be Checkpoint, so cache those as defined
|
elif Y_type in ("LoRA", "LoRA Stacks"): # X_type must be Checkpoint, so cache those as defined
|
||||||
model, clip = load_lora(lora_stack, ckpt_name, xyplot_id,
|
model, clip = load_lora(lora_stack, ckpt_name, xyplot_id,
|
||||||
cache=None, ckpt_cache=cache[1])
|
cache=None, ckpt_cache=cache[1])
|
||||||
encode = True
|
encode = True
|
||||||
@@ -1378,7 +1383,7 @@ class TSC_KSampler:
|
|||||||
|
|
||||||
# Encode base prompt
|
# Encode base prompt
|
||||||
if encode == True:
|
if encode == True:
|
||||||
positive, negative = \
|
positive, negative, clip = \
|
||||||
encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_clip,
|
encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_clip,
|
||||||
refiner_clip_skip, ascore, sampler_type == "sdxl", empty_latent_width,
|
refiner_clip_skip, ascore, sampler_type == "sdxl", empty_latent_width,
|
||||||
empty_latent_height, return_type="base")
|
empty_latent_height, return_type="base")
|
||||||
@@ -1388,7 +1393,7 @@ class TSC_KSampler:
|
|||||||
positive, negative = controlnet_conditioning[0], controlnet_conditioning[1]
|
positive, negative = controlnet_conditioning[0], controlnet_conditioning[1]
|
||||||
|
|
||||||
if encode_refiner == True:
|
if encode_refiner == True:
|
||||||
refiner_positive, refiner_negative = \
|
refiner_positive, refiner_negative, refiner_clip = \
|
||||||
encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_clip,
|
encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_clip,
|
||||||
refiner_clip_skip, ascore, sampler_type == "sdxl", empty_latent_width,
|
refiner_clip_skip, ascore, sampler_type == "sdxl", empty_latent_width,
|
||||||
empty_latent_height, return_type="refiner")
|
empty_latent_height, return_type="refiner")
|
||||||
@@ -1424,7 +1429,8 @@ class TSC_KSampler:
|
|||||||
|
|
||||||
samples = sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
samples = sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
||||||
latent_image, denoise, sampler_type, add_noise, start_at_step, end_at_step,
|
latent_image, denoise, sampler_type, add_noise, start_at_step, end_at_step,
|
||||||
return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative)
|
return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative,
|
||||||
|
vae, vae_decode, sampler_state)
|
||||||
|
|
||||||
# Add the latent tensor to the tensors list
|
# Add the latent tensor to the tensors list
|
||||||
latent_list.append(samples)
|
latent_list.append(samples)
|
||||||
@@ -1494,7 +1500,6 @@ class TSC_KSampler:
|
|||||||
ckpt_name, clip_skip, refiner_name, refiner_clip_skip, positive_prompt,
|
ckpt_name, clip_skip, refiner_name, refiner_clip_skip, positive_prompt,
|
||||||
negative_prompt, ascore, lora_stack, cnet_stack, X_label, len(X_value))
|
negative_prompt, ascore, lora_stack, cnet_stack, X_label, len(X_value))
|
||||||
|
|
||||||
|
|
||||||
if X_type != "Nothing" and Y_type == "Nothing":
|
if X_type != "Nothing" and Y_type == "Nothing":
|
||||||
if X_type == "XY_Capsule":
|
if X_type == "XY_Capsule":
|
||||||
model, clip, refiner_model, refiner_clip = \
|
model, clip, refiner_model, refiner_clip = \
|
||||||
@@ -1520,10 +1525,11 @@ class TSC_KSampler:
|
|||||||
|
|
||||||
elif X_type != "Nothing" and Y_type != "Nothing":
|
elif X_type != "Nothing" and Y_type != "Nothing":
|
||||||
for Y_index, Y in enumerate(Y_value):
|
for Y_index, Y in enumerate(Y_value):
|
||||||
|
if Y_type == "XY_Capsule" or X_type == "XY_Capsule":
|
||||||
if Y_type == "XY_Capsule" and X_type == "XY_Capsule":
|
|
||||||
model, clip, refiner_model, refiner_clip = \
|
model, clip, refiner_model, refiner_clip = \
|
||||||
clone_or_none(original_model, original_clip, original_refiner_model, original_refiner_clip)
|
clone_or_none(original_model, original_clip, original_refiner_model, original_refiner_clip)
|
||||||
|
|
||||||
|
if Y_type == "XY_Capsule" and X_type == "XY_Capsule":
|
||||||
Y.set_x_capsule(X)
|
Y.set_x_capsule(X)
|
||||||
|
|
||||||
# Define Y parameters and generate labels
|
# Define Y parameters and generate labels
|
||||||
@@ -1569,7 +1575,7 @@ class TSC_KSampler:
|
|||||||
clear_cache_by_exception(xyplot_id, lora_dict=[], refn_dict=[])
|
clear_cache_by_exception(xyplot_id, lora_dict=[], refn_dict=[])
|
||||||
elif X_type == "Refiner":
|
elif X_type == "Refiner":
|
||||||
clear_cache_by_exception(xyplot_id, ckpt_dict=[], lora_dict=[])
|
clear_cache_by_exception(xyplot_id, ckpt_dict=[], lora_dict=[])
|
||||||
elif X_type == "LoRA":
|
elif X_type in ("LoRA", "LoRA Stacks"):
|
||||||
clear_cache_by_exception(xyplot_id, ckpt_dict=[], refn_dict=[])
|
clear_cache_by_exception(xyplot_id, ckpt_dict=[], refn_dict=[])
|
||||||
|
|
||||||
# __________________________________________________________________________________________________________
|
# __________________________________________________________________________________________________________
|
||||||
@@ -1669,7 +1675,7 @@ class TSC_KSampler:
|
|||||||
lora_name = lora_wt = lora_model_str = lora_clip_str = None
|
lora_name = lora_wt = lora_model_str = lora_clip_str = None
|
||||||
|
|
||||||
# Check for all possible LoRA types
|
# Check for all possible LoRA types
|
||||||
lora_types = ["LoRA", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr"]
|
lora_types = ["LoRA", "LoRA Stacks", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr"]
|
||||||
|
|
||||||
if X_type not in lora_types and Y_type not in lora_types:
|
if X_type not in lora_types and Y_type not in lora_types:
|
||||||
if lora_stack:
|
if lora_stack:
|
||||||
@@ -1682,7 +1688,7 @@ class TSC_KSampler:
|
|||||||
else:
|
else:
|
||||||
if X_type in lora_types:
|
if X_type in lora_types:
|
||||||
value = get_lora_sublist_name(X_type, X_value)
|
value = get_lora_sublist_name(X_type, X_value)
|
||||||
if X_type == "LoRA":
|
if X_type in ("LoRA", "LoRA Stacks"):
|
||||||
lora_name = value
|
lora_name = value
|
||||||
lora_model_str = None
|
lora_model_str = None
|
||||||
lora_clip_str = None
|
lora_clip_str = None
|
||||||
@@ -1704,7 +1710,7 @@ class TSC_KSampler:
|
|||||||
|
|
||||||
if Y_type in lora_types:
|
if Y_type in lora_types:
|
||||||
value = get_lora_sublist_name(Y_type, Y_value)
|
value = get_lora_sublist_name(Y_type, Y_value)
|
||||||
if Y_type == "LoRA":
|
if Y_type in ("LoRA", "LoRA Stacks"):
|
||||||
lora_name = value
|
lora_name = value
|
||||||
lora_model_str = None
|
lora_model_str = None
|
||||||
lora_clip_str = None
|
lora_clip_str = None
|
||||||
@@ -1727,13 +1733,13 @@ class TSC_KSampler:
|
|||||||
return lora_name, lora_wt, lora_model_str, lora_clip_str
|
return lora_name, lora_wt, lora_model_str, lora_clip_str
|
||||||
|
|
||||||
def get_lora_sublist_name(lora_type, lora_value):
|
def get_lora_sublist_name(lora_type, lora_value):
|
||||||
if lora_type == "LoRA" or lora_type == "LoRA Batch":
|
if lora_type in ("LoRA", "LoRA Batch", "LoRA Stacks"):
|
||||||
formatted_sublists = []
|
formatted_sublists = []
|
||||||
for sublist in lora_value:
|
for sublist in lora_value:
|
||||||
formatted_entries = []
|
formatted_entries = []
|
||||||
for x in sublist:
|
for x in sublist:
|
||||||
base_name = os.path.splitext(os.path.basename(str(x[0])))[0]
|
base_name = os.path.splitext(os.path.basename(str(x[0])))[0]
|
||||||
formatted_str = f"{base_name}({round(x[1], 3)},{round(x[2], 3)})" if lora_type == "LoRA" else f"{base_name}"
|
formatted_str = f"{base_name}({round(x[1], 3)},{round(x[2], 3)})" if lora_type in ("LoRA", "LoRA Stacks") else f"{base_name}"
|
||||||
formatted_entries.append(formatted_str)
|
formatted_entries.append(formatted_str)
|
||||||
formatted_sublists.append(f"{', '.join(formatted_entries)}")
|
formatted_sublists.append(f"{', '.join(formatted_entries)}")
|
||||||
return "\n ".join(formatted_sublists)
|
return "\n ".join(formatted_sublists)
|
||||||
@@ -2376,7 +2382,7 @@ class TSC_XYplot:
|
|||||||
# Check that dependencies are connected for specific plot types
|
# Check that dependencies are connected for specific plot types
|
||||||
encode_types = {
|
encode_types = {
|
||||||
"Checkpoint", "Refiner",
|
"Checkpoint", "Refiner",
|
||||||
"LoRA", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr",
|
"LoRA", "LoRA Stacks", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr",
|
||||||
"Positive Prompt S/R", "Negative Prompt S/R",
|
"Positive Prompt S/R", "Negative Prompt S/R",
|
||||||
"AScore+", "AScore-",
|
"AScore+", "AScore-",
|
||||||
"Clip Skip", "Clip Skip (Refiner)",
|
"Clip Skip", "Clip Skip (Refiner)",
|
||||||
@@ -2392,8 +2398,13 @@ class TSC_XYplot:
|
|||||||
# Check if both X_type and Y_type are special lora_types
|
# Check if both X_type and Y_type are special lora_types
|
||||||
lora_types = {"LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr"}
|
lora_types = {"LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr"}
|
||||||
if (X_type in lora_types and Y_type not in lora_types) or (Y_type in lora_types and X_type not in lora_types):
|
if (X_type in lora_types and Y_type not in lora_types) or (Y_type in lora_types and X_type not in lora_types):
|
||||||
print(
|
print(f"{error('XY Plot Error:')} Both X and Y must be connected to use the 'LoRA Plot' node.")
|
||||||
f"{error('XY Plot Error:')} Both X and Y must be connected to use the 'LoRA Plot' node.")
|
return (None,)
|
||||||
|
|
||||||
|
# Do not allow LoRA and LoRA Stacks
|
||||||
|
lora_types = {"LoRA", "LoRA Stacks"}
|
||||||
|
if (X_type in lora_types and Y_type in lora_types):
|
||||||
|
print(f"{error('XY Plot Error:')} X and Y input types must be different.")
|
||||||
return (None,)
|
return (None,)
|
||||||
|
|
||||||
# Clean Schedulers from Sampler data (if other type is Scheduler)
|
# Clean Schedulers from Sampler data (if other type is Scheduler)
|
||||||
@@ -2514,7 +2525,7 @@ class TSC_XYplot_Steps:
|
|||||||
xy_type = "Steps"
|
xy_type = "Steps"
|
||||||
xy_first = first_step
|
xy_first = first_step
|
||||||
xy_last = last_step
|
xy_last = last_step
|
||||||
elif target_parameter == "start at step":
|
elif target_parameter == "start_at_step":
|
||||||
xy_type = "StartStep"
|
xy_type = "StartStep"
|
||||||
xy_first = first_start_step
|
xy_first = first_start_step
|
||||||
xy_last = last_start_step
|
xy_last = last_start_step
|
||||||
@@ -3140,7 +3151,7 @@ class TSC_XYplot_LoRA_Stacks:
|
|||||||
CATEGORY = "Efficiency Nodes/XY Inputs"
|
CATEGORY = "Efficiency Nodes/XY Inputs"
|
||||||
|
|
||||||
def xy_value(self, node_state, lora_stack_1=None, lora_stack_2=None, lora_stack_3=None, lora_stack_4=None, lora_stack_5=None):
|
def xy_value(self, node_state, lora_stack_1=None, lora_stack_2=None, lora_stack_3=None, lora_stack_4=None, lora_stack_5=None):
|
||||||
xy_type = "LoRA"
|
xy_type = "LoRA Stacks"
|
||||||
xy_value = [stack for stack in [lora_stack_1, lora_stack_2, lora_stack_3, lora_stack_4, lora_stack_5] if stack is not None]
|
xy_value = [stack for stack in [lora_stack_1, lora_stack_2, lora_stack_3, lora_stack_4, lora_stack_5] if stack is not None]
|
||||||
if not xy_value or not any(xy_value) or node_state == "Disabled":
|
if not xy_value or not any(xy_value) or node_state == "Disabled":
|
||||||
return (None,)
|
return (None,)
|
||||||
@@ -3312,7 +3323,7 @@ class TSC_XYplot_Control_Net_End:
|
|||||||
|
|
||||||
# =======================================================================================================================
|
# =======================================================================================================================
|
||||||
# TSC XY Plot: Control Net
|
# TSC XY Plot: Control Net
|
||||||
class TSC_XYplot_Control_Net:
|
class TSC_XYplot_Control_Net(TSC_XYplot_Control_Net_Strength, TSC_XYplot_Control_Net_Start, TSC_XYplot_Control_Net_End):
|
||||||
parameters = ["strength", "start_percent", "end_percent"]
|
parameters = ["strength", "start_percent", "end_percent"]
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -3329,7 +3340,7 @@ class TSC_XYplot_Control_Net:
|
|||||||
"first_end_percent": ("FLOAT", {"default": 0.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
"first_end_percent": ("FLOAT", {"default": 0.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
||||||
"last_end_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
"last_end_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
||||||
"strength": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 10.0, "step": 0.01}),
|
"strength": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 10.0, "step": 0.01}),
|
||||||
"start_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
||||||
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
|
||||||
},
|
},
|
||||||
"optional": {"cnet_stack": ("CONTROL_NET_STACK",)},
|
"optional": {"cnet_stack": ("CONTROL_NET_STACK",)},
|
||||||
@@ -3341,10 +3352,17 @@ class TSC_XYplot_Control_Net:
|
|||||||
CATEGORY = "Efficiency Nodes/XY Inputs"
|
CATEGORY = "Efficiency Nodes/XY Inputs"
|
||||||
|
|
||||||
def xy_value(self, control_net, image, target_parameter, batch_count, first_strength, last_strength, first_start_percent,
|
def xy_value(self, control_net, image, target_parameter, batch_count, first_strength, last_strength, first_start_percent,
|
||||||
last_start_percent, first_end_percent, last_end_percent, strength, start_percent, cnet_stack=None):
|
last_start_percent, first_end_percent, last_end_percent, strength, start_percent, end_percent, cnet_stack=None):
|
||||||
|
|
||||||
|
if target_parameter == "strength":
|
||||||
return ((xy_type, xy_value),)
|
return TSC_XYplot_Control_Net_Strength.xy_value(self, control_net, image, batch_count, first_strength,
|
||||||
|
last_strength, start_percent, end_percent, cnet_stack=cnet_stack)
|
||||||
|
elif target_parameter == "start_percent":
|
||||||
|
return TSC_XYplot_Control_Net_Start.xy_value(self, control_net, image, batch_count, first_start_percent,
|
||||||
|
last_start_percent, strength, end_percent, cnet_stack=cnet_stack)
|
||||||
|
elif target_parameter == "end_percent":
|
||||||
|
return TSC_XYplot_Control_Net_End.xy_value(self, control_net, image, batch_count, first_end_percent,
|
||||||
|
last_end_percent, strength, start_percent, cnet_stack=cnet_stack)
|
||||||
|
|
||||||
#=======================================================================================================================
|
#=======================================================================================================================
|
||||||
# TSC XY Plot: Control Net Plot
|
# TSC XY Plot: Control Net Plot
|
||||||
@@ -4010,8 +4028,8 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"XY Input: LoRA Stacks": TSC_XYplot_LoRA_Stacks,
|
"XY Input: LoRA Stacks": TSC_XYplot_LoRA_Stacks,
|
||||||
"XY Input: Control Net": TSC_XYplot_Control_Net,
|
"XY Input: Control Net": TSC_XYplot_Control_Net,
|
||||||
"XY Input: Control Net Plot": TSC_XYplot_Control_Net_Plot,
|
"XY Input: Control Net Plot": TSC_XYplot_Control_Net_Plot,
|
||||||
"XY Input: Manual XY Entry": TSC_XYplot_Manual_XY_Entry, # DISABLED, NEEDS UPDATE
|
"XY Input: Manual XY Entry": TSC_XYplot_Manual_XY_Entry,
|
||||||
"Manual XY Entry Info": TSC_XYplot_Manual_XY_Entry_Info, # DISABLED, NEEDS UPDATE
|
"Manual XY Entry Info": TSC_XYplot_Manual_XY_Entry_Info,
|
||||||
"Join XY Inputs of Same Type": TSC_XYplot_JoinInputs,
|
"Join XY Inputs of Same Type": TSC_XYplot_JoinInputs,
|
||||||
"Image Overlay": TSC_ImageOverlay
|
"Image Overlay": TSC_ImageOverlay
|
||||||
}
|
}
|
||||||
@@ -4110,7 +4128,7 @@ class TSC_HighRes_Fix:
|
|||||||
NODE_CLASS_MAPPINGS.update({"HighRes-Fix Script": TSC_HighRes_Fix})
|
NODE_CLASS_MAPPINGS.update({"HighRes-Fix Script": TSC_HighRes_Fix})
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
'''# FUTURE
|
'''
|
||||||
# Tiled Sampling KSamplers (https://github.com/BlenderNeko/ComfyUI_TiledKSampler)
|
# Tiled Sampling KSamplers (https://github.com/BlenderNeko/ComfyUI_TiledKSampler)
|
||||||
blenderneko_tiled_ksampler_path = os.path.join(custom_nodes_dir, "ComfyUI_TiledKSampler")
|
blenderneko_tiled_ksampler_path = os.path.join(custom_nodes_dir, "ComfyUI_TiledKSampler")
|
||||||
if os.path.exists(blenderneko_tiled_ksampler_path):
|
if os.path.exists(blenderneko_tiled_ksampler_path):
|
||||||
@@ -4120,14 +4138,20 @@ if os.path.exists(blenderneko_tiled_ksampler_path):
|
|||||||
blenderneko_tiled_nodes = import_module("ComfyUI_TiledKSampler.nodes")
|
blenderneko_tiled_nodes = import_module("ComfyUI_TiledKSampler.nodes")
|
||||||
print(f"\r{message('Efficiency Nodes:')} {printout}{success('Success!')}")
|
print(f"\r{message('Efficiency Nodes:')} {printout}{success('Success!')}")
|
||||||
|
|
||||||
# TSC Tiled Sampling
|
# TSC Tiled Upscaler
|
||||||
class TSC_Tiled_Sampling:
|
class TSC_Tiled_Upscaler:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {"required": {"tile_width": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}),
|
# Split the list based on the keyword "tile"
|
||||||
"tile_height": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}),
|
cnet_tile_filenames = [name for name in folder_paths.get_filename_list("controlnet") if "tile" in name]
|
||||||
|
cnet_other_filenames = [name for name in folder_paths.get_filename_list("controlnet") if "tile" not in name]
|
||||||
|
|
||||||
|
return {"required": {"tile_controlnet": (cnet_tile_filenames + cnet_other_filenames,),
|
||||||
|
"upscale_by": ("FLOAT", {"default": 1.25, "min": 0.01, "max": 8.0, "step": 0.25}),
|
||||||
|
"tile_size": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}),
|
||||||
"tiling_strategy": (["random", "random strict", "padded", 'simple', 'none'],),
|
"tiling_strategy": (["random", "random strict", "padded", 'simple', 'none'],),
|
||||||
|
"tiling_steps": ("INT", {"default": 30, "min": 1, "max": 10000}),
|
||||||
|
"denoise": ("FLOAT", {"default": .56, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
},
|
},
|
||||||
"optional": {"script": ("SCRIPT",)}}
|
"optional": {"script": ("SCRIPT",)}}
|
||||||
|
|
||||||
@@ -4135,13 +4159,14 @@ if os.path.exists(blenderneko_tiled_ksampler_path):
|
|||||||
FUNCTION = "tiled_sampling"
|
FUNCTION = "tiled_sampling"
|
||||||
CATEGORY = "Efficiency Nodes/Scripts"
|
CATEGORY = "Efficiency Nodes/Scripts"
|
||||||
|
|
||||||
def tiled_sampling(self, tile_width, tile_height, tiling_strategy, script=None):
|
def tiled_sampling(self, upscale_by, tile_controlnet, tile_size, tiling_strategy, tiling_steps, denoise, script=None):
|
||||||
if tiling_strategy != 'none':
|
if tiling_strategy != 'none':
|
||||||
script = script or {}
|
script = script or {}
|
||||||
script["tile"] = (tile_width, tile_height, tiling_strategy, blenderneko_tiled_nodes)
|
script["tile"] = (upscale_by, ControlNetLoader().load_controlnet(tile_controlnet)[0],
|
||||||
|
tile_size, tiling_strategy, tiling_steps, denoise, blenderneko_tiled_nodes)
|
||||||
return (script,)
|
return (script,)
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS.update({"Tiled Sampling Script": TSC_Tiled_Sampling})
|
NODE_CLASS_MAPPINGS.update({"Tiled Upscaler Script": TSC_Tiled_Upscaler})
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print(f"\r{message('Efficiency Nodes:')} {printout}{error('Failed!')}")
|
print(f"\r{message('Efficiency Nodes:')} {printout}{error('Failed!')}")
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ let colorKeys = Object.keys(COLOR_THEMES).filter(key => key !== "none");
|
|||||||
shuffleArray(colorKeys); // Shuffle the color themes initially
|
shuffleArray(colorKeys); // Shuffle the color themes initially
|
||||||
|
|
||||||
function setNodeColors(node, theme) {
|
function setNodeColors(node, theme) {
|
||||||
|
if (!theme) {return;}
|
||||||
node.shape = "box";
|
node.shape = "box";
|
||||||
if(theme.nodeColor && theme.nodeBgColor) {
|
if(theme.nodeColor && theme.nodeBgColor) {
|
||||||
node.color = theme.nodeColor;
|
node.color = theme.nodeColor;
|
||||||
@@ -79,9 +80,10 @@ const ext = {
|
|||||||
let colorKey = NODE_COLORS[title];
|
let colorKey = NODE_COLORS[title];
|
||||||
|
|
||||||
if (colorKey === "random") {
|
if (colorKey === "random") {
|
||||||
if (colorKeys.length === 0) {
|
// Check for a valid color key before popping
|
||||||
colorKeys = Object.values(COLOR_THEMES).filter(theme => theme.nodeColor && theme.nodeBgColor);
|
if (colorKeys.length === 0 || !COLOR_THEMES[colorKeys[colorKeys.length - 1]]) {
|
||||||
shuffleArray(colorKeys); // Reshuffle when out of colors
|
colorKeys = Object.keys(COLOR_THEMES).filter(key => key !== "none");
|
||||||
|
shuffleArray(colorKeys);
|
||||||
}
|
}
|
||||||
colorKey = colorKeys.pop();
|
colorKey = colorKeys.pop();
|
||||||
}
|
}
|
||||||
|
|||||||
89
tsc_utils.py
89
tsc_utils.py
@@ -472,10 +472,20 @@ def global_preview_method():
|
|||||||
#-----------------------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------------------
|
||||||
# Auto install Efficiency Nodes Python package dependencies
|
# Auto install Efficiency Nodes Python package dependencies
|
||||||
import subprocess
|
import subprocess
|
||||||
# Note: Auto installer install packages inside the requirements.txt.
|
# Note: This auto-installer attempts to import packages listed in the requirements.txt.
|
||||||
# It first trys ComfyUI's python_embedded folder if python.exe exists inside ...\ComfyUI_windows_portable\python_embeded.
|
# If the import fails, indicating the package isn't installed, the installer proceeds to install the package.
|
||||||
# If no python.exe is found, it attempts a general global pip install of packages.
|
# It first checks if python.exe exists inside the ...\ComfyUI_windows_portable\python_embeded directory.
|
||||||
# On an error, an user is directed to attempt manually installing the packages themselves.
|
# If python.exe is found in that location, it will use this embedded Python version for the installation.
|
||||||
|
# Otherwise, it uses the Python interpreter that's currently executing the script (via sys.executable)
|
||||||
|
# to attempt a general pip install of the packages. If any errors occur during installation, an error message is
|
||||||
|
# printed with the reason for the failure, and the user is directed to manually install the required packages.
|
||||||
|
|
||||||
|
def is_package_installed(pkg_name):
|
||||||
|
try:
|
||||||
|
__import__(pkg_name)
|
||||||
|
return True
|
||||||
|
except ImportError:
|
||||||
|
return False
|
||||||
|
|
||||||
def install_packages(my_dir):
|
def install_packages(my_dir):
|
||||||
# Compute path to the target site-packages
|
# Compute path to the target site-packages
|
||||||
@@ -483,71 +493,48 @@ def install_packages(my_dir):
|
|||||||
embedded_python_exe = os.path.abspath(os.path.join(my_dir, '..', '..', '..', 'python_embeded', 'python.exe'))
|
embedded_python_exe = os.path.abspath(os.path.join(my_dir, '..', '..', '..', 'python_embeded', 'python.exe'))
|
||||||
|
|
||||||
# If embedded_python_exe exists, target the installations. Otherwise, go untargeted.
|
# If embedded_python_exe exists, target the installations. Otherwise, go untargeted.
|
||||||
use_embedded = os.path.exists(embedded_python_exe)
|
use_embedded = os.path.exists(embedded_python_exe) and embedded_python_exe == sys.executable
|
||||||
|
|
||||||
# Load packages from requirements.txt
|
# Load packages from requirements.txt
|
||||||
with open(os.path.join(my_dir, 'requirements.txt'), 'r') as f:
|
with open(os.path.join(my_dir, 'requirements.txt'), 'r') as f:
|
||||||
required_packages = [line.strip() for line in f if line.strip()]
|
required_packages = [line.strip() for line in f if line.strip()]
|
||||||
|
|
||||||
installed_packages = packages(embedded_python_exe if use_embedded else None, versions=False)
|
|
||||||
for pkg in required_packages:
|
for pkg in required_packages:
|
||||||
if pkg not in installed_packages:
|
if not is_package_installed(pkg):
|
||||||
print(f"\033[32mEfficiency Nodes:\033[0m Installing required package '{pkg}'...", end='', flush=True)
|
printout = f"Installing required package '{pkg}'..."
|
||||||
|
print(f"{message('Efficiency Nodes:')} {printout}", end='', flush=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if use_embedded: # Targeted installation
|
if use_embedded: # Targeted installation
|
||||||
subprocess.check_call(['pip', 'install', pkg, '--target=' + target_dir, '--no-warn-script-location',
|
subprocess.check_call([embedded_python_exe, '-m', 'pip', 'install', pkg, '--target=' + target_dir,
|
||||||
'--disable-pip-version-check'], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
'--no-warn-script-location', '--disable-pip-version-check'],
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, timeout=7)
|
||||||
else: # Untargeted installation
|
else: # Untargeted installation
|
||||||
subprocess.check_call(['pip', 'install', pkg], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
subprocess.check_call([sys.executable, "-m", "pip", 'install', pkg],
|
||||||
print(f"\r\033[32mEfficiency Nodes:\033[0m Installing required package '{pkg}'... Installed!", flush=True)
|
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, timeout=7)
|
||||||
|
print(f"\r{message('Efficiency Nodes:')} {printout}{success(' Installed!')}", flush=True)
|
||||||
except subprocess.CalledProcessError as e: # Failed installation
|
except Exception as e:
|
||||||
base_message = f"\r\033[31mEfficiency Nodes Error:\033[0m Failed to install python package '{pkg}'. "
|
print(f"\r{message('Efficiency Nodes:')} {printout}{error(' Failed!')}", flush=True)
|
||||||
if e.stderr:
|
print(f"{warning(str(e))}")
|
||||||
error_message = e.stderr.decode()
|
|
||||||
print(base_message + f"Error message: {error_message}")
|
def print_general_error_message():
|
||||||
else:
|
print(f"{message('Efficiency Nodes:')} An unexpected error occurred during the package installation process. {error('Failed!')}")
|
||||||
print(base_message + "\nPlease check your permissions, network connectivity, or try a manual installation.")
|
print(warning("Please try manually installing the required packages from the requirements.txt file."))
|
||||||
|
|
||||||
def packages(python_exe=None, versions=False):
|
|
||||||
# Get packages of the active or embedded Python environment
|
|
||||||
if python_exe:
|
|
||||||
return [(r.decode().split('==')[0] if not versions else r.decode()) for r in
|
|
||||||
subprocess.check_output([python_exe, '-m', 'pip', 'freeze']).split()]
|
|
||||||
else:
|
|
||||||
return [(r.split('==')[0] if not versions else r) for r in subprocess.getoutput('pip freeze').splitlines()]
|
|
||||||
|
|
||||||
# Install missing packages
|
# Install missing packages
|
||||||
install_packages(my_dir)
|
install_packages(my_dir)
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------------------
|
||||||
# Auto install efficiency nodes web extensions '\js\' to 'ComfyUI\web\extensions'
|
# Delete efficiency nodes web extensions from 'ComfyUI\web\extensions'.
|
||||||
|
# Pull https://github.com/comfyanonymous/ComfyUI/pull/1273 now allows defining web extensions through a dir path in init
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
# Source and destination directories
|
# Destination directory
|
||||||
source_dir = os.path.join(my_dir, 'js')
|
|
||||||
destination_dir = os.path.join(comfy_dir, 'web', 'extensions', 'efficiency-nodes-comfyui')
|
destination_dir = os.path.join(comfy_dir, 'web', 'extensions', 'efficiency-nodes-comfyui')
|
||||||
|
|
||||||
# Create the destination directory if it doesn't exist
|
# Check if the directory exists and delete it
|
||||||
os.makedirs(destination_dir, exist_ok=True)
|
if os.path.exists(destination_dir):
|
||||||
|
shutil.rmtree(destination_dir)
|
||||||
# Get a list of all .js files in the source directory
|
|
||||||
source_files = [f for f in os.listdir(source_dir) if f.endswith('.js')]
|
|
||||||
|
|
||||||
# Clear files in the destination directory that aren't in the source directory
|
|
||||||
for file_name in os.listdir(destination_dir):
|
|
||||||
if file_name not in source_files and file_name.endswith('.js'):
|
|
||||||
file_path = os.path.join(destination_dir, file_name)
|
|
||||||
os.unlink(file_path)
|
|
||||||
|
|
||||||
# Iterate over all files in the source directory for copying
|
|
||||||
for file_name in source_files:
|
|
||||||
# Full paths for source and destination
|
|
||||||
source_path = os.path.join(source_dir, file_name)
|
|
||||||
destination_path = os.path.join(destination_dir, file_name)
|
|
||||||
|
|
||||||
# Directly copy the file (this will overwrite if the file already exists)
|
|
||||||
shutil.copy2(source_path, destination_path)
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------------------
|
||||||
# Establish a websocket connection to communicate with "efficiency-nodes.js" under:
|
# Establish a websocket connection to communicate with "efficiency-nodes.js" under:
|
||||||
|
|||||||
Reference in New Issue
Block a user