Generate, translate, and burn SRT subtitles locally with MLX Whisper.
Project description
srtforge
Generate, translate, and burn .srt subtitles locally on Apple Silicon.
srtforge extracts audio with ffmpeg, transcribes it with local MLX Whisper,
optionally re-cues the captions for readability, and can translate subtitles
with a local MLX language model. No cloud APIs or keys are required.
Features
- Local Whisper transcription through
mlx-whisper - Sentence-aware subtitle cueing from word timestamps
- Standard SRT output
- Optional local LLM translation with timestamp preservation
- Optional hard-subtitle burn-in through ffmpeg/libass
- Installable Python CLI:
srtforge
Requirements
- macOS on Apple Silicon
- Python 3.10, 3.11, or 3.12
ffmpegon yourPATH- Enough disk/RAM for the models you choose
The default transcription model is mlx-community/whisper-large-v3-turbo.
The default translation model is mlx-community/gemma-4-26b-a4b-it-4bit.
First use downloads models from Hugging Face into the local cache. Later runs can use the cached models.
By default, Hugging Face model files are stored outside this project in your user cache, normally under:
~/.cache/huggingface/hub
You can move that cache by setting Hugging Face cache environment variables such
as HF_HOME or HUGGINGFACE_HUB_CACHE. srtforge does not store downloaded
models in the repository or next to your videos. Temporary extracted WAV files
are created in the system temp directory and deleted after each run.
Install
From GitHub:
pipx install --python python3.11 git+https://github.com/rromanv/srtforge.git
srtforge currently supports Python 3.10-3.12. If your default Python is newer
than that, such as Python 3.14, pass a supported interpreter explicitly with
--python.
Or into a virtual environment:
python3.11 -m venv .venv
source .venv/bin/activate
pip install git+https://github.com/rromanv/srtforge.git
For local development:
git clone https://github.com/rromanv/srtforge.git
cd srtforge
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .
After a PyPI release:
pipx install --python python3.11 srtforge
Troubleshooting
pipx uses Python 3.13 or newer
If installation fails because your default Python is outside the supported
range, install a supported Python and tell pipx to use it:
python3.11 --version
pipx install --python python3.11 git+https://github.com/rromanv/srtforge.git
If python3.11 is not installed, install Python 3.11 or 3.12 first, then rerun
the pipx install --python ... command.
Usage
Generate subtitles next to a video:
srtforge video.mp4
Write to a custom path:
srtforge video.mp4 -o captions.srt
Force the source language:
srtforge video.mp4 -l en
Use another Whisper model:
srtforge video.mp4 -m mlx-community/whisper-small
Disable sentence-aware re-cueing:
srtforge video.mp4 --no-resegment
Tune subtitle readability:
srtforge video.mp4 --max-line-length 37 --max-lines 2 --reading-speed 15
Translate subtitles:
srtforge video.mp4 -t Spanish
srtforge video.mp4 -t "Brazilian Portuguese"
srtforge video.mp4 -t ja --translate-model mlx-community/Qwen3.5-9B-OptiQ-4bit
Burn subtitles into a video:
srtforge merge video.mp4 video.srt -o final.mp4
srtforge merge video.mp4 video.es.srt --crf 16 --font-size 26
Run:
srtforge --help
srtforge merge --help
Readability
By default, srtforge asks Whisper for word-level timestamps and rebuilds cues
so they are easier to read:
- cues prefer sentence boundaries
- lines are wrapped to two lines of 42 characters by default
- long sentences are split across cue boundaries
- cues are paced around 17 characters per second
- cues are adjusted to avoid overlap
Translated subtitles are re-fitted after translation because translated text can be longer or shorter than the source.
ffmpeg Notes
Audio extraction requires ffmpeg.
Burn-in uses ffmpeg's subtitles filter, which requires libass. If your ffmpeg
does not include it, install a full build such as:
nb install ffmpeg-full
Project Layout
src/srtforge/
audio.py # ffmpeg audio extraction
transcribe.py # MLX Whisper transcription
segment.py # sentence-aware re-cueing, wrapping, pacing
translate.py # context-aware local-LLM translation
merge.py # burn subtitles into video with ffmpeg/libass
srt.py # SRT rendering
cli.py # argparse CLI
tests/
test_cli.py
test_merge.py
test_segment.py
test_srt.py
Development
Run the test suite:
python -m unittest discover -s tests
Build release artifacts:
python -m build
python -m twine check dist/*
Publish to PyPI manually:
python -m twine upload dist/*
The repository also includes a GitHub Actions workflow for publishing to PyPI when a release is created. It uses PyPI Trusted Publishing, so no PyPI API token is needed in GitHub.
Before creating a release that should publish to PyPI, configure a pending publisher in your PyPI account:
PyPI project name: srtforge
Owner: rromanv
Repository name: srtforge
Workflow filename: publish.yml
Environment name: pypi
PyPI docs:
- Creating a new project with a pending publisher: https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/
- Publishing with a trusted publisher: https://docs.pypi.org/trusted-publishers/using-a-publisher/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file srtforge-0.1.0.tar.gz.
File metadata
- Download URL: srtforge-0.1.0.tar.gz
- Upload date:
- Size: 19.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b441e23df6293a8c530479dcb5019f695658c7f44a5f09c451c7e854e3b4e111
|
|
| MD5 |
5c08c14ab7fdbb6b11214e90861d6d19
|
|
| BLAKE2b-256 |
5d4012f7d0ad99cd3be742756bf953881c896af29b91ed9009ba09f1dc0861bd
|
Provenance
The following attestation bundles were made for srtforge-0.1.0.tar.gz:
Publisher:
publish.yml on rromanv/srtforge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
srtforge-0.1.0.tar.gz -
Subject digest:
b441e23df6293a8c530479dcb5019f695658c7f44a5f09c451c7e854e3b4e111 - Sigstore transparency entry: 1735895788
- Sigstore integration time:
-
Permalink:
rromanv/srtforge@860aa6fc8623af1fcc0b8379590a7838da8fbde3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/rromanv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@860aa6fc8623af1fcc0b8379590a7838da8fbde3 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file srtforge-0.1.0-py3-none-any.whl.
File metadata
- Download URL: srtforge-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d2334bb17de0178d57ae26c0bece53e5460eb07a13dbb916009ad3f3471aeda
|
|
| MD5 |
9e7bf946a5b8bff16882224a12baf92e
|
|
| BLAKE2b-256 |
9cf6316dc3624b121be4ff67917a11abb8ff8dc2abbcbce86ab180a003d12a1e
|
Provenance
The following attestation bundles were made for srtforge-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on rromanv/srtforge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
srtforge-0.1.0-py3-none-any.whl -
Subject digest:
2d2334bb17de0178d57ae26c0bece53e5460eb07a13dbb916009ad3f3471aeda - Sigstore transparency entry: 1735895811
- Sigstore integration time:
-
Permalink:
rromanv/srtforge@860aa6fc8623af1fcc0b8379590a7838da8fbde3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/rromanv
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@860aa6fc8623af1fcc0b8379590a7838da8fbde3 -
Trigger Event:
workflow_dispatch
-
Statement type: