leduftw-subtitler 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- leduftw_subtitler-1.0.0/LICENSE +21 -0
- leduftw_subtitler-1.0.0/PKG-INFO +363 -0
- leduftw_subtitler-1.0.0/README.md +352 -0
- leduftw_subtitler-1.0.0/pyproject.toml +26 -0
- leduftw_subtitler-1.0.0/setup.cfg +4 -0
- leduftw_subtitler-1.0.0/src/leduftw_subtitler.egg-info/PKG-INFO +363 -0
- leduftw_subtitler-1.0.0/src/leduftw_subtitler.egg-info/SOURCES.txt +32 -0
- leduftw_subtitler-1.0.0/src/leduftw_subtitler.egg-info/dependency_links.txt +1 -0
- leduftw_subtitler-1.0.0/src/leduftw_subtitler.egg-info/entry_points.txt +2 -0
- leduftw_subtitler-1.0.0/src/leduftw_subtitler.egg-info/top_level.txt +1 -0
- leduftw_subtitler-1.0.0/src/subtitler/__init__.py +7 -0
- leduftw_subtitler-1.0.0/src/subtitler/__main__.py +4 -0
- leduftw_subtitler-1.0.0/src/subtitler/align.py +162 -0
- leduftw_subtitler-1.0.0/src/subtitler/cli.py +382 -0
- leduftw_subtitler-1.0.0/src/subtitler/config.py +118 -0
- leduftw_subtitler-1.0.0/src/subtitler/errors.py +21 -0
- leduftw_subtitler-1.0.0/src/subtitler/media.py +110 -0
- leduftw_subtitler-1.0.0/src/subtitler/pipeline.py +122 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/__init__.py +74 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/_azure_speech.py +294 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/_http.py +58 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/azure_fast.py +128 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/azure_hybrid.py +111 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/azure_mai.py +158 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/base.py +60 -0
- leduftw_subtitler-1.0.0/src/subtitler/providers/elevenlabs_scribe.py +238 -0
- leduftw_subtitler-1.0.0/src/subtitler/srt.py +208 -0
- leduftw_subtitler-1.0.0/src/subtitler/translate.py +233 -0
- leduftw_subtitler-1.0.0/src/subtitler/translation_guide.md +165 -0
- leduftw_subtitler-1.0.0/src/subtitler/update.py +227 -0
- leduftw_subtitler-1.0.0/tests/test_cli.py +280 -0
- leduftw_subtitler-1.0.0/tests/test_packaging.py +86 -0
- leduftw_subtitler-1.0.0/tests/test_subtitles.py +494 -0
- leduftw_subtitler-1.0.0/tests/test_update.py +345 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 leduftw
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: leduftw-subtitler
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Subtitles for any video, in any language.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/leduftw/subtitler
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# subtitler
|
|
13
|
+
|
|
14
|
+
[](https://github.com/leduftw/subtitler/actions/workflows/playbook.yml)
|
|
15
|
+
[](https://github.com/leduftw/subtitler/releases/latest)
|
|
16
|
+
|
|
17
|
+
Subtitles for any video, in any language.
|
|
18
|
+
|
|
19
|
+
Point it at a video or audio file and it writes an `.srt`: who said what, when
|
|
20
|
+
they said it, and — if you ask — translated into whatever language you need.
|
|
21
|
+
|
|
22
|
+
It works two ways. Run it yourself as a command, or just ask a coding agent
|
|
23
|
+
(Claude Code, Codex, or similar) to do it for you — *"make me English subtitles
|
|
24
|
+
for this film"* is enough. The agent runs the commands and handles the translation
|
|
25
|
+
stage itself. You don't have to know what an SRT is.
|
|
26
|
+
|
|
27
|
+
Under the hood: local `ffmpeg` extracts a clean speech track, that audio (and only
|
|
28
|
+
that audio — never the video) goes to a transcription provider, and the result is
|
|
29
|
+
assembled into subtitles locally. No UI, no database, no background service.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
brew install leduftw/tap/subtitler # Homebrew on macOS or Linux; brings ffmpeg along
|
|
35
|
+
winget install leduftw.subtitler # Windows
|
|
36
|
+
uv tool install leduftw-subtitler # PyPI, with Python 3.10 or newer
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`pipx install leduftw-subtitler` and `pip install leduftw-subtitler` work too. On
|
|
40
|
+
PyPI the package has a longer name because `subtitler` belongs to an unrelated
|
|
41
|
+
project; the command is `subtitler` either way.
|
|
42
|
+
|
|
43
|
+
Each release is submitted to Microsoft's moderated WinGet repository, so a new
|
|
44
|
+
version can take a while to appear there.
|
|
45
|
+
|
|
46
|
+
Or use the one-line installer, which picks the build for your machine, checks it
|
|
47
|
+
against the release's `SHA256SUMS` and installs it without `sudo`:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
curl -fsSL https://github.com/leduftw/subtitler/releases/latest/download/subtitler-installer.sh | sh
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```powershell
|
|
54
|
+
irm https://github.com/leduftw/subtitler/releases/latest/download/subtitler-installer.ps1 | iex
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Set `SUBTITLER_VERSION` to install a specific version or `SUBTITLER_INSTALL_DIR` to
|
|
58
|
+
choose the destination. If the destination isn't on `PATH`, the installer prints the
|
|
59
|
+
line to add.
|
|
60
|
+
|
|
61
|
+
Every release also ships standalone programs for macOS, Linux and Windows, each for
|
|
62
|
+
both Arm and Intel/AMD chips, with stable names such as `subtitler-macos-arm64.zip`.
|
|
63
|
+
They carry their own Python. Grab the archive for your platform from the
|
|
64
|
+
[latest release](https://github.com/leduftw/subtitler/releases/latest), extract it,
|
|
65
|
+
and run `subtitler`. A standalone program unpacks itself each time it starts, which
|
|
66
|
+
takes a couple of seconds on a Mac. If macOS Gatekeeper blocks a browser-downloaded
|
|
67
|
+
program, clear the quarantine flag with `xattr -d com.apple.quarantine subtitler`.
|
|
68
|
+
|
|
69
|
+
### Requirements
|
|
70
|
+
|
|
71
|
+
- `ffmpeg` and `ffprobe` on `PATH`. Homebrew installs them for you; elsewhere, use
|
|
72
|
+
`winget install Gyan.FFmpeg` on Windows or your system's package manager on Linux.
|
|
73
|
+
- `AZURE_SPEECH_ENDPOINT` and `AZURE_SPEECH_API_KEY` for the Azure providers (`azure-fast`, `azure-hybrid`)
|
|
74
|
+
- `ELEVENLABS_API_KEY` for `scribe`
|
|
75
|
+
|
|
76
|
+
### Updating
|
|
77
|
+
|
|
78
|
+
`subtitler update` (or `upgrade`) checks for a newer release and installs it the same
|
|
79
|
+
way subtitler was installed: `brew upgrade leduftw/tap/subtitler` for Homebrew,
|
|
80
|
+
`winget upgrade --id leduftw.subtitler` for WinGet, `uv tool upgrade`, `pipx upgrade`
|
|
81
|
+
or `pip install --upgrade` for PyPI, and the one-line installer again, into the same
|
|
82
|
+
folder, for everything else. A new version reaches WinGet only after Microsoft's
|
|
83
|
+
review, so until it does, `subtitler update` says the release isn't on WinGet yet.
|
|
84
|
+
A clone isn't updated this way; use `git pull` instead.
|
|
85
|
+
|
|
86
|
+
### Run from a clone
|
|
87
|
+
|
|
88
|
+
With Python 3.10 or newer, no install step and no dependencies:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
git clone https://github.com/leduftw/subtitler.git
|
|
92
|
+
cd subtitler
|
|
93
|
+
./subtitler --help
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
That launcher is a shell script. In PowerShell or `cmd`, run `pip install .` from the
|
|
97
|
+
clone instead, which gives you the `subtitler` command.
|
|
98
|
+
|
|
99
|
+
## Usage
|
|
100
|
+
|
|
101
|
+
### Command line
|
|
102
|
+
|
|
103
|
+
`subtitler --help` (or `-h`, or `help`) lists every option, and `subtitler --version`
|
|
104
|
+
(or `-V`, or `version`) prints the version. Run with no arguments at all, it prints
|
|
105
|
+
the same help and what it needs: a video or audio file. The help is colored only in
|
|
106
|
+
a terminal; `--no-color`, `NO_COLOR` or `TERM=dumb` turn the colors off.
|
|
107
|
+
|
|
108
|
+
`help`, `version`, `update` and `upgrade` are commands when they come first. To read
|
|
109
|
+
a file with one of those names, write `./help`.
|
|
110
|
+
|
|
111
|
+
A mistake on the command line, such as an unknown option or a file that isn't
|
|
112
|
+
there, exits 2. A failure while running, such as a missing `ffmpeg` or a
|
|
113
|
+
provider error, exits 1.
|
|
114
|
+
|
|
115
|
+
### Providers
|
|
116
|
+
|
|
117
|
+
Every provider produces word-level timing and speaker labels in sync with the audio.
|
|
118
|
+
|
|
119
|
+
| `--provider` | Model | Passes | Cost/min | Cost/hour | Notes |
|
|
120
|
+
| --- | --- | --- | --- | --- | --- |
|
|
121
|
+
| `scribe` (default) | ElevenLabs Scribe v2 | 1 | $0.0037 | $0.22 | Cheapest, lowest WER |
|
|
122
|
+
| `azure-fast` | Azure fast transcription | 1 | $0.006 | $0.36 | No ElevenLabs account needed |
|
|
123
|
+
| `azure-hybrid` | MAI-1.5 text + fast timing | 2 | $0.012 | $0.72 | Cleaner text than `azure-fast`, at 2× its cost |
|
|
124
|
+
|
|
125
|
+
`scribe` is the default because it is both the cheapest of the three and the most
|
|
126
|
+
accurate: it tops the independent
|
|
127
|
+
[Artificial Analysis leaderboard](https://artificialanalysis.ai/speech-to-text/non-streaming)
|
|
128
|
+
at 2.2% WER, against MAI's 2.4%. Use the Azure providers when you have no
|
|
129
|
+
ElevenLabs account, or for locales Scribe doesn't cover.
|
|
130
|
+
|
|
131
|
+
### Speaker labels (diarization)
|
|
132
|
+
|
|
133
|
+
Without speaker separation, an interruption collapses into nonsense — one person
|
|
134
|
+
says "Sorry, I…" and the other cuts in with "You don't need to apologize", and
|
|
135
|
+
the subtitle reads `Sorry I you don't need to apologize.`
|
|
136
|
+
|
|
137
|
+
Every provider identifies who is speaking, starts a new subtitle at each change of
|
|
138
|
+
speaker, and marks each turn with the standard `-` dialogue prefix:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
2
|
|
142
|
+
00:00:07,120 --> 00:00:08,340
|
|
143
|
+
- Sorry, I
|
|
144
|
+
|
|
145
|
+
3
|
|
146
|
+
00:00:08,340 --> 00:00:09,900
|
|
147
|
+
- You don't need to apologize.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
This is **on by default** — Azure bills diarization at no extra cost on fast
|
|
151
|
+
transcription, and a recording that turns out to have only one voice renders with
|
|
152
|
+
no prefixes at all, so there's nothing to lose.
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
# Turn it off:
|
|
156
|
+
subtitler "video.mp4" --provider azure-fast --no-diarize
|
|
157
|
+
|
|
158
|
+
# Raise or lower the speaker ceiling (2-35, default 8):
|
|
159
|
+
subtitler "video.mp4" --provider azure-fast --max-speakers 2
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`--max-speakers 2` is worth setting for a two-person interview: a tighter bound
|
|
163
|
+
makes Azure less likely to split one voice across two speaker ids.
|
|
164
|
+
|
|
165
|
+
Diarization is not perfect. Where Azure gives both sides of an interruption the
|
|
166
|
+
same speaker id, the two still share one subtitle — expect a handful of these per
|
|
167
|
+
recording.
|
|
168
|
+
|
|
169
|
+
### Scribe Quick Start
|
|
170
|
+
|
|
171
|
+
```sh
|
|
172
|
+
export ELEVENLABS_API_KEY="..."
|
|
173
|
+
|
|
174
|
+
subtitler \
|
|
175
|
+
--provider scribe \
|
|
176
|
+
"video.mp4" \
|
|
177
|
+
--language en \
|
|
178
|
+
--output "video.en.srt"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Scribe can also tag non-speech sounds, which is useful for accessibility subtitles:
|
|
182
|
+
|
|
183
|
+
```sh
|
|
184
|
+
subtitler --provider scribe "video.mp4" --tag-audio-events
|
|
185
|
+
# ... produces cues like "(laughter)" and "(footsteps)"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
For a dry run that extracts audio but calls no API:
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
subtitler \
|
|
192
|
+
"video.mp4" \
|
|
193
|
+
--language en \
|
|
194
|
+
--output "video.en.srt" \
|
|
195
|
+
--dry-run
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Azure Quick Start
|
|
199
|
+
|
|
200
|
+
The Azure providers need an Azure Speech resource. With the
|
|
201
|
+
[Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli)
|
|
202
|
+
installed, you can read its endpoint and key straight into the environment:
|
|
203
|
+
|
|
204
|
+
```sh
|
|
205
|
+
# Sign in (opens a browser).
|
|
206
|
+
az login
|
|
207
|
+
|
|
208
|
+
# List your Speech resources, then copy the name + resource group of the one to use.
|
|
209
|
+
az cognitiveservices account list \
|
|
210
|
+
--query "[?kind=='SpeechServices' || kind=='AIServices'].{name:name, resourceGroup:resourceGroup, kind:kind, endpoint:properties.endpoint}" \
|
|
211
|
+
--output table
|
|
212
|
+
|
|
213
|
+
# Fill these two in from the table above.
|
|
214
|
+
RESOURCE_GROUP="<your-resource-group>"
|
|
215
|
+
SPEECH_RESOURCE="<your-resource-name>"
|
|
216
|
+
|
|
217
|
+
# Read the endpoint and key subtitler expects into the environment.
|
|
218
|
+
export AZURE_SPEECH_ENDPOINT="$(az cognitiveservices account show \
|
|
219
|
+
--name "$SPEECH_RESOURCE" --resource-group "$RESOURCE_GROUP" \
|
|
220
|
+
--query "properties.endpoint" --output tsv)"
|
|
221
|
+
export AZURE_SPEECH_API_KEY="$(az cognitiveservices account keys list \
|
|
222
|
+
--name "$SPEECH_RESOURCE" --resource-group "$RESOURCE_GROUP" \
|
|
223
|
+
--query "key1" --output tsv)"
|
|
224
|
+
|
|
225
|
+
# Sanity check (should print https://<resource>.cognitiveservices.azure.com/).
|
|
226
|
+
echo "$AZURE_SPEECH_ENDPOINT"
|
|
227
|
+
|
|
228
|
+
subtitler \
|
|
229
|
+
--provider azure-fast \
|
|
230
|
+
"video.mp4" \
|
|
231
|
+
--language en \
|
|
232
|
+
--output "video.en.srt"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Azure accepts large uploads (500 MiB for `azure-fast`, 300 MiB for `azure-hybrid`):
|
|
236
|
+
|
|
237
|
+
```sh
|
|
238
|
+
subtitler \
|
|
239
|
+
--provider azure-fast \
|
|
240
|
+
"video.mp4" \
|
|
241
|
+
--audio-bitrate 48k \
|
|
242
|
+
--output "video.srt"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Best wording Azure can give: `azure-hybrid`
|
|
246
|
+
|
|
247
|
+
MAI-Transcribe-1.5 is Azure's LLM transcription mode, and it writes cleaner text
|
|
248
|
+
than plain fast transcription does. But Azure returns only coarse timing for it
|
|
249
|
+
and can't diarize it at all — on its own it collapses a whole recording into one
|
|
250
|
+
enormous subtitle, which is why it isn't offered as a `--provider` choice.
|
|
251
|
+
|
|
252
|
+
`azure-hybrid` makes it usable: it runs MAI for the text and Azure fast
|
|
253
|
+
transcription for word-level timing *and speakers*, then merges them so each line
|
|
254
|
+
appears as it's spoken and attributed to whoever said it. Same credentials, two
|
|
255
|
+
transcription passes (about 2× the cost).
|
|
256
|
+
|
|
257
|
+
This is the best transcript *Azure* offers, not the best transcript available:
|
|
258
|
+
Scribe still scores lower on the WER leaderboard (2.2% against MAI's 2.4%) and
|
|
259
|
+
costs a third as much. Reach for `azure-hybrid` when you're staying on Azure and
|
|
260
|
+
`azure-fast`'s wording isn't good enough.
|
|
261
|
+
|
|
262
|
+
```sh
|
|
263
|
+
subtitler \
|
|
264
|
+
--provider azure-hybrid \
|
|
265
|
+
"video.mp4" \
|
|
266
|
+
--language en \
|
|
267
|
+
--output "video.en.srt"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### Languages MAI doesn't cover
|
|
271
|
+
|
|
272
|
+
MAI supports 43 languages; for anything outside that set, `azure-hybrid`
|
|
273
|
+
auto-detects and often produces wrong-language or mistimed output.
|
|
274
|
+
|
|
275
|
+
`scribe` covers 90+ languages and takes a plain ISO code, so it is the simplest
|
|
276
|
+
option:
|
|
277
|
+
|
|
278
|
+
```sh
|
|
279
|
+
subtitler "video.mp4" --language es --output "video.es.srt"
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`azure-fast` also covers many more locales than MAI, but wants a BCP-47 region
|
|
283
|
+
code (`es-ES`, `en-US`); bare codes like `es` are mapped for you:
|
|
284
|
+
|
|
285
|
+
```sh
|
|
286
|
+
subtitler \
|
|
287
|
+
--provider azure-fast \
|
|
288
|
+
"video.mp4" \
|
|
289
|
+
--language es-ES \
|
|
290
|
+
--output "video.es.srt"
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Run `subtitler --provider <name> --list-languages` for a provider's set.
|
|
294
|
+
|
|
295
|
+
### Translating subtitles
|
|
296
|
+
|
|
297
|
+
Transcription and translation are separate stages. No speech provider offers
|
|
298
|
+
translation *and* word-level timing *and* speakers in one call — Azure's LLM
|
|
299
|
+
Speech `translate` task drops both word offsets and diarization, and Scribe
|
|
300
|
+
doesn't translate at all. So translation runs over cues that are already
|
|
301
|
+
correctly timed, and only the text changes.
|
|
302
|
+
|
|
303
|
+
Translating cue by cue produces nonsense, because a sentence usually spans
|
|
304
|
+
several cues. Instead, `subtitler` writes a **worksheet** that groups cues into
|
|
305
|
+
speaker turns — so the translator reads whole sentences — while numbering every
|
|
306
|
+
line so the result reassembles onto the original timings.
|
|
307
|
+
|
|
308
|
+
```sh
|
|
309
|
+
# 1. Write the worksheet
|
|
310
|
+
subtitler film.es.srt \
|
|
311
|
+
--language es --target-language en \
|
|
312
|
+
--emit-worksheet work.txt
|
|
313
|
+
|
|
314
|
+
# 2. Translate the numbered lines in work.txt (see below)
|
|
315
|
+
|
|
316
|
+
# 3. Rebuild the subtitles on the original timings
|
|
317
|
+
subtitler film.es.srt \
|
|
318
|
+
--apply-worksheet work.txt \
|
|
319
|
+
--target-language en \
|
|
320
|
+
-o film.en.srt
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Step 3 validates the numbering and refuses a worksheet that doesn't line up — a
|
|
324
|
+
single missing line would shift every later subtitle onto the wrong timestamp.
|
|
325
|
+
|
|
326
|
+
**There is no translation engine, by design.** Step 2 is done by the agent
|
|
327
|
+
itself, working through the worksheet directly. `subtitler --translation-guide`
|
|
328
|
+
prints the instructions for doing it well, and the worksheet and `--help` both point
|
|
329
|
+
an agent at them, so any agent can follow them on an installed copy. In a clone,
|
|
330
|
+
the `.claude/skills/translate-subtitles` skill hands Claude Code the same guide.
|
|
331
|
+
Machine translation flattens the register of ordinary speech; an agent that can read
|
|
332
|
+
the whole scene keeps the slang, the tone and the interruptions intact.
|
|
333
|
+
|
|
334
|
+
The practical consequence: a translated file can't be regenerated by rerunning
|
|
335
|
+
the tool. Keep the worksheet if the translation was expensive to produce.
|
|
336
|
+
|
|
337
|
+
If you are driving this through an agent, you don't need to run any of the above
|
|
338
|
+
by hand — ask for the language you want and it will do all three steps.
|
|
339
|
+
|
|
340
|
+
### Notes
|
|
341
|
+
|
|
342
|
+
- Pass `--output` to say where the `.srt` goes. Without it, the run writes to `outputs/<input-stem>.<language-or-auto>.srt` under the current directory, creating that directory if needed.
|
|
343
|
+
- Default audio extraction is mono speech audio at `16 kHz` and `48k`, MP3. Diarization requires mono, which is what the pipeline already extracts.
|
|
344
|
+
- For very long files, lower `--audio-bitrate` to stay under the provider upload limit.
|
|
345
|
+
- Use `--language` when you know it; it improves accuracy and reduces language-detection ambiguity.
|
|
346
|
+
- Language hints use short codes. English is `en`, German is `de`, Spanish is `es`. Run `subtitler --list-languages` for the full supported language/code list.
|
|
347
|
+
- `azure-hybrid` inherits MAI's 43-language list, which is shorter than Whisper's. If you pass a `--language` it doesn't list, `subtitler` drops the hint and lets Azure auto-detect; run `subtitler --provider azure-hybrid --list-languages` to see its set.
|
|
348
|
+
- OpenAI is deliberately not supported. `whisper-1` is legacy (removed 2027-01-20), its replacement `gpt-transcribe` returns no timestamps at all, and `gpt-4o-transcribe-diarize` returns only speaker-turn segments — measured on a 3-minute clip, 7 of its 91 segments ran over 6 seconds with no word timings to split them. None of the three can produce well-timed subtitles.
|
|
349
|
+
|
|
350
|
+
## Development
|
|
351
|
+
|
|
352
|
+
subtitler is plain Python with no dependencies beyond the standard library, and
|
|
353
|
+
neither do its tests:
|
|
354
|
+
|
|
355
|
+
```sh
|
|
356
|
+
python3 -m unittest discover tests
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
See [docs/system.md](https://github.com/leduftw/subtitler/blob/main/docs/system.md) for the architecture and implementation details.
|
|
360
|
+
|
|
361
|
+
## License
|
|
362
|
+
|
|
363
|
+
[MIT](https://github.com/leduftw/subtitler/blob/main/LICENSE)
|