Multi-Dir Image Batch (queue-driven, stops at the end)
Queue once per image across many folders, and actually stop at the end
- image
- filename_text
- total_count
- current_dir
What it replaces
Everyone builds datasets in more than one folder. "Keep" and "review". Scans from one archive and phone photos from another. If you want to run one workflow over the lot, you either load a whole batch into memory (fine until 500 images at 4000px decide to be one tensor) or you point a single-file loader at a path and re-queue it manually, folder by folder.
Multi-Dir Image Batch takes a list of directories, indexes them as if they were one sequence, and returns one image per queued run. The index is global across all the directories in listed order, so you queue 500 runs and walk away.
How it works
Three inputs: directories (multiline - one absolute path per line), index, and pattern (a glob like *.jpg, default *). It expands ~, globs each directory, filters to image extensions, sorts within each directory, and concatenates the lists in the order you typed them. Then index picks one.
The interesting part is how it stops. The node implements VALIDATE_INPUTS, and when the index has run past the total it returns an error string at validation time rather than raising during execution. That distinction is the author's whole argument: a prompt that fails validation never enters the queue at all, so auto-queue can't re-submit it. Failing during execution only fails that run - the frontend's instant-mode queue re-submits from the client regardless of what the server does, and the index climbs forever against nothing. There's also a belt-and-braces path in load() that wipes any pending queue items and raises IndexError if an exhausted index somehow reaches execution.
To drive it, wire a PrimitiveNode into index and set its control_after_generate to increment. That per-widget dropdown is the same mechanism that randomises your seed each run; here it steps the index by one after each queued execution. Set it to fixed and you'll process the same image forever.
Outputs: image, filename_text, total_count, current_dir. filename_text is the part you want - wire it into a save node that keeps the source stem (SaveImageWithName in this pack does exactly that, and takes the filename as an input), so an accepted crop is traceable back to the photo it came from instead of being ComfyUI_00042.png. total_count prints the batch size once on the index-0 run, which is a good sanity check that your glob found what you think it did. current_dir is handy if you want to write outputs back out per source folder.
Install
ComfyUI Manager: search Portrait Dataset Tools. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/BillyBGit/ComfyUI-PortraitDatasetTools
pip install -r ComfyUI-PortraitDatasetTools/requirements.txt
This node has no model dependency - it's Pillow, numpy and glob - but the pack as a whole wants insightface, opencv-python, numpy, Pillow, onnxruntime, and downloads buffalo_l (~300 MB) on first use of any face node.
Where people get burned
Never point an output folder at a directory being scanned. The loader re-globs on every run, so every image you write becomes a new input and the batch grows while it processes. This is the classic way to fill a disk overnight.
Reset the index to 0 before the next batch. A stale index means your first run either starts mid-dataset or fails validation immediately.
Auto-queue off for the last run, or accept that you'll get one loud validation error and then stop - which is the intended behaviour, not a bug. The error text says BATCH COMPLETE, clears nothing, and tells you to reset.
Order is per-directory sort order, so dir1/* then dir2/*, alphabetically inside each. If you need a specific order, name your files for it or list single-image directories.
Non-image files are filtered out, but your glob still has to be sane. pattern of * on a Mac folder will pick up .DS_Store - harmless, it gets filtered - but it'll also happily glob a nested thumbs/ file if one exists at that level.
One last quirk: it's filed under the nudenet category in the node menu (it was built as a sibling of the NudeNet detect/crop nodes and to replace the WAS image-batch loader's end-of-batch behaviour). If you can't find it in the sidebar, that's why.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| directories | STRING | One absolute directory path per line. Images are indexed sequentially across all directories in the listed order. | |
| index | INT | 00–999999 | Global image index across all directories. Connect a PrimitiveNode set to 'increment'. |
| pattern | STRING | * | Glob pattern for filenames within each directory, e.g. '*.jpg' or '*'. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| filename_text | STRING | — |
| total_count | INT | — |
| current_dir | STRING | — |