mixing

Tools for video and audio editing.

mixing is organized into focused subpackages, each with a clear dependency footprint so you only pay for what you import:

  • mixing.audio — sliceable Audio, fades, crop, concat, overlay, alignment, segmentation (needs pydub).

  • mixing.video — sliceable Video, crop, loop, audio replace/normalize, Ken Burns, thumbnails, subtitles (needs moviepy / opencv).

  • mixing.transcript — speech-to-text (ElevenLabs Scribe, stdlib HTTP), filler removal, transcript formats (no heavy deps).

  • mixing.dubbing — text-to-speech re-voicing / translation from SRT.

  • mixing.srt — canonical SRT/timeline parsing & formatting (pure).

  • mixing.chapters — transcript → chapter markers (pure; LLM optional).

Lazy by design. Importing mixing (or a light submodule such as mixing.chapters / mixing.srt) does not import moviepy or opencv. The heavy backends load only when you first touch a name that needs them — e.g. mixing.Video or from mixing.video import replace_audio.

The top-level namespace is a curated, lazily-resolved facade over the subpackages; see __all__. For the full surface of a subsystem, import it directly (from mixing.audio import ...).

Functions

detect_chapters(transcript, *[, duration, ...])

Detect chapter markers from a transcript.

dump_srt(cues)

Serialize cues back to SRT text, renumbering from 1.

has_ffmpeg()

Return True if the ffmpeg binary is available on PATH.

parse_srt(srt_text)

Parse SRT text into a list of Cue objects.

remove_fillers(input_media, output_dir, *[, ...])

End-to-end filler removal.

seconds_to_srt_time(seconds)

Format a time in seconds as an SRT timestamp HH:MM:SS,mmm.

srt_for_media(media, *[, srt_path, reuse, ...])

Return (srt_text, srt_path) for media, transcribing if needed.

srt_time_to_seconds(timestamp)

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

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

Synthesize text to speech with ElevenLabs and return audio bytes.

transcribe(audio, *[, api_key, model_id, ...])

Transcribe audio with ElevenLabs Scribe.

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

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

Classes

Chapter(start, title)

A chapter marker: a start time (seconds) and a short title.

Cue(index, start, end, text)

One SRT subtitle cue.

FillerRemovalResult(cleaned_media, ...[, ...])

Paths and computed ranges produced by remove_fillers().

Exceptions

MixingError

Base for every error mixing raises on its own behalf.

WindowTooWideForClip(*, clip_index, ...)

An explicit analysis window leaves a clip no second, independent look.

class mixing.Chapter(start, title)[source]

Bases: object

A chapter marker: a start time (seconds) and a short title.

class mixing.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).

class mixing.FillerRemovalResult(cleaned_media, transcript_md, transcript_srt, cleaned_md, cleaned_srt, scribe_json, cuts_json, keeps_json, duration, cuts=list(), keeps=list())[source]

Bases: object

Paths and computed ranges produced by remove_fillers().

exception mixing.MixingError[source]

Bases: Exception

Base for every error mixing raises on its own behalf.

exception mixing.WindowTooWideForClip(*, clip_index, clip_duration_s, window_s, hop_s, max_window_s)[source]

Bases: MixingError, ValueError

An explicit analysis window leaves a clip no second, independent look.

Raised by mixing.audio.align_clips_to_reference() when the caller passes a window_s wider than max_window_s — the length above which the clip holds no second INDEPENDENT window, so the consensus vote has nothing to arbitrate and support comes back None.

