mixing.dubbing

Text-to-speech dubbing: re-voice or translate a video from its SRT.

The transcript side of mixing turns speech into SRT (ElevenLabs Scribe); this subpackage closes the loop — turning SRT back into speech with an ElevenLabs voice and muxing it over the video, optionally translating the SRT first to produce a foreign-language dub.

Building blocks:

The HTTP clients use stdlib only (no requests / elevenlabs SDK), so this adds no required deps. Needs ffmpeg on PATH and an ElevenLabs API key (env var ELEVENLABS_API_KEY or explicit api_key=). Translation needs either the aix package (pip install 'mixing[llm]') or a custom translate_fn.

Lazy by design. Like the top-level mixing facade, this subpackage uses PEP 562 __getattr__ so that the TTS / SRT building blocks (text_to_speech(), list_voices(), parse_srt(), Cue, …) import with no moviepy dependency. Only dub_video_from_srt() — the end-to-end pipeline that muxes audio over a video — pulls moviepy, and only when it is first accessed.

Quick start:

>>> from mixing.dubbing import dub_video_from_srt, translate_srt
>>> dub_video_from_srt("promo.mp4", "promo.srt", voice_id="...", output="promo.en.mp4")
>>> fr_srt = translate_srt(open("promo.srt").read(), "French")

Functions

default_cache_dir()

Default on-disk cache for synthesized audio.

text_to_speech(text, voice_id, *[, api_key, ...])

Synthesize text to speech with ElevenLabs and return audio bytes.

synthesize_to_file(text, voice_id, path, ...)

Synthesize text and write the audio to path.

list_voices(*[, api_key, timeout])

List the voices available on the account.

find_voice(query, *[, api_key, voices])

Return the first voice whose name or labels contain query (case-insensitive).

search_shared_voices(*[, language, ...])

Search ElevenLabs' public shared voice library (not just the account).

add_shared_voice(public_owner_id, voice_id, ...)

Add a shared-library voice to the account so it can be synthesized.

parse_srt(srt_text)

Parse SRT text into a list of Cue objects.

dump_srt(cues)

Serialize cues back to SRT text, renumbering from 1.

srt_time_to_seconds(timestamp)

Parse an HH:MM:SS,mmm (or .mmm) SRT timestamp to seconds.

translate_srt(srt, target_language, *[, ...])

Translate the cue text of an SRT to target_language, keeping timings.

default_translate_fn(texts, target_language)

LLM-backed translator (segment-count preserving) using aix.chat.

Classes

Cue(index, start, end, text)

One SRT subtitle cue.

class mixing.dubbing.Cue(index, start, end, text)[source]

Bases: object

One SRT subtitle cue.

index

1-based cue number.

Type:

int

start

Start time in seconds.

Type:

float

end

End time in seconds.

Type:

float

text

Cue text (may contain embedded newlines).

Type:

str

property duration: float

Cue duration in seconds (never negative).

mixing.dubbing.add_shared_voice(public_owner_id, voice_id, name, *, api_key=None, timeout=60.0)[source]

Add a shared-library voice to the account so it can be synthesized.

Idempotent-ish: if the voice (by name) is already on the account, returns its existing id instead of erroring on a duplicate add.

Parameters:
  • public_owner_id (str) – public_owner_id from search_shared_voices().

  • voice_id (str) – The shared voice’s voice_id.

  • name (str) – Name to give the added voice on the account.

  • api_key (str | None) – API key. Falls back to env var ELEVENLABS_API_KEY.

Return type:

str

Returns:

The account-local voice_id to pass to text_to_speech().

mixing.dubbing.default_cache_dir()[source]

Default on-disk cache for synthesized audio.

Honors $MIXING_TTS_CACHE_DIR, then $XDG_CACHE_HOME, then ~/.cache/. Final segment is always mixing/tts.

Return type:

Path

mixing.dubbing.default_translate_fn(texts, target_language, source_language=None, *, model=None)[source]

LLM-backed translator (segment-count preserving) using aix.chat.

Translates all segments in a single call, returning a JSON array so the one-to-one mapping with the input cues is preserved. Falls back to a clear error if aix is not importable — pass your own translate_fn then.

Return type:

list[str]

mixing.dubbing.dump_srt(cues)[source]

Serialize cues back to SRT text, renumbering from 1.

Return type:

str

mixing.dubbing.find_voice(query, *, api_key=None, voices=None)[source]

Return the first voice whose name or labels contain query (case-insensitive).

Convenience for picking a voice by a human name (e.g. "Brian") or a label keyword (e.g. "french") without memorizing voice ids.

Return type:

dict[str, Any] | None

mixing.dubbing.list_voices(*, api_key=None, timeout=60.0)[source]

List the voices available on the account.

