π€ Batch Save Image
The node that keeps the batch alive while you're not looking
- images
BatchSaveImage looks like a boring save node, and it is - until you realize it's the whole engine of the batch. Replace your SaveImage with this at the end of a workflow, wire it to BatchLoadImage's IMAGE, and it does three jobs: writes the processed file to a mirrored folder tree, prints progress to the console, and re-submits the same prompt at the front of the queue as long as images remain. That requeue is the loop. ComfyUI graphs can't loop, so the Save node cheats.
It mirrors your source tree by default: output_root + the file's relative path, so subdir/a.png in becomes subdir/a.png out, and same-named files in different folders never collide. Already-handled files are skipped on re-runs, which is also how resume works - the Load node counts what's already in the destination tree to figure out what's next. One mechanism, used for both.
The inputs that matter
images- the processed image from the end of your chain. Obvious.slot,total,relative_path- wire these straight fromBatchLoadImage. Don't hand-type them; the Save node validates that they came in properly and will refuse to run otherwise.output_root- required. Absolute path to the destination root. Leave it empty and the batch stops with an error. Keep it outsidefolder_path, or at least accept that the pack excludes it from scanning so nothing loops.format-auto(keeps the source extension, default), or forcepng/jpg/webp.filename_template- how the output is named, applied to the stem only. Default{filename}reproduces your source name byte-for-byte. You can use{date}forYYYY-MM-DD; any other{token}stays literal. The extension always comes fromformat, appended last -formatcontrols it, never the template.
The crash recovery you didn't know you needed
The reason this pack ships a JS watchdog alongside its Python is the failure path: if a node upstream of the save crashes (OOM, corrupt input), the Save node never runs, so it can't log or requeue. ComfyUI broadcasts execution_error, the watchdog catches it, the server writes one line to _simple_batch_log.txt in output_root, drops a zero-byte <destination>.error marker next to where the output would have gone, and re-queues the batch. The next run sees the marker, skips that item, and continues. One attempt per item, then logged and skipped - which is honest design, because a silently retrying batch is how you come back to a runaway job.
To re-process a failed item, delete its .error marker (and the output, if a partial one exists). A zero-byte destination from a killed write doesn't count as handled, so it gets retried; a truncated-but-non-empty file does count. And Cancel is respected: that broadcasts execution_interrupted instead, and the watchdog does nothing - the batch just stops. That's the intended behavior when you hit cancel.
Notes on output format
PNG saves embed the full workflow metadata (drag the result back onto the canvas and the graph rebuilds - the standard ComfyUI convention). WebP saves can't, and the node prints a note to that effect. So if the "workflow in the file" thing matters to you - and in this ecosystem it should - don't force webp on your keepers.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/lordekeen/comfyui-simple-batch
Then restart. Or search ComfyUI Simple Batch in Manager. No pip install, no model downloads; you need a recent ComfyUI with the V3 API. After that the only real setup is setting output_root to somewhere sensible - and making sure it's not inside the folder you're batch-reading.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | The processed image (or image batch) from the end of the workflow. | |
| slot | INT | 0 | 0-based index of the current image (wire from Batch Load Image). |
| total | INT | 0 | Total matching file count (wire from Batch Load Image). |
| relative_path | STRING | Current file's path relative to the input folder (wire from Batch Load Image). | |
| output_root | STRING | Root of the destination tree (absolute path). Required. | |
| format | COMBO | auto | Output format. 'auto' keeps the source container and extension. |
| filename_template | STRING | {filename} | Output filename template, applied to the file stem. {filename} = original stem, {date} = YYYY-MM-DD. |
Outputs (0)
No outputs