Files
RockyHong-ComfyUI-FilePaths…/nodes.py
T
2026-01-25 14:07:40 +08:00

208 lines
6.5 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 = ("current_filepath", "current_filename", "current_index", "total_file_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 FilePathsEnsureExecution output here to ensure all nodes in the loop execute.
This ensures all iterations complete before proceeding.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"current_index": ("INT", {"default": 0, "forceInput": True}),
},
"optional": {
"passthrough": ("*",),
}
}
INPUT_IS_LIST = True
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("processed_count",)
FUNCTION = "close_loop"
CATEGORY = "FilePathsFromFolder/Loop"
OUTPUT_NODE = True
def close_loop(self, current_index, passthrough=None):
# 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.
If you need more than 5 inputs, you can chain multiple of these nodes together.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"optional": {
"passthrough_1": ("*",),
"passthrough_2": ("*",),
"passthrough_3": ("*",),
"passthrough_4": ("*",),
"passthrough_5": ("*",),
}
}
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 File Path By Index",
"FilePathsForLoop": "File Paths For Loop Start",
"FilePathsLoopClose": "File Paths Loop End",
"FilePathsEnsureExecution": "File Paths Loop Ensure Execution",
}