transcription-pipeline
Drop a recording in a folder; get back a transcript that says who said what, stamped to the word, filed where the rest of your notes live.
inbox/ --launchd WatchPaths--> transcribe inbox
1. take files whose size has stopped changing; one run at a time (lock file)
2. ffmpeg any audio or video -> 16 kHz mono WAV
3. transcribe local: faster-whisper large-v3-turbo, int8 (default)
deepinfra: hosted Whisper large-v3-turbo (optional)
4. align wav2vec2 forced alignment -> a start and end time for every word
5. diarize pyannote -> who spoke when; each word gets a speaker
6. shape sentences (split at punctuation and at every change of speaker),
speakers renumbered "Speaker 1, 2, ..." by first appearance
7. write transcripts/<name>.txt .md .json (+ .srt .vtt); the .md also to a vault
8. file recording -> done/, or failed/ with <name>.error.txtSteps 3 to 5 are WhisperX. The rest is this package: the watch folder, the sentence and speaker shaping, the writers, and the plumbing that makes it safe to leave running.
What a transcript looks like
From the integration test, which runs on two LibriSpeech readers taking turns
(tests/fixtures/two-speakers.flac):
[00:00:00] Speaker 1: for a full hour he had paced up and down waiting but he could wait no longer
[00:00:06] Speaker 2: harangue the tiresome product of a tireless tongue
[00:00:13] Speaker 1: pride after satisfaction uplifted him like long slow waves
[00:00:19] Speaker 2: anger pain painful to hearThe model returned that clip as one unpunctuated segment. It still comes out as four turns, because sentences are also split wherever the speaker changes.
The .md is a vault note: this frontmatter, then one paragraph per speaker turn.
The .json keeps every segment, every timed word and the run's metadata.
---
type: transcript
title: "two-speakers"
recorded: 2026-09-28
transcribed: 2026-09-28T06:15:09
duration: "0:00:24"
engine: local
language: en
source: "two-speakers.flac"
speakers:
- "Speaker 1"
- "Speaker 2"
tags:
- transcript
---Use it
transcribe call.m4a # local, with speakers; outputs beside the file
transcribe *.mp3 --out ~/transcripts --formats txt,srt
transcribe interview.mov --speakers 2 # you know how many voices there are
transcribe memo.m4a --no-diarize # just the words
transcribe call.m4a --engine deepinfra # hosted Whisper; uploads the audio
transcribe inbox --root ~/Transcription # what launchd runsInstall
python3.12 -m venv .venv && .venv/bin/pip install -e ".[test]"Python 3.10 to 3.13 (WhisperX does not support 3.14 yet). Tokens, the gated diarization model and the launchd job: docs/SETUP.md.
Tests
pytest -q # unit tier: no network, no models (CI)
TP_INTEGRATION=1 pytest -q tests/integration # real models; add HF_TOKEN for pyannoteThe integration tier checks every reference word, monotonic word times and the four turn boundaries on four utterances from LibriSpeech test-clean (Panayotov, Chen, Povey and Khudanpur, 2015), used under CC BY 4.0.
Choices worth knowing
- Local by default. DeepInfra's hosted Whisper has transcribed noticeably better than local int8 faster-whisper; use it when quality matters and uploading is fine.
- Timed to the word. A sentence starts at its first aligned word; where nothing
aligns, the time is interpolated and the JSON marks it
"timed": false. - Sentence rules pinned by a golden file.
sentences.pyports a JavaScript splitter;tests/fixtures/sentences-golden.jsonholds that splitter's output. - Speakers. Sentences split at every change of speaker; a one-word run is diarization noise; labels are renumbered by first appearance.
- Safe to leave running. Settle window, one run at a time with stale-lock takeover,
failed/with the reason, never an overwrite; tokens from the env or the Keychain.
Layout
| Path | What it is |
|---|---|
transcription_pipeline/ |
The package: cli, pipeline, inbox, audio, engines/, align, diarize, sentences, shape, render, filing, secrets, steps. |
tests/ |
Unit tier (runs in CI) and tests/integration (real models on real speech). |
scheduling/ |
The launchd plist and the script it runs. |
docs/SETUP.md |
Install, tokens, the watch folder. |
Where it came from
It grew out of a small DeepInfra transcriber with a one-page UI. The pipeline kept its
sentence rules, [HH:MM:SS] lines and never-overwrite filing, and added the rest.
License
MIT.