Nodes/comfyui-simple-batch/πŸ“₯ Batch Load Records
ComfyUI Node

πŸ“₯ Batch Load Records

ComfyUI Has No Loops β€” BatchLoadRecords Fakes One, One Record at a Time

By lordekeenΒ·Created about a month agoΒ·Updated about 18 hours agoΒ· 1
πŸ“₯ Batch Load Records
  • records
  • record
  • SLOT
  • TOTAL
  • RELATIVE_PATH
β—„filename_template{name}.wavβ–Ί

ComfyUI graphs are DAGs. No loops, no for, no "run this 300 times" checkbox that does what you'd expect. Every batch solution in the ecosystem is a cheat, and patience with those cheats is thin - "Why is it so hard to batch process photos in a directory?" is a recurring r/comfyui post, and the answers are usually "set the queue-count box to the number of files" (WAS) or "it processes everything then dumps it at the end" (Inspire, which spikes VRAM on big sets).

BatchLoadRecords is that cheat pointed at records instead of files on disk. When another pack hands you a BATCH_RECORDS list (the README's example is a screenplay pack's dialogue lines), this node gives you back one record per execution, names each destination, and lets the batch loop run your single-item pipeline until every record has a file. A TTS workflow can grind through a hundred dialogue lines and drop each .wav the moment it finishes, rather than synthesizing the whole script in one enormous pass.

How the loop actually works

There's no in-graph iteration. On each execution the node formats every record into a destination path from your template, walks that list in order, and picks the first one that isn't handled yet - handled meaning a non-empty file or a .error marker sits there. Zero-byte files don't count, because that's what a killed process leaves behind. That's the whole resume mechanism: restart ComfyUI, re-queue, it picks up where it stopped. No index to reset.

The loop closes at whichever BatchSave* node you connect: after each item it re-submits the same prompt at the front of the queue. Crash recovery is the JS watchdog's job, which is why the node writes a _records_batch_state.json under your output_root - the records live in another pack's runtime output and can't be re-derived from the prompt graph, so the failure route reads that file to find which record died. Lose the state file and the route refuses to guess and stops the batch. It isn't a reset button.

Two smaller things: fingerprint_inputs returns a fresh timestamp each call, so the node re-scans every execution rather than serving a stale cached record - the IS_CHANGED-returns-NaN trick, and it costs you the cache behind it. It also drops the previous item's cached outputs between runs, which is what keeps a long batch out of your RAM.

The two inputs

records - a BATCH_RECORDS socket, so it only accepts a node that declares that type. Records are dicts shaped {"index": int, "name": str, "fields": {...}}, field values string or int. Malformed ones raise a clear ValueError instead of defaulting silently, and a field named index or name is rejected - those tokens are reserved.

filename_template (default {name}.wav) - the odd one out next to the save nodes: it produces the full relative filename, extension included, so {index:03d}_{character}.wav is legal. Tokens are {index}, {name}, and any record field, with normal Python format specs. An unknown token is a hard error here - the save node's template just leaves it literal.

The outputs

RELATIVE_PATH is the one that matters: wire it into the connected BatchSave* node's relative_path input, along with SLOT and TOTAL for the [3/50] … saved -> progress lines. Which save node you pick decides the media type - BatchSaveAudio for voice lines, the image or video pair if your records drive those. The record output is the current dict, for whatever downstream node speaks BATCH_RECORD.

The node isn't an output node itself, so nothing runs unless a save node is downstream. And when there's nothing to do - empty list, or every record already has a file - it blocks the execution with a message like "batch complete" rather than throwing a red error.

Installing it

ComfyUI Manager, search ComfyUI Simple Batch. Or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/lordekeen/comfyui-simple-batch
# restart ComfyUI

No pip install, no model downloads - the pack only uses APIs ComfyUI already provides (PyAV for audio encoding ships with ComfyUI). The real requirement is a recent ComfyUI: it's a V3-API pack registered through comfy_entrypoint() with from comfy_api.latest import io, Python β‰₯ 3.10. So it has no NODE_CLASS_MAPPINGS dictionary, and grepping for one will wrongly convince you it's broken - that's just what new packs look like. On an older build it won't register at all.

Where people get burned

Keep the connected save node's own filename_template at its default {filename} - the records template already writes the whole name, and the save node's template would apply a second layer of naming on top. The save node's format widget still owns the extension, though: auto keeps whatever your template produced, but format: mp3 plus a {name}.wav template writes name.mp3.

If two records format to the same path, the second one looks already-handled and gets silently skipped - the pack writes one deduped warning line to _simple_batch_log.txt in output_root, which is your only clue. Build uniqueness into the template ({index}, or a global dialogue index). To retry a record the watchdog marked as failed, delete its zero-byte .error marker next to where the output should have been; that's the documented undo.

One caveat worth stating plainly: this is a brand-new, small pack (v0.2.0) with essentially no community footprint - no threads arguing about it, no battle-tested edge cases. What it does have is an unusually thorough README, implementation notes included, telling you exactly which decisions were guesses.

Categoryrecords/batch

Inputs (2)

NameTypeDefaultDescription
recordsBATCH_RECORDSThe record list (BATCH_RECORDS) from the source node, e.g. a screenplay pack's dialogue source.
filename_templateSTRING{name}.wavOutput filename template producing the FULL relative filename, extension included. Tokens: {index}, {name}, any record field (e.g. {character}, {global_dialogue_index:03d}). Keep the connected BatchSave* node's own filename_template at its default {filename} so naming is applied exactly once.

Outputs (4)

NameTypeDescription
recordBATCH_RECORDThe current record dict (BATCH_RECORD) for this run.
SLOTINT0-based index of the current record for this run.
TOTALINTTotal record count for this run.
RELATIVE_PATHSTRINGFormatted destination name for the current run.