SRT multilingual TTS with forced cue-window timing (xAI Grok)
Project description
srtspeak
Multilingual SRT TTS. Forces each cue into its exact start/end window (xAI Grok TTS + ffmpeg).
Authoritative design: DESIGN.md.
Requirements
| Item | Detail |
|---|---|
| OS | Windows / macOS / Linux (developed and verified on Windows) |
| Python | 3.11+ |
| ffmpeg / ffprobe | Prefer on PATH; optional fallback via imageio-ffmpeg |
| API key | XAI_API_KEY only for real TTS (not needed for dry-run) |
Install
From the repository root:
python -m pip install -e .
With GUI:
python -m pip install -e ".[gui]"
Dev extras (pytest / ruff / Babel):
python -m pip install -e ".[dev]"
Optional ffmpeg fallback via pip:
python -m pip install -e ".[ffmpeg]"
After editable install, the srtspeak command is available. Without install:
set PYTHONPATH=src
python -m srtspeak --help
Japanese yomi preprocess (kanji→hiragana via kanjiconv):
python -m pip install -e ".[ja]"
Combined extras example:
python -m pip install -e ".[gui,ja]"
Launch (Windows)
Double-click or run from the repo root:
| Script | Action |
|---|---|
run_gui.bat |
Start GUI |
run_doctor.bat |
Environment check |
run_srtspeak.bat … |
CLI passthrough (same args as srtspeak) |
Examples:
run_gui.bat
run_doctor.bat
run_srtspeak.bat --help
run_srtspeak.bat dry-run --srt GRAN_TENKU_japan.srt --lang ja
Equivalent without bat:
srtspeak gui
python -m srtspeak gui
Notes:
- Scripts
cdto the repo root automatically. - If
srtspeakis not onPATH, they setPYTHONPATH=srcand usepython -m srtspeak. - If
XAI_API_KEYis unset andUAGENT_GROK_API_KEYis set, it is copied for the session only (not persisted).
When ffmpeg is missing
Resolution order (core/ffmpeg_resolve.py):
ffmpeg/ffprobeonPATH(shutil.which)- Optional dependency
imageio-ffmpegbundled binary (get_ffmpeg_exe()) - Otherwise
FFmpegNotFoundError
| Situation | Behavior |
|---|---|
srtspeak doctor |
Prints ffmpeg: MISSING (...). Exit code 0 (diagnostics only) |
dry-run |
ffmpeg not required (parse + char count + cost estimate only) |
Real build / build-all |
Fails at fit stage. CLI: error: ffmpeg not found on PATH and imageio-ffmpeg is unavailable, exit code 2 |
imageio-ffmpeg only |
Works, but ffprobe may be (none). Full PATH ffmpeg build is recommended |
Install examples (Windows)
WinGet (recommended):
winget install Gyan.FFmpeg
Then in a new terminal:
ffmpeg -version
ffprobe -version
srtspeak doctor
If you prefer not to change PATH:
python -m pip install -e ".[ffmpeg]"
srtspeak doctor
doctor shows source: path for system ffmpeg, or source: imageio_ffmpeg for the pip bundle.
xAI (Grok TTS) signup and API key
TTS is xAI Grok only (POST https://api.x.ai/v1/tts). Create the key in the console.
1. Account
- Open https://console.x.ai/
- Sign up / log in (xAI account)
- Accept terms and set up billing / credits as guided
- TTS is usage-based. dry-run estimate uses $15 / 1M characters (implementation unit price)
2. Create an API key
- Open API Keys (or equivalent) in the console
- Create a new key
- Copy the value (often starts with
xai-)- It may not be shown again — put it in an env var immediately
- Do not put the key in chat, Git,
report.json, or screenshots
Official docs:
- Text to Speech
- Voice
- API:
https://api.x.ai/v1/tts/ voices:https://api.x.ai/v1/tts/voices
3. How this tool reads the key
| Rule | Detail |
|---|---|
| Variable | XAI_API_KEY only |
| CLI flag | No --api-key (avoids shell history) |
| Persistence | No .env read/write; never written to report/logs |
| dry-run | Key optional |
| Real TTS | env → (CLI) getpass prompt → else exit code 2 |
| GUI | env first; masked session input if missing |
Windows cmd (current window only):
set "XAI_API_KEY=xai-..."
srtspeak doctor
PowerShell:
$env:XAI_API_KEY = "xai-..."
srtspeak doctor
Persist for new terminals (cmd):
setx XAI_API_KEY "xai-..."
doctor shows presence only:
XAI_API_KEY: set (env)
or:
XAI_API_KEY: missing
Invalid key / insufficient balance surfaces as TTS error: ... during build (exit code 1).
Commands
srtspeak --help
srtspeak doctor
srtspeak languages
srtspeak voices
srtspeak gui
srtspeak build --help
srtspeak build-all --help
srtspeak dry-run --help
| Command | Purpose |
|---|---|
doctor |
Check XAI_API_KEY, ffmpeg/ffprobe, PySide6 |
languages |
Language codes sendable to the API |
voices |
Grok voices (API if key present, else builtin) |
dry-run |
Parse + char count + cost estimate (no key, no ffmpeg) |
build |
Generate one language |
build-all |
Generate multiple languages in sequence |
gui |
PySide6 GUI |
Global options:
--locale en|ja UI locale (default: SRTSPEAK_LOCALE → LC_ALL/LANG → system → en)
--verbose / --quiet
Place --quiet / --verbose before the subcommand:
srtspeak --locale ja doctor
srtspeak --verbose build --srt sample.srt --lang ja --dry-run
Input / output layout
--out is the output root. Artifacts always go under {out}/{lang}/.
out/{lang}/
cues/ per-cue audio
fitted/ duration-fitted audio
GRAN_TENKU_{lang}.wav final track (fixed name)
report.json
work/{lang}/
raw/
cache/
- Default root:
out→ e.g.out/ja/ --out out/enwith--lang en→out/en/(no double nesting)--out artifactswith--lang pt→artifacts/pt/build-allwritessummary.jsonat the out root
Usage
Environment check
srtspeak doctor
Before a real build you typically want:
XAI_API_KEY: set (env)ffmpeg:path withsource: path(orimageio_ffmpeg)
Cost estimate (no key, no ffmpeg)
srtspeak dry-run --srt GRAN_TENKU_japan.srt --lang ja
Single-language build
set "XAI_API_KEY=xai-..."
srtspeak build --srt GRAN_TENKU_japan.srt --lang ja --voice-id leo
srtspeak build --srt GRAN_TENKU_English.srt --lang en --voice-id leo --out out
srtspeak build --srt GRAN_TENKU_Portugus.srt --lang pt --voice-id leo
Main options:
| Option | Meaning | Default |
|---|---|---|
--srt |
Input SRT | required |
--lang |
Internal key ja / en / pt … |
may be guessed from filename |
--language-code |
BCP-47 sent to API | lang default (pt→pt-BR) |
--out |
Output root (lang appended) | out |
--work-dir |
Work root | work |
--voice-id |
Voice ID | leo |
--short-mode |
pad / stretch |
pad |
--limit N |
First N cues only | all |
--dry-run |
Estimate only | off |
--also-mp3 |
Also write mp3 | off |
--jobs |
Parallelism (MVP: 1 only) | 1 |
Smoke a few cues:
srtspeak build --srt GRAN_TENKU_japan.srt --lang ja --limit 3 --voice-id leo
Portuguese as pt-PT:
srtspeak build --srt GRAN_TENKU_Portugus.srt --lang pt --language-code pt-PT --voice-id leo
Multi-language batch
srtspeak build-all ^
--map ja=GRAN_TENKU_japan.srt ^
--map en=GRAN_TENKU_English.srt ^
--map pt=GRAN_TENKU_Portugus.srt ^
--voice-id leo ^
--out out
Per-language voices:
srtspeak build-all ^
--map ja=GRAN_TENKU_japan.srt ^
--map en=GRAN_TENKU_English.srt ^
--voice-id ja=leo --voice-id en=orion
GUI
python -m pip install -e ".[gui]"
srtspeak gui
- Out field is the root (default
out); lang is appended at run time - API key: env preferred; masked session input if unset
Processing notes
- TTS: xAI Grok unary REST only (
speedalways 1.0) - Fit: ffmpeg CLI only (
atempo0.5–2.0 multi-stage; short cues default to pad) - Timeline: silence canvas + PCM placement; half-open window
[start, end) - Track length: aligned to last cue end (±50 ms)
- Audio: mono s16le 24 kHz WAV
- Cost estimate (dry-run): $15 / 1M chars
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success (doctor is 0 even if ffmpeg is missing) |
| 1 | Runtime errors (e.g. TTS) |
| 2 | Config/args/invalid SRT/ffmpeg missing/key missing |
| 130 | Cancelled (Ctrl+C) |
Development
python -m pip install -e ".[dev]"
set PYTHONPATH=src
python -m pytest -q
python -m ruff check src tests
python -m ruff format src tests
i18n (when messages change):
python scripts/update_i18n.py
Sample inputs (expected alongside the repo)
| File | lang | Notes |
|---|---|---|
GRAN_TENKU_japan.srt |
ja |
|
GRAN_TENKU_English.srt |
en |
|
GRAN_TENKU_Portugus.srt |
pt |
API default pt-BR if unspecified |
Timecodes are assumed aligned across the three files (design-time: 293 cues, 00:00:07,600–00:12:44,000).
License
Apache License 2.0 (see LICENSE)
Author
Hirofumi Ukawa hirofumi@ukawa.biz
Project details
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 srtspeak-0.1.1.tar.gz.
File metadata
- Download URL: srtspeak-0.1.1.tar.gz
- Upload date:
- Size: 70.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2556330370a3078aecffe3d06cae28d41ffddf3824853b27a6bc477ddcbc53b0
|
|
| MD5 |
6e7ff6254b340045f8ee6e3e9f8fb4ce
|
|
| BLAKE2b-256 |
862116104b89fe8a712960492656bba8e425f3062bce346994670d82e2a5c270
|
File details
Details for the file srtspeak-0.1.1-py3-none-any.whl.
File metadata
- Download URL: srtspeak-0.1.1-py3-none-any.whl
- Upload date:
- Size: 70.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f66c17b6b5efe26404f4776a4a4641157e5d18783d25270081771b7ad586a319
|
|
| MD5 |
2b7b81ac77d0204c2d4e8a12c32731b8
|
|
| BLAKE2b-256 |
9925c183d4c23e018d58ad32c588ba67f0c40fe4ffb71d65ea1f3f918414933a
|