CLI + skill for transcribing arbitrary online video/audio via WhisperX (ASR + alignment + optional speaker diarization). yt-dlp for source extraction, WhisperX for speech-to-text, ffmpeg for format conversion.
Find a file
2026-09-11 19:57:26 -07:00
.benchmarks Apply changes from kilo-gsd.py run for Mark/whisper-transcribe#17 2026-09-05 10:54:12 -07:00
.gsd/gates chore(repo): remove pytest tempdir residue + ignore future occurrences 2026-09-05 17:39:05 -07:00
docs Issue #21: --state-file override + 10h streaming/resume integration test 2026-09-09 17:06:50 -07:00
examples examples/README.md: fix word counts in summary table (#17) 2026-09-05 10:49:45 -07:00
references Issue #15: GPU memory monitoring + graceful OOM 2026-09-05 23:03:28 -07:00
scripts Apply changes from kilo-gsd.py run for Mark/whisper-transcribe#17 2026-09-05 10:54:12 -07:00
src Issue #21: --state-file override + 10h streaming/resume integration test 2026-09-09 17:06:50 -07:00
tests Issue #21: expose --state-file and 10h streaming/resume tests under the gate names 2026-09-09 17:08:48 -07:00
.gitignore chore: gitignore .kilo/ working notes 2026-09-09 09:56:39 -07:00
.pre-commit-config.yaml chore: add pre-commit hooks to catch lint/type drift before push 2026-09-11 19:57:26 -07:00
Containerfile Apply changes from kilo-gsd.py run for Mark/whisper-transcribe#5 2026-09-05 10:35:30 -07:00
pyproject.toml Pin typer>=0.24 for click 8.2+ compatibility 2026-09-05 10:17:38 -07:00
README.md Issue #1: tighten diarize preflight and force alignment for --diarize 2026-09-06 10:09:48 -07:00
uv.lock Add uv.lock 2026-09-05 10:20:47 -07:00

whisper-transcribe

A command-line wrapper around WhisperX that transcribes an audio URL (YouTube, podcast RSS, direct MP4, …) or a local file into a segmented JSON transcript, with optional forced alignment and optional speaker diarization via pyannote. The package exposes a single transcribe console script (src/whisper_transcribe/cli.py, pyproject.toml [project.scripts]) that accepts URL or local file input and writes {language, duration_sec, segments[]} JSON either to --out or to <input>.json next to the source.

Smoke test (2026-09-01)

End-to-end runs against real sources on an RTX 2060 (CUDA, float16, align on, diarize off). Numbers are lifted from the canonical JSON files under /tmp/whisperx-smoke/ — the smoke-test log was not retained, so per-run transcribe time is n/a for this revision (see docs/ follow-up). Word counts were computed as the sum of len(seg.text.split()) over each segments[] array.

Source File Duration Model Transcribe time Words Outcome
Big Buck Bunny clip /tmp/whisperx-smoke/transcript.json ~15:03 base n/a (log not retained) 2,295 OK
Big Buck Bunny clip (re-run) /tmp/whisperx-smoke/jobs_transcript_small.json 15:04 small n/a (log not retained) 2,292 OK
Lex Fridman (long-form, 3.4 h) /tmp/whisperx-smoke/lex_long_transcript.json 3:23:41 base n/a (log not retained) 36,143 OK
Spanish-language clip /tmp/whisperx-smoke/spanish_transcript.json 11:25 base n/a (log not retained) 1,568 OK (es)

Hardware: RTX 2060, CUDA, base/small model, float16, align on, diarize off. Run by the author on 2026-09-01.

The smoke-test log was not retained, so per-run wall-clock transcribe time is not recoverable from disk. The JSON files in /tmp/whisperx-smoke/ are the canonical record; re-running the smoke test is deferred until CI lands (see feature matrix below).

Quickstart

Five commands on a Linux box with ffmpeg on PATH and an NVIDIA GPU. Steps 2 and 3 are only required if you intend to pass --diarize.

# 1. Install the package (from the repo root).
pip install -e .

# 2. Set up Hugging Face access for diarization. This opens the required
#    model pages when possible, explains both EULAs, and saves a Read token.
transcribe auth

# Manual alternative:
#    https://huggingface.co/pyannote/segmentation-3.0
#    https://huggingface.co/pyannote/speaker-diarization-3.1
huggingface-cli login

# 3. Export the token issued by `huggingface-cli login`.
export HF_TOKEN=hf_REPLACE_THIS

# 4. Transcribe a YouTube URL. (Any short public URL works.)
transcribe https://www.youtube.com/watch?v=dQw4w9WgXcQ --format chapters -o out.md

# 5. Inspect the output: chapters, detected language, duration.
head -20 out.md

Output formats

transcribe accepts --format <name> and renders the canonical segment JSON into one of seven human-readable shapes. --format json preserves the historical JSON output byte-for-byte; everything else is pure-Python post-processing on the in-memory payload (no extra model calls).

Format Default extension Best for
json (default) .json Round-tripping, machine consumption, the historical contract.
chapters .md Timestamped chapter list grouped by silence gap + topic shift.
summary .md 5–10 sentence overview paragraph.
chapter_summaries .md A # HH:MM:SS - label header plus a 3–6 sentence summary each.
thread .txt Numbered tweet-style posts (1/n, 2/n, …), each ≤280 chars.
blog .md Title, ## Introduction, one section per chapter, ## Takeaways.
quotes .md Pull-quotes with — <speaker> at HH:MM:SS attribution.

The underlying JSON (the --format json output and the payload every other format is derived from) has the shape {language, duration_sec, model, segments: [{start, end, text, speaker?}, …]} (src/whisper_transcribe/cli.py). Exit codes: 0 ok, 2 usage, 10 network, 11 model not cached, 12 OOM, 13 unsupported format (src/whisper_transcribe/cli.py).

Feature matrix

Capability Status
URL input (yt-dlp) shipped (cli.py:71-110)
Local file input shipped (cli.py:126-136)
Whisper transcription shipped
Forced alignment shipped, default on (cli.py:216, cli.py:321-330)
Speaker diarization shipped behind --diarize, gated on HF_TOKEN + accepted pyannote EULAs (cli.py:332-344)
Output formats (`--format json chapters
YouTube age-gate / cookie handling tracked (see #11)
Batch / queue mode (RSS, episode range) tracked (see #13)
Multi-language + code-switched audio tracked (see #14)
GPU sizing + model compatibility matrix tracked (see #10)
Per-source integration test matrix tracked (see #9)
Hermes skill wrapper tracked (see #4)
Rootless Podman Containerfile tracked (see #5)
Pre-cached model verification (doctor) tracked (see #1, "doctor subcommand" follow-up)

Hardware requirements

VRAM tiers are derived from scripts/benchmark_gpu_sizing.py's MODEL_PLAN list (scripts/benchmark_gpu_sizing.py:50-66) and the RTX 2060 smoke test above. Compute-type is float16 unless noted.

GPU tier VRAM Models that fit (float16) Notes
Entry (4 GB) — verified 4–6 GB tiny, base (verified on RTX 2060, 6 GB) small/medium need --compute-type int8. RTX 2060 smoke test passed (see above).
Mid (8 GB) 8 GB small, medium large-v3 with --compute-type int8.
High (12 GB+) 12 GB+ large-v3, large-v3-turbo Diarize works on RTX 3060 12 GB and up.
Workstation (16–24 GB) 16–24 GB All models All models + diarize without compromise. RTX 4080, A4000, A5000.
CPU only n/a tiny, base, small --device cpu --compute-type int8. No diarize. ~30–50× slower than CUDA.

For authoritative per-model VRAM measurements, run scripts/benchmark_gpu_sizing.py against a representative audio clip.

Pyannote gating caveat

Heads up — pyannote models are gated. The diarization models (pyannote/segmentation-3.0, pyannote/speaker-diarization-3.1) require you to (a) create a free Hugging Face account, (b) accept each model's EULA on its model page, and (c) pass a HF_TOKEN (env var or --hf-token) whose owner has done both. You can run transcribe auth for the guided first-run setup. Until all three are true, --diarize will fail with exit code 11 (ModelNotCachedError / DiarizationError, src/whisper_transcribe/errors.py). This is not a bug in whisper-transcribe; it is upstream gating enforced by Hugging Face.

--diarize on files ≥ 30 min auto-enables alignment so per-word speaker attribution works; pass --no-align to opt out (speakers will be approximate).

YouTube-specific extraction quirks (JS runtime requirement, login walls, anti-bot behavior) are documented separately in references/youtube-extraction-quirks.md.

Multi-language behaviour (mid-file switches, --language-hints semantics, batch output schema, known-good / known-bad cases for the six fixtures shipped under tests/fixtures/multilang/) is documented in docs/multilang-limitations.md, with the user-facing how-to in references/multi-language.md.

Status

Pre-alpha. No CI yet. The smoke-test table above is the only automated check. See tests/ for the unit/integration suite and scripts/benchmark_gpu_sizing.py for the GPU sizing harness.

# Back to top