267 lines
8.4 KiB
Markdown
267 lines
8.4 KiB
Markdown
# Usage
|
||||
|
|
|
|||
|
|
ComfyUI must be running before any command that generates speech
|
|||
|
|
(`~/comfy-tts-app/start_comfyui.sh`). `--rebuild` is the exception — it only
|
|||
|
|
touches ffmpeg and works with the backend stopped.
|
|||
|
|
|
|||
|
|
## Invocation
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
python3 lektor_ai_comfy.py [SRT] VIDEO [options]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Both positionals are optional in the argparse sense, because several modes do
|
|||
|
|
not need an SRT file. The rule:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# External subtitle file — pass both, SRT first
|
|||
|
|
python3 lektor_ai_comfy.py input/film.pl.srt input/film.mkv
|
|||
|
|
|
|||
|
|
# Subtitles inside the video — pass the video alone
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --embedded-subs
|
|||
|
|
|
|||
|
|
# --rebuild and --regen also need only the video
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --rebuild
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
When a single positional is given in a mode that needs no SRT, the script moves
|
|||
|
|
it into the video slot automatically. Always quote paths containing spaces or
|
|||
|
|
parentheses:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py "input/Star City (2026)/S01_E01 The Eyes.mkv" --embedded-subs
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Working modes
|
|||
|
|
|
|||
|
|
### Full generation (default)
|
|||
|
|
|
|||
|
|
Parses subtitles, generates one TTS clip per block, assembles, muxes.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --embedded-subs
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Roughly real-time-ish on a 4070 Ti SUPER: a 60-minute episode of ~700 subtitle
|
|||
|
|
blocks takes on the order of an hour. Each clip is saved as it completes, so a
|
|||
|
|
crash never loses more than the clip in flight.
|
|||
|
|
|
|||
|
|
### `--embedded-subs`
|
|||
|
|
|
|||
|
|
Lists the video's subtitle tracks and prompts for one:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Available subtitle tracks in S01_E01 The Eyes.mkv:
|
|||
|
|
[1] Stream #2: subrip [eng] — Forced
|
|||
|
|
[2] Stream #3: subrip [eng]
|
|||
|
|
[3] Stream #4: subrip [eng] — SDH
|
|||
|
|
...
|
|||
|
|
[30] Stream #31: subrip [pol]
|
|||
|
|
Select track number: 30
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The chosen track is extracted to `output/embedded_sub.srt` and then processed
|
|||
|
|
exactly like an external file. Image-based tracks are listed but marked
|
|||
|
|
unusable — they hold bitmaps, not text.
|
|||
|
|
|
|||
|
|
### `--rebuild`
|
|||
|
|
|
|||
|
|
Reassembles audio from existing clips and rebuilds the video, with no TTS at
|
|||
|
|
all. This is the fast iteration loop — seconds to minutes rather than an hour.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --rebuild
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --rebuild --orig-volume 0.5 --tts-volume 2.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Use it after `--regen`, or to adjust the mix. It requires `output/tts_input.srt`
|
|||
|
|
and a complete set of `output/clips/clip_NNNN.flac`, and reports which are
|
|||
|
|
missing if any are.
|
|||
|
|
|
|||
|
|
If the original run used `--end`, pass it again so the same video fragment is
|
|||
|
|
used.
|
|||
|
|
|
|||
|
|
### `--regen`
|
|||
|
|
|
|||
|
|
Regenerates specific clips by block number, leaving everything else alone.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --regen 12 22 41
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --rebuild # then apply
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Block numbers match `output/tts_input.srt` and the `clip_NNNN.flac` filenames.
|
|||
|
|
Only the selected FLAC files are replaced — `--rebuild` afterwards is what
|
|||
|
|
actually updates the video.
|
|||
|
|
|
|||
|
|
This is the intended fix for a bad clip. There is no automatic retry; when the
|
|||
|
|
model hallucinates, mumbles, or truncates, note the number and regenerate it. A
|
|||
|
|
different seed usually resolves it, and `--seed 0` (the default) randomises
|
|||
|
|
every run.
|
|||
|
|
|
|||
|
|
### `--resume-from`
|
|||
|
|
|
|||
|
|
Continues a crashed run, loading clips `1..N-1` from disk and generating from
|
|||
|
|
`N` onward.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --resume-from 47
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The console prints `[47/693]` style progress, so the number to resume from is
|
|||
|
|
the last one that completed.
|
|||
|
|
|
|||
|
|
### `--skip-tts`
|
|||
|
|
|
|||
|
|
Uses a pre-assembled audio file and goes straight to the mux.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --skip-tts output/tts_assembled.flac
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Options
|
|||
|
|
|
|||
|
|
### Range
|
|||
|
|
|
|||
|
|
| Flag | Default | Meaning |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `--start MM:SS` | `00:00` | Start of the range to process |
|
|||
|
|
| `--end MM:SS` | *(end)* | End of the range |
|
|||
|
|
|
|||
|
|
With `--end`, a matching fragment is extracted to `output/video_fragment.mkv`
|
|||
|
|
and all TTS timestamps become relative to `--start`. Pass the same values to any
|
|||
|
|
later `--rebuild`, or the audio will not line up.
|
|||
|
|
|
|||
|
|
`--end 01:00` is the quickest way to sanity-check a new voice or speed setting.
|
|||
|
|
|
|||
|
|
### Voice and model
|
|||
|
|
|
|||
|
|
| Flag | Default | Meaning |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `--voice NAME\|PATH` | `gudowski_short` | Name in `gudowski/`, or a path to a WAV |
|
|||
|
|
| `--seed N` | `0` | Base seed; `0` randomises each run |
|
|||
|
|
| `--temperature T` | `0.9` | Sampling temperature |
|
|||
|
|
| `--max-tokens N` | `4096` | Token ceiling per clip |
|
|||
|
|
|
|||
|
|
`--temperature` is worth understanding. At `1.0` the model sometimes recites the
|
|||
|
|
reference transcript instead of the subtitle. `0.9` largely prevents that;
|
|||
|
|
lower is more deterministic but flatter. A per-clip seed of `base_seed + index`
|
|||
|
|
means a fixed `--seed` reproduces a run exactly.
|
|||
|
|
|
|||
|
|
Raise `--max-tokens` only if long clips are cut off mid-word.
|
|||
|
|
|
|||
|
|
### Timing and cleanup
|
|||
|
|
|
|||
|
|
| Flag | Default | Meaning |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `--speed FACTOR` | `1.2` | Per-clip speed-up via `atempo` |
|
|||
|
|
| `--strip-leading-ms MS` | `0` | Trim the start of each clip |
|
|||
|
|
| `--trailing-fade-ms MS` | `80` | Fade the end of each clip |
|
|||
|
|
|
|||
|
|
`--speed` compresses speech so a clip is more likely to fit its subtitle window.
|
|||
|
|
It is applied **per clip, before assembly** — never to the assembled track,
|
|||
|
|
which would shift every timestamp by speeding up the silences too.
|
|||
|
|
|
|||
|
|
`--strip-leading-ms` removes an inhale artifact at clip starts; try 80–150 if
|
|||
|
|
you hear one. `--trailing-fade-ms` suppresses tail pops; `0` disables it.
|
|||
|
|
|
|||
|
|
### Mixing
|
|||
|
|
|
|||
|
|
| Flag | Default | Meaning |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `--orig-volume 0-1` | `0.60` | Original audio level inside the lektor mix |
|
|||
|
|
| `--tts-volume 0-2` | `1.8` | Voiceover level inside the lektor mix |
|
|||
|
|
|
|||
|
|
These affect **Track 1 only**. Track 0 is always the untouched original, so
|
|||
|
|
nothing is lost by mixing aggressively. Changing them needs only `--rebuild`.
|
|||
|
|
|
|||
|
|
Typical starting point for dialogue-heavy material: `--orig-volume 0.5
|
|||
|
|
--tts-volume 2.0`. For quiet sources, `--orig-volume 1.0 --tts-volume 1.4`.
|
|||
|
|
|
|||
|
|
### Paths
|
|||
|
|
|
|||
|
|
| Flag | Default | Meaning |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `--output-dir DIR` | `output` | Where everything is written |
|
|||
|
|
| `--comfy-url URL` | `http://127.0.0.1:8189` | Backend address |
|
|||
|
|
|
|||
|
|
## Output
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
output/
|
|||
|
|
tts_input.srt processed text actually sent to TTS — inspect this
|
|||
|
|
embedded_sub.srt extracted embedded subtitle (--embedded-subs only)
|
|||
|
|
clips/clip_NNNN.flac one lossless clip per block, kept for --regen
|
|||
|
|
tts_assembled.flac all clips at their final positions, lossless
|
|||
|
|
video_fragment.mkv extracted segment (--end only)
|
|||
|
|
{stem}_lektor.mkv the finished video
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The finished file contains:
|
|||
|
|
|
|||
|
|
| Track | Content |
|
|||
|
|
|---|---|
|
|||
|
|
| Video | Stream-copied, untouched |
|
|||
|
|
| Audio 0 | **Original** — stream-copied, full 5.1 if the source had it |
|
|||
|
|
| Audio 1 | **Polish** — lektor mix, AAC 192k stereo, `language=pol` |
|
|||
|
|
| Subtitles | Every track from the source, stream-copied |
|
|||
|
|
|
|||
|
|
The lektor track is stereo deliberately; see
|
|||
|
|
[troubleshooting.md](troubleshooting.md#the-lektor-track-is-silent-in-the-player).
|
|||
|
|
|
|||
|
|
FLAC is used for every intermediate so repeated processing stays lossless. Only
|
|||
|
|
the final voiceover track is ever encoded.
|
|||
|
|
|
|||
|
|
## Recipes
|
|||
|
|
|
|||
|
|
**Check a voice before committing to a full run**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --embedded-subs --end 02:00 --voice gudowski
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Tune the mix without regenerating speech**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --rebuild --orig-volume 0.45 --tts-volume 2.1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Fix a handful of bad clips**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# read output/tts_input.srt, note the offending block numbers
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --regen 88 91 104
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --rebuild
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Reproduce a run exactly**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --embedded-subs --seed 123456
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Speech running long and overlapping the next line**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 lektor_ai_comfy.py input/film.mkv --embedded-subs --speed 1.35
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Reading the console
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
[1/4] Parsing subtitles 00:00–end…
|
|||
|
|
693 entries after dialogue splitting.
|
|||
|
|
[2/4] Generating TTS (693 clips, speed 1.2×)…
|
|||
|
|
[12/693] 00:01:34,120 'Nie wiem, o czym mówisz.'
|
|||
|
|
Waiting for a3f8b201… done.
|
|||
|
|
→ placed at 00:01:34,120, duration 1.84s
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`→ placed at` reveals timing drift. A clip that overruns its window pushes the
|
|||
|
|
next one forward, shown as `[+1.20s behind SRT]`. Small values are normal and
|
|||
|
|
self-correct at the next natural gap in the subtitles; values that keep growing
|
|||
|
|
mean `--speed` is too low for the material.
|
|||
|
|
|
|||
|
|
Assembly then shows a progress bar per batch (clips are assembled 500 at a time
|
|||
|
|
to stay under the open-file limit), followed by the two-pass mux.
|