Files
RockyHong-ComfyUI-FilePaths…/nodes.py
T
2026-01-25 03:59:50 +08:00

217 lines
6.8 KiB
Python

import os
import glob
class FilePathsForLoop:
"""
Iterates through files in a folder one at a time.
Returns each file path individually for processing in a loop.
Connect to any node that processes files, then to FilePathsLoopClose.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"folder_path": ("STRING", {"default": ""}),
"extension": ("STRING", {"default": "*.png"}),
}
}
RETURN_TYPES = ("STRING", "STRING", "INT", "INT")
RETURN_NAMES = ("filepath", "filename", "current_index", "total_count")
FUNCTION = "iterate_files"
CATEGORY = "FilePathsFromFolder/Loop"
OUTPUT_NODE = False
OUTPUT_IS_LIST = (True, True, True, True)
def iterate_files(self, folder_path, extension):
# Build search pattern
search_pattern = os.path.join(folder_path, extension)
# Find all matching files
matched_files = glob.glob(search_pattern)
matched_files.sort()
total_count = len(matched_files)
# If no files, return empty lists
if not matched_files:
return ([], [], [], [])
# Build output lists
filepaths = []
filenames = []
indices = []
counts = []
for idx, filepath in enumerate(matched_files):
basename = os.path.basename(filepath)
filename_without_ext = os.path.splitext(basename)[0]
filepaths.append(filepath)
filenames.append(filename_without_ext)
indices.append(idx)
counts.append(total_count)
# Return as lists - OUTPUT_IS_LIST tells ComfyUI this is a batch
return (filepaths, filenames, indices, counts)
class FilePathsLoopClose:
"""
Closes the file paths loop.
Connect any outputs you want to collect from the loop iterations.
This ensures all iterations complete before proceeding.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"current_index": ("INT", {"default": 0, "forceInput": True}),
},
"optional": {
"passthrough_1": ("*",),
"passthrough_2": ("*",),
"passthrough_3": ("*",),
"passthrough_4": ("*",),
"passthrough_5": ("*",),
}
}
INPUT_IS_LIST = True
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("total_iterations",)
FUNCTION = "close_loop"
CATEGORY = "FilePathsFromFolder/Loop"
OUTPUT_NODE = True
def close_loop(self, current_index, **kwargs):
# Count total iterations
total = len(current_index) if isinstance(current_index, list) else 1
return (total,)
class FilePathsEnsureExecution:
"""
Ensures connected nodes execute by passing through their outputs.
Use this to chain multiple nodes that need to execute during each loop iteration.
Connect up to 10 node outputs, then connect this node's output to FilePathsLoopClose.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"optional": {
"input_1": ("*",),
"input_2": ("*",),
"input_3": ("*",),
"input_4": ("*",),
"input_5": ("*",),
"input_6": ("*",),
"input_7": ("*",),
"input_8": ("*",),
"input_9": ("*",),
"input_10": ("*",),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("passthrough",)
FUNCTION = "passthrough"
CATEGORY = "FilePathsFromFolder/Loop"
OUTPUT_NODE = False
def passthrough(self, **kwargs):
# Return the first non-None input as passthrough
for key in sorted(kwargs.keys()):
if kwargs[key] is not None:
return (kwargs[key],)
return (None,)
class LoadFilePathsFromFolder:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"folder_path": ("STRING", {"default": ""}),
"extension": ("STRING", {"default": "*.png"}),
}
}
RETURN_TYPES = ("STRING", "STRING", "INT")
RETURN_NAMES = ("filepaths", "filenames", "count")
FUNCTION = "load_files"
CATEGORY = "FilePathsFromFolder"
def load_files(self, folder_path, extension):
# Build search pattern
search_pattern = os.path.join(folder_path, extension)
# Find all matching files
matched_files = glob.glob(search_pattern)
matched_files.sort()
# Extract file paths and names
filepaths = []
filenames = []
for filepath in matched_files:
filepaths.append(filepath)
# Get filename without extension
basename = os.path.basename(filepath)
filename_without_ext = os.path.splitext(basename)[0]
filenames.append(filename_without_ext)
# Join with line breaks
filepaths_str = "\n".join(filepaths)
filenames_str = "\n".join(filenames)
count = len(matched_files)
return (filepaths_str, filenames_str, count)
class GetPathByIndex:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"filepaths": ("STRING", {"default": "", "multiline": True}),
"filenames": ("STRING", {"default": "", "multiline": True}),
"index": ("INT", {"default": 0, "min": 0}),
}
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("filepath", "filename")
FUNCTION = "get_by_index"
CATEGORY = "FilePathsFromFolder"
def get_by_index(self, filepaths, filenames, index):
# Split by line breaks
filepath_list = filepaths.split("\n") if filepaths else []
filename_list = filenames.split("\n") if filenames else []
# Get item at index (default to empty if out of range)
filepath = filepath_list[index] if index < len(filepath_list) else ""
filename = filename_list[index] if index < len(filename_list) else ""
return (filepath, filename)
NODE_CLASS_MAPPINGS = {
"LoadFilePathsFromFolder": LoadFilePathsFromFolder,
"GetPathByIndex": GetPathByIndex,
"FilePathsForLoop": FilePathsForLoop,
"FilePathsLoopClose": FilePathsLoopClose,
"FilePathsEnsureExecution": FilePathsEnsureExecution,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LoadFilePathsFromFolder": "Load File Paths From Folder",
"GetPathByIndex": "Get Path By Index",
"FilePathsForLoop": "File Paths For Loop",
"FilePathsLoopClose": "File Paths Loop Close",
"FilePathsEnsureExecution": "File Paths Ensure Execution",
}