Parameters:
  • api_key (str | None) – API key. Falls back to env var ELEVENLABS_API_KEY.

  • timeout (float) – Request timeout in seconds.

Return type:

list[dict[str, Any]]

Returns:

A list of voice dicts. Each has at least voice_id, name, category, and a labels dict (accent, description, age, gender, use_case) describing the voice.

Raises:

RuntimeError – No API key supplied and ELEVENLABS_API_KEY unset.

mixing.dubbing.parse_srt(srt_text)[source]

Parse SRT text into a list of Cue objects.

Tolerant of blank-line spacing variations and of either , or . as the millisecond separator. Cues without a valid time line are skipped.

Return type:

list[Cue]

mixing.dubbing.search_shared_voices(*, language=None, use_cases=None, gender=None, category=None, sort=None, page_size=40, api_key=None, timeout=60.0, **extra_params)[source]

Search ElevenLabs’ public shared voice library (not just the account).

Useful for finding a native voice in a target language (e.g. a French advertising voice) that is not yet on the account. Add the chosen voice with add_shared_voice(), then synthesize with the returned id.

Parameters:
  • language (str | None) – ISO-639-1 language filter, e.g. "fr".

  • use_cases (str | None) – e.g. "advertisement", "narrative_story".

  • gender (str | None) – "male" / "female" / "neutral".

  • category (str | None) – e.g. "professional", "high_quality".

  • sort (str | None) – e.g. "trending".

  • page_size (int) – Max results.

  • extra_params – Any other query params the endpoint accepts.

Return type:

list[dict[str, Any]]

Returns:

A list of shared-voice dicts. Each has voice_id and public_owner_id (both needed by add_shared_voice()), plus name, language, accent, gender, use_case, description.

mixing.dubbing.srt_time_to_seconds(timestamp)[source]

Parse an HH:MM:SS,mmm (or .mmm) SRT timestamp to seconds.

>>> srt_time_to_seconds('00:43:12,187')
2592.187
>>> srt_time_to_seconds('00:00:01.500')
1.5
Return type:

float

mixing.dubbing.synthesize_to_file(text, voice_id, path, **kwargs)[source]

Synthesize text and write the audio to path.

Thin convenience wrapper over text_to_speech(). Extra keyword arguments are forwarded unchanged.

Return type:

Path

Returns:

The path written.

mixing.dubbing.text_to_speech(text, voice_id, *, api_key=None, model_id='eleven_multilingual_v2', output_format='mp3_44100_128', voice_settings=None, language_code=None, timeout=600.0, cache=True, refresh=False, return_cache_status=False)[source]

Synthesize text to speech with ElevenLabs and return audio bytes.

Parameters:
  • text (str) – The text to speak.

  • voice_id (str) – ElevenLabs voice id (see list_voices()).

  • api_key (str | None) – API key. Falls back to env var ELEVENLABS_API_KEY.

  • model_id (str) – TTS model id. Defaults to eleven_multilingual_v2.

  • output_format (str) – ElevenLabs output_format query value, e.g. mp3_44100_128 (default), mp3_44100_192 (needs a paid tier), or pcm_44100.

  • voice_settings (Optional[Mapping[str, Any]]) – Override the default voice settings (stability, similarity_boost, style, use_speaker_boost). Merged over DFLT_VOICE_SETTINGS.

  • language_code (str | None) – Optional ISO-639-1 hint (e.g. "fr"). Only some models honor it; ignored by others.

  • timeout (float) – Total request timeout in seconds.

  • cache (Union[bool, str, Path]) – True (default) → use default_cache_dir(). False → no cache. A path → use that directory. The cache key is a SHA-256 of the text plus every parameter that affects the audio.

  • refresh (bool) – When True and cache is enabled, force a re-call and overwrite the cached entry.

  • return_cache_status (bool) – When True, return (audio, was_cached) instead of just audio. was_cached is True iff the bytes came from the on-disk cache (no ElevenLabs call = no spend), so a caller can attribute real cost. Default False keeps the bytes return.

Return type:

bytes | tuple[bytes, bool]

Returns:

Raw audio bytes in output_format (MP3 by default) — or, when return_cache_status is True, a (audio, was_cached) tuple.

Raises:
  • RuntimeError – No API key supplied and ELEVENLABS_API_KEY unset.

  • HTTPError – Request failed (4xx/5xx).

mixing.dubbing.translate_srt(srt, target_language, *, source_language=None, translate_fn=None)[source]

Translate the cue text of an SRT to target_language, keeping timings.

Parameters:
Return type:

str

Returns:

Translated SRT text with the original cue timings.

Raises:

ValueError – The translator returned a different number of segments than it was given.

Modules

srt

SRT translation for dubbing — built on the canonical mixing.srt.

tts

ElevenLabs text-to-speech (TTS) HTTP client.