Why an error and not a clamp or a silent single window: the caller asked for a windowed vote and would otherwise receive a single whole-clip correlation whose only signal is the ABSENCE of a support number — indistinguishable from a clip that is genuinely too short to support at any window (issue #43). Clamping would be worse still: an explicit window_s is the caller saying what a window means for their material, and silently measuring at a different one makes support incomparable across a set for a reason nothing reports.

It subclasses ValueError because it is an argument that cannot be honored, so existing except ValueError handlers around alignment keep working.

clip_index

Position of the offending clip in the clips sequence, or None when the caller did not identify one.

clip_duration_s

The clip’s duration in seconds.

window_s

The window that was asked for.

hop_s

The hop in force — the caller’s, or the one derived from window_s. Reported because it is part of the grid that was asked for; it is NOT part of the bound, which does not depend on it (see _max_supportable_window_s()).

max_window_s

The largest window that still leaves this clip a second, independent look. The bound is inclusive, so for any clip long enough to be worth aligning a retry at exactly this window measures — which is what makes it worth reporting. A degenerate clip of a sample or two has no such window at all; the value is floored at one sample there and is a lower bound rather than a promise.

mixing.detect_chapters(transcript, *, duration=None, min_chapters=3, max_chapters=8, min_spacing=10.0, target_count=None, segment_fn=None, model=None)[source]

Detect chapter markers from a transcript.

Parameters:
  • transcript – One of — a Scribe response dict (with "words"), a Scribe words list, SRT text, or a list of cue dicts/objects exposing start/end/text.

  • duration (float | None) – Media duration in seconds. Inferred from the transcript’s last timestamp when omitted; used to choose a sensible chapter count and to bound the final marker.

  • min_chapters (int) – Minimum chapters worth showing. If fewer survive the constraints, an empty list is returned.

  • max_chapters (int) – Upper bound on chapter count.

  • min_spacing (float) – Minimum seconds between consecutive chapters (players such as YouTube require >= 10s).

  • target_count (int | None) – Desired chapter count. When omitted, scales with duration (roughly one chapter per ~1.5 min, clamped to [min_chapters, max_chapters]).

  • segment_fn (Optional[Callable[[Sequence[dict], int], list[dict]]]) – Pluggable segmenter (segments, target_count) -> [{start, title}]. Defaults to default_segment_fn() (LLM-backed).

  • model (str | None) – Optional LLM model override for the default segmenter.

Return type:

list[Chapter]

Returns:

A list of Chapter, first at 0:00, spaced by at least min_spacing — or [] when the media can’t support min_chapters (e.g. it is too short).

mixing.dump_srt(cues)[source]

Serialize cues back to SRT text, renumbering from 1.

Return type:

str

mixing.has_ffmpeg()[source]

Return True if the ffmpeg binary is available on PATH.

Most real video work in mixing (concatenation, trim/pad, dimension normalization, audio replacement) shells out to ffmpeg via moviepy. Without it, some operations degrade silently — producing wrong-duration or lower-quality output instead of failing. Callers should check this at startup and refuse to run rather than emit broken media.

>>> isinstance(has_ffmpeg(), bool)
True
Return type:

bool

mixing.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.remove_fillers(input_media, output_dir, *, output_media=None, fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}), api_key=None, scribe_kwargs=None, apply_keeps_kwargs=None, keep_intermediate=True, scribe_data=None)[source]

End-to-end filler removal.

Parameters:
  • input_media (Union[str, Path]) – Video or audio file to clean.

  • output_dir (Union[str, Path]) – Directory to write transcripts and intermediate files into. Created if missing.

  • output_media (Union[str, Path, None]) – Output cleaned media path. Defaults to output_dir / f"{stem}.cleaned.mp4".

  • audio_events (Iterable[str]) – Override the default filler / event sets.

  • api_key (str | None) – ElevenLabs API key (else env ELEVENLABS_API_KEY).

  • scribe_kwargs (Optional[Mapping[str, object]]) – Extra kwargs forwarded to transcribe().

  • apply_keeps_kwargs (Optional[Mapping[str, object]]) – Extra kwargs forwarded to apply_keeps().

  • keep_intermediate (bool) – If False, delete the extracted audio file.

  • scribe_data (dict | None) – If provided, skip the network call and use this dict (must match Scribe’s response shape). Useful for tests / replays.

Return type:

FillerRemovalResult

