subcap
A one-command captioning pipeline built around WhisperX's forced alignment. Give it a video and a transcript — it handles audio extraction, alignment, subtitle segmentation, styling, and burn-in encoding.
Why this exists
WhisperX solves the hard problem: using wav2vec2 to map each word of a known transcript to its exact position in audio. But WhisperX outputs raw word timestamps — turning those into readable, styled, burned-in captions is still a non-trivial amount of glue code per video.
subcap is that glue code, packaged as a CLI:
- Subtitle segmentation — groups aligned words into readable chunks with sentence-boundary breaks, line wrapping, duration caps, and proper gaps between cues
- Styled ASS generation — four presets (modern, outline, minimal, bold), auto-adapted for landscape vs portrait video
- ffmpeg burn-in — re-encodes to H.264, H.265, or ProRes with a single
--qualityflag - SRT bypass — if you already have timed subtitles, it skips alignment and goes straight to styling + burn-in
Without subcap, getting from video + transcript to video with burned-in captions requires chaining WhisperX, writing your own segmentation logic, hand-crafting ASS files, and orchestrating ffmpeg. subcap is subcap video.mov transcript.txt.
Install
pip install subcap
Requires Python 3.10–3.12 and ffmpeg with libass support.
On first run, subcap downloads the wav2vec2 alignment model (~360 MB).
Usage
# Align a transcript and burn captions in
subcap video.mov transcript.txt -o output.mp4
# Use an existing SRT file (skips alignment)
subcap video.mov subtitles.srt -o output.mp4
# Choose a style
subcap video.mov transcript.txt --style outline
# ProRes output for editing
subcap video.mov transcript.txt --quality studio -o output.mov
# Portrait/vertical video (auto-detected)
subcap shorts.mp4 transcript.txt -o shorts_captioned.mp4
Options
subcap <video> <transcript> [options]
-o, --output Output path (default: <input>_captioned.mp4)
--style modern | outline | minimal | bold (default: modern)
--quality standard | high | studio (default: standard)
--max-lines Max lines per subtitle (default: 2)
--max-chars Max characters per line (default: auto)
--line-spacing Gap between lines in px (default: auto)
--position bottom | center | top (default: bottom)
Styles
| Preset | Look |
|---|---|
modern |
White bold text, semi-transparent dark box |
outline |
White text with black outline |
minimal |
Lighter weight, subtle shadow |
bold |
Large text, opaque dark box |
Quality
| Preset | Codec | Use case |
|---|---|---|
standard |
H.264 | Sharing, uploading |
high |
H.265 | Smaller files |
studio |
ProRes 422 | Editing, broadcast |
Pipeline
- Extract audio — mono 16 kHz WAV via ffmpeg
- Force-align (WhisperX / wav2vec2) — map each word of the transcript to its exact position in the audio
- Segment (subcap) — group words into readable subtitle cues, break at sentence boundaries, wrap long lines, enforce min/max display duration, insert gaps
- Style (subcap) — generate ASS with the selected preset, adapted to aspect ratio
- Burn in (ffmpeg) — re-encode with hardcoded subtitles
Steps 1, 2, and 5 are wrappers around existing tools. Steps 3 and 4 are what subcap adds. Because the transcript text is fixed and only timing is being solved, alignment stays precise even for fast speech, accents, or noisy audio — conditions that break speech-to-text approaches.
Transcript notes
Your transcript must match what's actually said in the audio. Small edits are tolerated, but missing or extra sentences will cause alignment failures. If the speaker ad-libs or skips text, update the transcript to match the final delivery.
Acknowledgments
Built on:
- WhisperX — Phoneme-level forced alignment using wav2vec2
- wav2vec2 — Self-supervised speech model used as the acoustic backbone for alignment
- ffmpeg — Video encoding and subtitle rendering via libass
License
MIT
Metadata
Release files for subcap 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| subcap-0.2.2.tar.gz | 14.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| subcap-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.7 kB
Release files / subcap-0.2.2.tar.gz
| Download URL | subcap-0.2.2.tar.gz |
|---|---|
| Size | 14.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6faceb9a02fa71742bc95433aa262648d14207de73dcf42d2e8b38de21725fdf
|
|
BLAKE2b-256 checksum How to use checksums |
afaec30a7026edca173b33a0d97d1a8bda9da43f9104a03a8833e0b05ccd3c1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 13, 2026.
Transparency logRelease files / subcap-0.2.2-py3-none-any.whl
| Download URL | subcap-0.2.2-py3-none-any.whl |
|---|---|
| Size | 12.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a2bb97d22152891562a06f6e01f7af013634612b112453d38688d781f9a8e84e
|
|
BLAKE2b-256 checksum How to use checksums |
e8e896b453e37425cfa0c0035035a22b851ebeefc6944d2e43ec3ca92503b873
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 13, 2026.
Transparency log