3v324v23's picture
lfs
1e3b872
#---------------------------------------------------------------------------------------------------------------------#
# Comfyroll Studio custom nodes by RockOfFire and Akatsuzi https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes
# for ComfyUI https://github.com/comfyanonymous/ComfyUI
#---------------------------------------------------------------------------------------------------------------------#
import torch
import numpy as np
import os
import sys
import folder_paths
import re
import comfy.sd
import csv
import math
import random
from PIL import Image, ImageSequence
from pathlib import Path
from itertools import product
from ..categories import icons
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
def tensor2rgba(t: torch.Tensor) -> torch.Tensor:
size = t.size()
if (len(size) < 4):
return t.unsqueeze(3).repeat(1, 1, 1, 4)
elif size[3] == 1:
return t.repeat(1, 1, 1, 4)
elif size[3] == 3:
alpha_tensor = torch.ones((size[0], size[1], size[2], 1))
return torch.cat((t, alpha_tensor), dim=3)
else:
return t
class AnyType(str):
"""A special type that can be connected to any other types. Credit to pythongosssss"""
def __ne__(self, __value: object) -> bool:
return False
any_type = AnyType("*")
def get_input_folder(input_folder, input_path):
# Set the input path
if input_path != '' and input_path is not None:
if not os.path.exists(input_path):
print(f"[Warning] CR Image List: The input_path `{input_path}` does not exist")
return ("",)
in_path = input_path
else:
input_dir = folder_paths.input_directory
in_path = os.path.join(input_dir, input_folder)
return in_path
#---------------------------------------------------------------------------------------------------------------------#
# List Nodes
#---------------------------------------------------------------------------------------------------------------------#
class CR_TextList:
@classmethod
def INPUT_TYPES(s):
return {"required": {"multiline_text": ("STRING", {"multiline": True, "default": "text"}),
"start_index": ("INT", {"default": 0, "min": 0, "max": 9999}),
"max_rows": ("INT", {"default": 1000, "min": 1, "max": 9999}),
}
}
RETURN_TYPES = ("STRING", "STRING", )
RETURN_NAMES = ("STRING", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = "make_list"
CATEGORY = icons.get("Comfyroll/List")
def make_list(self, multiline_text, start_index, max_rows, loops):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-text-list"
lines = multiline_text.split('\n')
# Ensure start_index is within the bounds of the list
start_index = max(0, min(start_index, len(lines) - 1))
# Calculate the end index based on max_rows
end_index = min(start_index + max_rows, len(lines))
# Extract the desired portion of the list
selected_rows = lines[start_index:end_index]
return (selected_rows, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_PromptList:
@classmethod
def INPUT_TYPES(s):
return {"required": {"prepend_text": ("STRING", {"multiline": False, "default": ""}),
"multiline_text": ("STRING", {"multiline": True, "default": "body_text"}),
"append_text": ("STRING", {"multiline": False, "default": ""}),
"start_index": ("INT", {"default": 0, "min": 0, "max": 9999}),
"max_rows": ("INT", {"default": 1000, "min": 1, "max": 9999}),
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING", )
RETURN_NAMES = ("prompt", "body_text", "show_help", )
OUTPUT_IS_LIST = (True, True, False)
FUNCTION = "make_list"
CATEGORY = icons.get("Comfyroll/List")
def make_list(self, multiline_text, prepend_text="", append_text="", start_index=0, max_rows=9999):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-prompt-list"
lines = multiline_text.split('\n')
# Ensure start_index is within the bounds of the list
start_index = max(0, min(start_index, len(lines) - 1))
# Calculate the end index based on max_rows
end_index = min(start_index + max_rows, len(lines))
# Extract the desired portion of the list
selected_rows = lines[start_index:end_index]
prompt_list_out = [prepend_text + line + append_text for line in selected_rows]
body_list_out = selected_rows
return (prompt_list_out, body_list_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_LoadImageList:
@classmethod
def INPUT_TYPES(s):
input_dir = folder_paths.input_directory
image_folder = [name for name in os.listdir(input_dir) if os.path.isdir(os.path.join(input_dir,name))]
return {"required": {"input_folder": (sorted(image_folder), ),
"start_index": ("INT", {"default": 0, "min": 0, "max": 9999}),
"max_images": ("INT", {"default": 1, "min": 1, "max": 9999}),
},
"optional": {"input_path": ("STRING", {"default": '', "multiline": False}),
}
}
RETURN_TYPES = ("IMAGE", "STRING", )
RETURN_NAMES = ("IMAGE", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = "make_list"
CATEGORY = icons.get("Comfyroll/List/IO")
def make_list(self, start_index, max_images, input_folder, input_path=None):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-image-list"
# Set the input path
if input_path != '' and input_path is not None:
if not os.path.exists(input_path):
print(f"[Warning] CR Image List: The input_path `{input_path}` does not exist")
return ("",)
in_path = input_path
else:
input_dir = folder_paths.input_directory
in_path = os.path.join(input_dir, input_folder)
# Check if the folder is empty
if not os.listdir(in_path):
print(f"[Warning] CR Image List: The folder `{in_path}` is empty")
return None
file_list = sorted(os.listdir(in_path), key=lambda s: sum(((s, int(n)) for s, n in re.findall(r'(\D+)(\d+)', 'a%s0' % s)), ()))
image_list = []
# Ensure start_index is within the bounds of the list
start_index = max(0, min(start_index, len(file_list) - 1))
# Calculate the end index based on max_rows
end_index = min(start_index + max_images, len(file_list) - 1)
for num in range(start_index, end_index):
img = Image.open(os.path.join(in_path, file_list[num]))
image = img.convert("RGB")
image_list.append(pil2tensor(image))
if not image_list:
# Handle the case where the list is empty
print("CR Load Image List: No images found.")
return None
images = torch.cat(image_list, dim=0)
images_out = [images[i:i + 1, ...] for i in range(images.shape[0])]
return (images_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_LoadImageListPlus:
@classmethod
def INPUT_TYPES(s):
input_dir = folder_paths.input_directory
image_folder = [name for name in os.listdir(input_dir) if os.path.isdir(os.path.join(input_dir,name))]
return {"required": {"input_folder": (sorted(image_folder), ),
"start_index": ("INT", {"default": 0, "min": 0, "max": 99999}),
"max_images": ("INT", {"default": 1, "min": 1, "max": 99999}),
},
"optional": {"input_path": ("STRING", {"default": '', "multiline": False}),
}
}
RETURN_TYPES = ("IMAGE", "MASK", "INT", "STRING", "INT", "INT", "INT", "STRING", )
RETURN_NAMES = ("IMAGE", "MASK", "index", "filename", "width", "height", "list_length", "show_help", )
OUTPUT_IS_LIST = (True, True, True, True, False, False, False, False)
FUNCTION = "make_list"
CATEGORY = icons.get("Comfyroll/List/IO")
def make_list(self, start_index, max_images, input_folder, input_path=None, vae=None):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-image-list-plus"
# Set the input path
if input_path != '' and input_path is not None:
if not os.path.exists(input_path):
print(f"[Warning] CR Image List: The input_path `{input_path}` does not exist")
return ("",)
in_path = input_path
else:
input_dir = folder_paths.input_directory
in_path = os.path.join(input_dir, input_folder)
# Check if the folder is empty
if not os.listdir(in_path):
print(f"[Warning] CR Image List: The folder `{in_path}` is empty")
return None
file_list = sorted(os.listdir(in_path), key=lambda s: sum(((s, int(n)) for s, n in re.findall(r'(\D+)(\d+)', 'a%s0' % s)), ()))
image_list = []
mask_list = []
index_list = []
filename_list = []
exif_list = []
# Ensure start_index is within the bounds of the list
start_index = max(0, min(start_index, len(file_list) - 1))
# Calculate the end index based on max_rows
end_index = min(start_index + max_images, len(file_list) - 1)
for num in range(start_index, end_index):
filename = file_list[num]
img_path = os.path.join(in_path, filename)
img = Image.open(os.path.join(in_path, file_list[num]))
image_list.append(pil2tensor(img.convert("RGB")))
tensor_img = pil2tensor(img)
mask_list.append(tensor2rgba(tensor_img)[:,:,:,0])
# Populate the image index
index_list.append(num)
# Populate the filename_list
filename_list.append(filename)
if not image_list:
# Handle the case where the list is empty
print("CR Load Image List: No images found.")
return None
width, height = Image.open(os.path.join(in_path, file_list[start_index])).size
images = torch.cat(image_list, dim=0)
images_out = [images[i:i + 1, ...] for i in range(images.shape[0])]
masks = torch.cat(mask_list, dim=0)
mask_out = [masks[i:i + 1, ...] for i in range(masks.shape[0])]
list_length = end_index - start_index
return (images_out, mask_out, index_list, filename_list, index_list, width, height, list_length, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_LoadGIFAsList:
@classmethod
def INPUT_TYPES(cls):
input_dir = folder_paths.input_directory
image_folder = [name for name in os.listdir(input_dir) if os.path.isdir(os.path.join(input_dir,name))]
return {"required": {"input_folder": (sorted(image_folder), ),
"gif_filename": ("STRING", {"multiline": False, "default": "text"}),
"start_frame": ("INT", {"default": 0, "min": 0, "max": 99999}),
"max_frames": ("INT", {"default": 1, "min": 1, "max": 99999}),
},
"optional": {"input_path": ("STRING", {"default": '', "multiline": False}),
}
}
RETURN_TYPES = ("IMAGE", "MASK", "STRING", )
RETURN_NAMES = ("IMAGE", "MASK", "show_help", )
OUTPUT_IS_LIST = (True, True, False)
FUNCTION = "load_gif"
CATEGORY = icons.get("Comfyroll/List/IO")
def load_gif(self, input_folder, gif_filename, start_frame, max_frames, input_path=None):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-load-gif-images"
# Set the input path
if input_path != '' and input_path is not None:
if not os.path.exists(input_path):
print(f"[Warning] CR Image List: The input_path `{input_path}` does not exist")
return ("",)
in_path = input_path
else:
input_dir = folder_paths.input_directory
in_path = os.path.join(input_dir, input_folder)
# Construct the GIF file path
gif_file_path = os.path.join(in_path, gif_filename)
frames_list = []
masks_list = []
try:
# Open the GIF file
with Image.open(gif_file_path) as gif_image:
for i, frame in enumerate(ImageSequence.Iterator(gif_image)):
if i < start_frame:
continue # Skip frames until reaching the start_frame
if max_frames is not None and i >= start_frame + max_frames:
break # Stop after max_frames frames
# Extract frame
img = frame.copy()
width, height = img.size
frames_list.append(pil2tensor(img.convert("RGB")))
tensor_img = pil2tensor(img)
masks_list.append(tensor2rgba(tensor_img)[:,:,:,0])
# Convert frames to tensor list
images = torch.cat(frames_list, dim=0)
images_out = [images[i:i + 1, ...] for i in range(images.shape[0])]
# Convert masks to tensor list
masks = torch.cat(masks_list, dim=0)
masks_out = [masks[i:i + 1, ...] for i in range(masks.shape[0])]
return (images_out, masks_out, show_help, )
except Exception as e:
print(f"Error: {e}")
return (None, None, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_FontFileList:
@classmethod
def INPUT_TYPES(s):
comfyroll_font_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "fonts")
comfyroll_file_list = [f for f in os.listdir(comfyroll_font_dir) if os.path.isfile(os.path.join(comfyroll_font_dir, f)) and f.lower().endswith(".ttf")]
sources = ["system", "Comfyroll", "from folder"]
return {"required": {"source_folder": (sources,),
"start_index": ("INT", {"default": 0, "min": 0, "max": 9999}),
"max_rows": ("INT", {"default": 1000, "min": 1, "max": 9999}),
},
"optional": {"folder_path": ("STRING", {"default": "C:\Windows\Fonts", "multiline": False}),
}
}
RETURN_TYPES = (any_type, "STRING", )
RETURN_NAMES = ("LIST", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = "make_list"
CATEGORY = icons.get("Comfyroll/List/IO")
def make_list(self, source_folder, start_index, max_rows, folder_path="C:\Windows\Fonts"):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-font-file-list"
if source_folder == "system":
system_root = os.environ.get('SystemRoot')
system_font_dir = os.path.join(system_root, 'Fonts')
file_list = [f for f in os.listdir(system_font_dir) if os.path.isfile(os.path.join(system_font_dir, f)) and f.lower().endswith(".ttf")]
elif source_folder == "Comfyroll":
comfyroll_font_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "fonts")
file_list = [f for f in os.listdir(comfyroll_font_dir) if os.path.isfile(os.path.join(comfyroll_font_dir, f)) and f.lower().endswith(".ttf")]
elif source_folder == "from folder":
if folder_path != '' and folder_path is not None:
if not os.path.exists(folder_path):
print(f"[Warning] CR Font File List: The folder_path `{folder_path}` does not exist")
return None
font_dir = folder_path
file_list = [f for f in os.listdir(font_dir) if os.path.isfile(os.path.join(font_dir, f)) and f.lower().endswith(".ttf")]
else:
print(f"[Warning] CR Font File List: No folder_path entered")
return None
else:
pass
# Ensure start_index is within the bounds of the list
start_index = max(0, min(start_index, len(file_list) - 1))
# Calculate the end index based on max_rows
end_index = min(start_index + max_rows, len(file_list))
# Extract the desired portion of the list
selected_files = file_list[start_index:end_index]
return (selected_files, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_FloatRangeList:
@classmethod
def INPUT_TYPES(s):
operations = ["none","sin","cos","tan"]
return {"required": {"start": ("FLOAT", {"default": 0.00, "min": -99999.99, "max": 99999.99, "step": 0.01}),
"end": ("FLOAT", {"default": 1.00, "min": -99999.99, "max": 99999.99, "step": 0.01}),
"step": ("FLOAT", {"default": 1.00, "min": -99999.99, "max": 99999.99, "step": 0.01}),
"operation": (operations, ),
"decimal_places": ("INT", {"default": 2, "min": 0, "max": 10}),
"ignore_first_value": ("BOOLEAN", {"default": True}),
"max_values_per_loop": ("INT", {"default": 128, "min": 1, "max": 99999}),
"loops": ("INT", {"default": 1, "min": 1, "max": 999}),
"ping_pong": ("BOOLEAN", {"default": False}),
},
}
RETURN_TYPES = ("FLOAT", "STRING",)
RETURN_NAMES = ("FLOAT", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = 'make_range'
CATEGORY = icons.get("Comfyroll/List")
def make_range(self, start, end, step, max_values_per_loop, operation, decimal_places, ignore_first_value, loops, ping_pong):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-float-range-list"
range_values = list()
for i in range(loops):
if end < start and step > 0:
step = -step
current_range = list(np.arange(start, end + step, step))
# Apply math operations to each value in the range
if operation == "sin":
current_range = [math.sin(value) for value in current_range]
elif operation == "cos":
current_range = [math.cos(value) for value in current_range]
elif operation == "tan":
current_range = [math.tan(value) for value in current_range]
current_range = [round(value, decimal_places) for value in current_range]
if ping_pong:
# Reverse the direction of the range on even iterations
if i % 2 == 1:
if ignore_first_value:
current_range = current_range[:-1]
current_range = current_range[:max_values_per_loop]
range_values += reversed(current_range)
else:
if ignore_first_value:
current_range = current_range[1:]
current_range = current_range[:max_values_per_loop]
range_values += current_range
else:
if ignore_first_value:
current_range = current_range[1:]
current_range = current_range[:max_values_per_loop]
range_values += current_range
return (range_values, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_IntegerRangeList:
@classmethod
def INPUT_TYPES(s):
return {"required": {"start": ("INT", {"default": 0, "min": -99999, "max": 99999}),
"end": ("INT", {"default": 0, "min": -99999, "max": 99999}),
"step": ("INT", {"default": 1, "min": 1, "max": 99999}),
"loops": ("INT", {"default": 1, "min": 1, "max": 999}),
"ping_pong": ("BOOLEAN", {"default": False}),
},
}
RETURN_TYPES = ("INT", "STRING",)
RETURN_NAMES = ("INT", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = 'make_range'
CATEGORY = icons.get("Comfyroll/List")
def make_range(self, start, end, step, loops, ping_pong):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-list-schedule"
range_values = list()
for i in range(loops):
current_range = list(range(start, end, step))
if ping_pong:
# Reverse the direction of the range on even iterations
if i % 2 == 1:
range_values += reversed(current_range)
else:
range_values += current_range
else:
range_values += current_range
return (range_values, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_LoadTextList:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"input_file_path": ("STRING", {"multiline": False, "default": ""}),
"file_name": ("STRING", {"multiline": False, "default": ""}),
"file_extension": (["txt", "csv"],),
}
}
RETURN_TYPES = ("STRING", "STRING",)
RETURN_NAMES = ("STRING", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = 'load_list'
CATEGORY = icons.get("Comfyroll/List")
def load_list(self, input_file_path, file_name, file_extension):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-load-value-list"
filepath = input_file_path + "\\" + file_name + "." + file_extension
print(f"CR Load Values: Loading {filepath}")
list = []
if file_extension == "csv":
with open(filepath, "r") as csv_file:
for row in csv_file:
list.append(row)
elif file_extension == "txt":
with open(filepath, "r") as txt_file:
for row in txt_file:
list.append(row)
else:
pass
return(list, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_IntertwineLists:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"list1": ("STRING", {"multiline": True, "default": "", "forceInput": True}),
"list2": ("STRING", {"multiline": True, "default": "", "forceInput": True}),
}
}
RETURN_TYPES = ("STRING", "STRING",)
RETURN_NAMES = ("STRING", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = 'make_list'
CATEGORY = icons.get("Comfyroll/List/Utils")
def make_list(self, list1, list2):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-intertwine-lists"
# Ensure both lists have the same length
min_length = min(len(list1), len(list2))
# Initialize an empty list to store the combined elements
combined_list = []
combined_element = str(list1) + ", " + str(list2)
combined_list.append(combined_element)
return(combined_list, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_BinaryToBitList:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"bit_string": ("STRING", {"multiline": True, "default": ""}),
}
}
RETURN_TYPES = ("STRING", "STRING",)
RETURN_NAMES = ("STRING", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = 'make_list'
CATEGORY = icons.get("Comfyroll/List")
def make_list(self, bit_string):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-binary-to-list"
list_out = [str(bit) for bit in bit_string]
return(list_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_MakeBatchFromImageList:
# based on ImageListToImageBatch by DrLtData
@classmethod
def INPUT_TYPES(s):
return {"required": {"image_list": ("IMAGE", ),}}
RETURN_TYPES = ("IMAGE", "STRING",)
RETURN_NAMES = ("image_batch", "show_help", )
INPUT_IS_LIST = True
FUNCTION = "make_batch"
CATEGORY = icons.get("Comfyroll/List/Utils")
def make_batch(self, image_list):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-binary-to-list"
if len(image_list) <= 1:
return (image_list,)
batched_images = torch.cat(image_list, dim=0)
return (batched_images, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_TextListToString:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"text_list": ("STRING", {"forceInput": True}),
},
}
RETURN_TYPES = ("STRING", "STRING", )
RETURN_NAMES = ("STRING", "show_help", )
INPUT_IS_LIST = True
FUNCTION = "joinlist"
CATEGORY = icons.get("Comfyroll/List/Utils")
def joinlist(self, text_list):
string_out = "\n".join(text_list)
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-text-list-to-string"
return (string_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_SimpleList:
@classmethod
def INPUT_TYPES(s):
return {"required":{
"list_values": ("STRING", {"multiline": True, "default": "text"}),
}
}
RETURN_TYPES = (any_type, "STRING", )
RETURN_NAMES = ("LIST", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = "cross_join"
CATEGORY = icons.get("Comfyroll/List")
def cross_join(self, list_values):
lines = list_values.split('\n')
list_out = [i.strip() for i in lines if i.strip()]
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-simple-list"
return (list_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_XYProduct:
@classmethod
def INPUT_TYPES(s):
return {"required":{
"text_x": ("STRING", {"multiline": True}),
"text_y": ("STRING", {"multiline": True}),
}
}
RETURN_TYPES = (any_type, any_type, "STRING", )
RETURN_NAMES = ("x_values", "y_values", "show_help", )
OUTPUT_IS_LIST = (True, True, False)
FUNCTION = "cross_join"
CATEGORY = icons.get("Comfyroll/List/Utils")
def cross_join(self, text_x, text_y):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-xy-product"
list1 = text_x.strip().split('\n')
list2 = text_y.strip().split('\n')
cartesian_product = list(product(list1, list2))
x_values, y_values = zip(*cartesian_product)
return (list(x_values), list(y_values), show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_Repeater:
@classmethod
def INPUT_TYPES(s):
return {"required":{
"input_data": (any_type, ),
"repeats": ("INT", {"default": 1, "min": 1, "max": 99999}),
}
}
RETURN_TYPES = (any_type, "STRING", )
RETURN_NAMES = ("list", "show_help", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = "repeat_list_items"
CATEGORY = icons.get("Comfyroll/List/Utils")
def repeat_list_items(self, input_data, repeats):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-repeater"
new_list = []
if isinstance(input_data, list):
new_list = []
for item in input_data:
new_list.extend([item] * repeats)
return (new_list, show_help, )
else:
return ([input_data] * repeats, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_TextCycler:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"text": ("STRING", {"multiline": True, "default": ""}),
"repeats": ("INT", {"default": 1, "min": 1, "max": 99999}),
"loops": ("INT", {"default": 1, "min": 1, "max": 99999}),
}
}
RETURN_TYPES = (any_type, "STRING", )
RETURN_NAMES = ("STRING", "show_text", )
OUTPUT_IS_LIST = (True, False)
FUNCTION = "cycle"
CATEGORY = icons.get("Comfyroll/List")
def cycle(self, text, repeats, loops=1):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-text-cycler"
lines = text.split('\n')
list_out = []
for i in range(loops):
for text_item in lines:
for _ in range(repeats):
list_out.append(text_item)
return (list_out, show_help,)
#---------------------------------------------------------------------------------------------------------------------#
class CR_ValueCycler:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"values": ("STRING", {"multiline": True, "default": ""}),
"repeats": ("INT", {"default": 1, "min": 1, "max": 99999}),
"loops": ("INT", {"default": 1, "min": 1, "max": 99999}),
}
}
RETURN_TYPES = ("FLOAT", "INT", "STRING", )
RETURN_NAMES = ("FLOAT", "INT", "show_text", )
OUTPUT_IS_LIST = (True, True, False)
FUNCTION = "cycle"
CATEGORY = icons.get("Comfyroll/List")
def cycle(self, values, repeats, loops=1):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/List-Nodes#cr-value-cycler"
lines = values.split('\n')
float_list_out = []
int_list_out = []
# add check if valid number
for i in range(loops):
for _ in range(repeats):
for text_item in lines:
if all(char.isdigit() or char == '.' for char in text_item.strip()):
float_list_out.append(float(text_item))
int_list_out.append(int(float(text_item))) # Convert to int after parsing as float
return (float_list_out, int_list_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
# MAPPINGS
#---------------------------------------------------------------------------------------------------------------------#
# For reference only, actual mappings are in __init__.py
'''
NODE_CLASS_MAPPINGS = {
### List nodes
"CR Text List": CR_TextList,
"CR Prompt List": CR_PromptList,
"CR Simple List": CR_SimpleList,
"CR Load Image List": CR_LoadImageList,
"CR Load Image List Plus": CR_LoadImageListPlus,
"CR Load GIF As List": CR_LoadGIFAsList,
"CR Float Range List": CR_FloatRangeList,
"CR Integer Range List": CR_FloatRangeList,
"CR Load Text List": CR_LoadTextList,
"CR Font File List": CR_FontFileList,
"CR Binary To Bit List": CR_BinaryToBitList,
"CR Text Cycler": CR_TextCycler,
"CR Value Cycler": CR_ValueCycler,
### List Utils
"CR Batch Images From List": CR_MakeBatchFromImageList,
"CR Intertwine Lists" : CR_IntertwineLists,
"CR Repeater": CR_Repeater,
"CR XY Product": CR_XYProduct,
"CR Text List To String": CR_TextListToString,
}
'''