Returns:

A FillerRemovalResult with file paths and the computed cut/keep ranges.

mixing.seconds_to_srt_time(seconds)[source]

Format a time in seconds as an SRT timestamp HH:MM:SS,mmm.

Milliseconds are rounded (not truncated), with carry handled correctly, and negative inputs clamp to zero.

>>> seconds_to_srt_time(2592.187)
'00:43:12,187'
>>> seconds_to_srt_time(1.5)
'00:00:01,500'
>>> seconds_to_srt_time(-3)
'00:00:00,000'
Return type:

str

mixing.srt_for_media(media, *, srt_path=None, reuse=True, refresh=False, cache=True, max_chars=80, **transcribe_kwargs)[source]

Return (srt_text, srt_path) for media, transcribing if needed.

Parameters:
  • media (Union[str, Path]) – Audio or video file (Scribe extracts audio from video).

  • srt_path (Union[str, Path, None]) – Where the SRT lives/should be written. Defaults to the media path with an .srt suffix.

  • reuse (bool) – When True (default) and the SRT already exists, read and return it without re-transcribing.

  • refresh (bool) – Force re-transcription even if the SRT exists (overwrites it).

  • cache (bool) – Pass-through to mixing.transcript.transcribe()’s on-disk response cache.

  • max_chars (int) – Max characters per SRT cue when generating.

  • transcribe_kwargs – Extra args forwarded to transcribe (e.g. language_code, diarize).

Return type:

tuple[str, Path]

Returns:

The SRT text and the path it lives at.

mixing.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.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.transcribe(audio, *, api_key=None, model_id='scribe_v1', timestamps_granularity='word', tag_audio_events=True, diarize=False, language_code=None, extra_fields=None, timeout=600.0, cache=False, refresh=False)[source]

Transcribe audio with ElevenLabs Scribe.

Parameters:
  • audio (Union[str, Path, bytes]) – Path to an audio/video file, or raw bytes.

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

  • model_id (str) – Scribe model id (currently scribe_v1).

  • timestamps_granularity (str) – "word" (default), "character", or "none".

  • tag_audio_events (bool) – Whether to surface (laughs) / (coughs) etc.

  • diarize (bool) – Speaker diarization on/off.

  • language_code (str | None) – Optional BCP-47 hint to skip language detection.

  • extra_fields (Optional[Mapping[str, str]]) – Additional multipart fields to send (forward compatible).

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

  • cache (Union[bool, str, Path]) – False (default) → no cache. True → use default_cache_dir(). A path → use that directory. The cache key is the SHA-256 of the audio bytes plus all request parameters that affect the response.

  • refresh (bool) – When True and cache is enabled, force a re-call and overwrite the cached entry. Useful for invalidating stale entries when Scribe upgrades its model.

Return type:

dict[str, Any]

Returns:

The raw JSON response from ElevenLabs (a dict). When timestamps_granularity="word" (default), the response contains a "words" list where each entry has at minimum {"text", "start", "end", "type", "confidence"}. Non-word events (type != "word") include (laughs) etc. when tag_audio_events=True.

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

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

mixing.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:
  • srt (Union[str, Sequence[Cue]]) – SRT text or a list of Cue objects.

  • target_language (str) – Human-readable target language (e.g. "French").

  • source_language (str | None) – Optional source language hint.

  • translate_fn (Optional[Callable[[Sequence[str], str, str | None], list[str]]]) – A callable (texts, target_language, source_language) -> list[str] returning one translation per input text, in order. Defaults to default_translate_fn() (LLM-backed).

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

chapters

Detect chapter markers from a transcript — platform-agnostic.

dubbing

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

egress

Canonical egress: route a produced result to its destination.

errors

Typed errors mixing raises — the home for exceptions a caller may want to catch.

srt

Canonical SRT / timeline parsing and formatting (pure, dependency-free).

transcript

Transcript-driven media editing.

util

General utilities for mixing video, audio, etc.