Add files via upload

This commit is contained in:
FaberVS
2025-04-15 14:00:55 +03:00
committed by GitHub
commit eddaa3cf2a
3 changed files with 222 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
import comfy.sd
import comfy.samplers # For accessing lists of SAMPLERS and SCHEDULERS
import folder_paths
import json
import inspect # For type verification
# Define a custom type for "pipe" for better readability
PIPE_TYPE_NAME = "PARAMS_PIPE"
# --- Node 1: ModelParamsPipeNode ---
class ModelParamsPipeNode:
@classmethod
def INPUT_TYPES(cls):
samplers = comfy.samplers.KSampler.SAMPLERS
schedulers = comfy.samplers.KSampler.SCHEDULERS
return {
"required": {
"ckpt_name": (folder_paths.get_filename_list("checkpoints"),),
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0, "step": 0.1}),
"sampler_name": (samplers,),
"scheduler": (schedulers,),
"positive_tag": ("STRING", {"multiline": True, "default": ""}),
"negative_tag": ("STRING", {"multiline": True, "default": ""}),
"clip_skip": ("INT", {"default": -2, "min": -12, "max": 0, "step": 1}),
},
"optional": {
"vae_override": ("VAE",),
}
}
RETURN_TYPES = ("MODEL", "CLIP", "VAE",
"INT", "FLOAT", "STRING", "STRING", "STRING", "STRING",
PIPE_TYPE_NAME)
RETURN_NAMES = ("MODEL", "CLIP", "VAE",
"steps", "cfg", "sampler_name", "scheduler",
"positive_tag", "negative_tag",
"params_pipe")
FUNCTION = "process_to_pipe"
CATEGORY = "MultiModel"
def process_to_pipe(self, ckpt_name, steps, cfg, sampler_name, scheduler,
positive_tag, negative_tag, clip_skip, vae_override=None):
ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings"))
model = out[0]
clip = out[1]
vae_from_checkpoint = out[2]
clip_skipped = clip.clone()
clip_skipped.clip_layer(clip_skip)
final_vae = vae_override if vae_override is not None else vae_from_checkpoint
combined_params = {
"ckpt_name": ckpt_name, "steps": steps, "cfg": cfg,
"sampler_name": str(sampler_name), "scheduler": str(scheduler),
"positive_tag": positive_tag.strip() if isinstance(positive_tag, str) else positive_tag,
"negative_tag": negative_tag.strip() if isinstance(negative_tag, str) else negative_tag,
"clip_skip": clip_skip
}
pipe_data = (model, clip_skipped, final_vae, combined_params)
return (model, clip_skipped, final_vae, steps, cfg, str(sampler_name), str(scheduler),
positive_tag, negative_tag, pipe_data)
# --- Node 2: ParamsPipeUnpack ---
class ParamsPipeUnpack:
@classmethod
def INPUT_TYPES(cls):
return { "required": { "params_pipe": (PIPE_TYPE_NAME,), } }
RETURN_TYPES = ("MODEL", "CLIP", "VAE", "INT", "FLOAT",
comfy.samplers.KSampler.SAMPLERS, comfy.samplers.KSampler.SCHEDULERS,
"STRING", "STRING", PIPE_TYPE_NAME)
RETURN_NAMES = ("MODEL", "CLIP", "VAE", "steps", "cfg",
"sampler_name", "scheduler",
"positive_tag", "negative_tag", "params_pipe")
FUNCTION = "unpack_pipe"
CATEGORY = "MultiModel"
def unpack_pipe(self, params_pipe):
if not isinstance(params_pipe, tuple) or len(params_pipe) != 4:
raise ValueError(f"Unpack: Expected PARAMS_PIPE tuple of length 4, but got: {type(params_pipe)}")
model, clip, vae, params_dict = params_pipe
if not isinstance(params_dict, dict):
raise ValueError(f"Unpack: Expected dict as the 4th element in PARAMS_PIPE, but got: {type(params_dict)}")
steps = params_dict.get("steps")
cfg = params_dict.get("cfg")
sampler_name = params_dict.get("sampler_name")
scheduler = params_dict.get("scheduler")
positive_tag = params_dict.get("positive_tag")
negative_tag = params_dict.get("negative_tag")
return (model, clip, vae, steps, cfg, sampler_name, scheduler,
positive_tag, negative_tag, params_pipe)
# --- Registration for only the first two nodes ---
NODE_CLASS_MAPPINGS = {
"ModelParamsPipeNode": ModelParamsPipeNode,
"ParamsPipeUnpack": ParamsPipeUnpack
}
# --- Display names for only the first two nodes ---
NODE_DISPLAY_NAME_MAPPINGS = {
"ModelParamsPipeNode": "Model Parameters Pipe Hub",
"ParamsPipeUnpack": "Unpack Parameters Pipe"
}
# Loading message
print("✅ Loaded custom nodes (Reverted): ModelParamsPipeNode, ParamsPipeUnpack ▪B ▪B ▪С")
+103
View File
@@ -0,0 +1,103 @@
class ListSelectorNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# Input for multiline text where each line is a list item
"list_items": ("STRING", {"multiline": True, "default": "Item 1\nItem 2\nItem 3"}),
# Input for selecting the item's number (1-based index)
"index": ("INT", {"default": 1, "min": 1, "max": 10000, "step": 1}),
}
}
# Return types: selected string and its number
RETURN_TYPES = ("STRING", "INT")
# Output names
RETURN_NAMES = ("selected_item", "selected_index")
FUNCTION = "get_item_by_index"
CATEGORY = "MultiModel" # Category for the node
def get_item_by_index(self, list_items, index):
# Split the input text into lines
lines = list_items.splitlines()
# Filter out empty lines and trim unnecessary spaces
items = [line.strip() for line in lines if line.strip()]
selected_item_str = ""
selected_item_index = 1 # Default to the first item
if not items:
# If the list is empty after filtering
print("Warning: ListSelectorNode - Input list is empty.")
selected_item_str = ""
selected_item_index = 1 # Nothing to select, return index 1
else:
# Adjust the 1-based input index to 0-based for Python list access
zero_based_index = index - 1
# Check if the index is within bounds
# If index is too small (<0), use 0
if zero_based_index < 0:
zero_based_index = 0
# If index is too large, use the index of the last item
elif zero_based_index >= len(items):
zero_based_index = len(items) - 1
# Get the selected item
selected_item_str = items[zero_based_index]
# Determine the actual 1-based index of the selected item
selected_item_index = zero_based_index + 1
# Return the selected string and its 1-based index
return (selected_item_str, selected_item_index)
class DenoiseSelector:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"denoise": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
}}
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("denoise",)
FUNCTION = "select_denoise"
CATEGORY = "MultiModel"
def select_denoise(self, denoise):
return (denoise,)
class DualTextInput:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# Text field for the first text (visible on the node)
"text_positive": ("STRING", {"multiline": True, "default": ""}),
# Text field for the second text (visible on the node)
"text_negative": ("STRING", {"multiline": True, "default": ""}),
}
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("pos", "neg")
FUNCTION = "process_text"
CATEGORY = "MultiModel"
def process_text(self, text_positive, text_negative):
# Return values from the fields using correct variable names
return (text_positive, text_negative)
NODE_CLASS_MAPPINGS = {
"DenoiseSelector": DenoiseSelector,
"ListSelectorNode": ListSelectorNode,
"DualTextInput": DualTextInput
}
NODE_DISPLAY_NAME_MAPPINGS = {
"DenoiseSelector": "Denoise Selector",
"ListSelectorNode": "Select_from_list_by_index",
"DualTextInput": "pos/neg text"
}
+12
View File
@@ -0,0 +1,12 @@
from .WF_nodes import NODE_CLASS_MAPPINGS as DENOISE_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as DENOISE_NODE_DISPLAY_NAME_MAPPINGS
from .ModelParamsNode import NODE_CLASS_MAPPINGS as MODEL_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as MODEL_NODE_DISPLAY_NAME_MAPPINGS
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(DENOISE_NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(MODEL_NODE_CLASS_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(DENOISE_NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(MODEL_NODE_DISPLAY_NAME_MAPPINGS)