Simple File Path Loop

This commit is contained in:
Rocky-PC
2026-01-25 03:59:50 +08:00
parent 391230fdec
commit 345ea8ed87
3 changed files with 204 additions and 2 deletions
+71 -1
View File
@@ -1,6 +1,6 @@
# ComfyUI-FilePathsFromFolder
A simple ComfyUI custom node pack for loading and selecting files from folders.
A simple ComfyUI custom node pack for loading and selecting files from folders, with for-loop support for batch processing.
## Nodes
@@ -30,6 +30,76 @@ Selects a specific file from the lists by index.
- `filename`: Selected filename
- `filepath`: Selected filepath
## Loop Nodes
### FilePathsForLoop
Initiates iteration through files in a folder. Uses ComfyUI's list-based batching to process each file individually.
**Parameters:**
- `folder_path`: Path to the folder to scan
- `extension`: File extension pattern (e.g., "*.png", "*.jpg", "*.txt")
**Outputs:**
- `filepath`: Full path of current file (list output - each item processed separately)
- `filename`: Filename without extension (list output)
- `current_index`: Current iteration index, 0-based (list output)
- `total_count`: Total number of files (list output)
**Important:** This node outputs lists with `OUTPUT_IS_LIST = True`. ComfyUI will automatically run all connected nodes once for each item in the list. Each connected node receives a **single value** from the list per execution, not the entire list.
### FilePathsLoopClose
Closes the file paths loop and collects iteration results.
**Inputs:**
- `current_index`: Current index from FilePathsForLoop (required, force input)
- `passthrough_1` through `passthrough_5`: Optional outputs from nodes in the loop
**Outputs:**
- `total_iterations`: Total number of iterations completed
**Usage:** This is the termination point of your loop. Connect the `current_index` from FilePathsForLoop (or through other nodes) to ensure the loop completes. You can also connect outputs from processing nodes to the passthrough inputs to collect results.
### FilePathsEnsureExecution
Ensures connected nodes execute during loop iteration by collecting their outputs.
**Inputs:**
- `input_1` through `input_10`: Optional inputs (supports any type with wildcard `*`)
**Outputs:**
- `passthrough`: The first non-None input value
**Usage:** Connect outputs from any nodes that need to execute within the loop to this node's inputs. Then connect this node's output downstream (e.g., to FilePathsLoopClose) to ensure all connected nodes complete execution.
## How ComfyUI List-Based Loops Work
When a node has `OUTPUT_IS_LIST = True`:
1. The node returns lists: `["file1.png", "file2.png", "file3.png"]`
2. ComfyUI automatically executes **all downstream nodes once per list item**
3. Each execution receives **one item from the list**, not the whole list
4. **Iteration 1**: downstream nodes receive `"file1.png"`
5. **Iteration 2**: downstream nodes receive `"file2.png"`
6. **Iteration 3**: downstream nodes receive `"file3.png"`
This is ComfyUI's native batching/looping mechanism.
## Example Workflows
### Simple Loop Processing
1. **FilePathsForLoop** - Set folder path and extension (e.g., "C:/images", "*.png")
2. Connect `filepath` to your image loader node (e.g., Load Image)
3. Connect your processing nodes (e.g., filters, effects)
4. Connect the final processing output to **FilePathsEnsureExecution** inputs
5. Connect **FilePathsForLoop**'s `current_index` to **FilePathsLoopClose**'s `current_index`
6. (Optional) Connect **FilePathsEnsureExecution**'s output to **FilePathsLoopClose**'s `passthrough_1`
This setup will automatically process each file in the folder sequentially.
### Why Three Nodes?
- **FilePathsForLoop**: Initiates the loop, provides file paths and metadata
- **FilePathsEnsureExecution**: Optional helper to chain dependencies and ensure execution order
- **FilePathsLoopClose**: Terminates the loop, provides completion signal, collects results
## Installation
Place this folder in your ComfyUI `custom_nodes` directory and restart ComfyUI.
Binary file not shown.
+133 -1
View File
@@ -2,6 +2,133 @@ 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):
@@ -75,10 +202,15 @@ class GetPathByIndex:
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",
}