CineSpatial · BanditSeparate
Split any soundtrack into dialogue, music and effects stems in one step
- artifact_json
Ever wanted to yank the dialogue off a music bed without touching the mix by hand? That's the whole job of this node, and it's the front door to the rest of the CineSpatial pack. BanditSeparate takes one audio file and returns three stems - dialogue, music, effects - plus the proof that the separation actually happened. Everything downstream in the pack assumes you've done this step first.
How it works
The node itself is deliberately dumb. It's a thin client that takes your source_audio_path, wraps it with an operation-scoped output directory, and POSTs it to the fixed loopback endpoint http://127.0.0.1:8199/v1 asking for the separate_soundtrack operation. The heavy lifting - the source separation model (the "bandit" slot in the pack's health report) - runs in a completely separate cinespatial runner service, in its own Python environment, not in ComfyUI.
The runner writes the three stems into a fresh per-run directory, verifies every file is non-zero length and matches its SHA-256, and requires the exact artifact roles for this operation: dialogue, music, effects, and separation_provenance. Then the node converts the absolute paths into downloadable worker_ref references (filename, subfolder, type: "output") pointing into ComfyUI's own output folder - under cinespatial/separate_soundtrack/<run-id>/. So your stems show up in the output directory like any other saved file, and the artifact_json tells you where.
That provenance field is worth a moment. It's the pack's way of making sure a stem isn't just "a file with a plausible name" - the runner reports how the separation was done, and the README is explicit that a filename is never treated as proof.
The inputs and outputs that matter
Just one required input: source_audio_path (STRING). You can give it an absolute path, or a filename relative to ComfyUI's input directory - that's how uploaded files work, and the node resolves them against the input root and rejects anything that escapes it. A path that doesn't exist fails with FileNotFoundError.
One output: artifact_json (STRING), an output node, so the JSON prints when you run it. That string is what feeds the rest of the pipeline:
- the dialogue stem →
CineSpatialTranscribeAlignDialogue - the effects stem →
CineSpatialClassifyAudioEventsandCineSpatialQuerySeparateEvent
Install - and the gotcha that bites everyone
Install via ComfyUI Manager's Git URL installer, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Vighneshjs/ComfyUI-CineSpatial
then restart. There's nothing to pip-install - requirements.txt is empty - and, crucially, no model weights download through this pack. The separation model lives in the separate cinespatial runner package. If you haven't installed and started that (via cinespatial-runner --config /opt/cinespatial/runner.json), this node throws CineSpatial runner service is unavailable or invalid every time. Run CineSpatialWorkerHealth first and you'll see exactly which model slots are ready.
Troubleshooting
- "runner service is unavailable or invalid" → the loopback service isn't up, or isn't answering on
8199. Start the runner; check WorkerHealth. FileNotFoundError→ your path isn't an absolute path and isn't a real file inside ComfyUI's input directory.- Long features: operations have a generous 3600-second timeout, so a full movie won't time out - but it will block the graph while the runner works. There's no async progress; either accept the wait or separate shorter segments first.
- Schema errors → the runner answered but its
schema_versionisn't0.1. Update the runner to match the pack.
If your goal is transcription, event spotting, or isolating a single sound, this stem split is the move before any of those - you're one node in front of the rest of the pack.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| source_audio_path | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| artifact_json | STRING | — |