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— sliceableAudio, fades, crop, concat, overlay, alignment, segmentation (needspydub).mixing.video— sliceableVideo, crop, loop, audio replace/normalize, Ken Burns, thumbnails, subtitles (needsmoviepy/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 chapter markers from a |
|
Serialize cues back to SRT text, renumbering from 1. |
Return True if the |
|
|
Parse SRT text into a list of |
|
End-to-end filler removal. |
|
Format a time in seconds as an SRT timestamp |
|
Return |
|
Parse an |
|
Synthesize |
|
Transcribe |
|
Translate the cue text of an SRT to |
Classes
|
A chapter marker: a start time (seconds) and a short title. |
|
One SRT subtitle cue. |
|
Paths and computed ranges produced by |
Exceptions
Base for every error |
|
|
An explicit analysis window leaves a clip no second, independent look. |
- class mixing.Chapter(start, title)[source]¶
Bases:
objectA chapter marker: a start time (seconds) and a short title.
- 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:
objectPaths and computed ranges produced by
remove_fillers().
- exception mixing.MixingError[source]¶
Bases:
ExceptionBase for every error
mixingraises on its own behalf.
- exception mixing.WindowTooWideForClip(*, clip_index, clip_duration_s, window_s, hop_s, max_window_s)[source]¶
Bases:
MixingError,ValueErrorAn explicit analysis window leaves a clip no second, independent look.
Raised by
mixing.audio.align_clips_to_reference()when the caller passes awindow_swider thanmax_window_s— the length above which the clip holds no second INDEPENDENT window, so the consensus vote has nothing to arbitrate andsupportcomes backNone.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_sis the caller saying what a window means for their material, and silently measuring at a different one makessupportincomparable across a set for a reason nothing reports.It subclasses
ValueErrorbecause it is an argument that cannot be honored, so existingexcept ValueErrorhandlers around alignment keep working.- clip_index¶
Position of the offending clip in the
clipssequence, orNonewhen 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 Scribewordslist, SRT text, or a list of cue dicts/objects exposingstart/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 withduration(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 todefault_segment_fn()(LLM-backed).model (
str|None) – Optional LLM model override for the default segmenter.
- Return type:
- Returns:
A list of
Chapter, first at0:00, spaced by at leastmin_spacing— or[]when the media can’t supportmin_chapters(e.g. it is too short).
- mixing.has_ffmpeg()[source]¶
Return True if the
ffmpegbinary is available onPATH.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:
- mixing.parse_srt(srt_text)[source]¶
Parse SRT text into a list of
Cueobjects.Tolerant of blank-line spacing variations and of either
,or.as the millisecond separator. Cues without a valid time line are skipped.
- 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 tooutput_dir / f"{stem}.cleaned.mp4".audio_events (
Iterable[str]) – Override the default filler / event sets.api_key (
str|None) – ElevenLabs API key (else envELEVENLABS_API_KEY).scribe_kwargs (
Optional[Mapping[str,object]]) – Extra kwargs forwarded totranscribe().apply_keeps_kwargs (
Optional[Mapping[str,object]]) – Extra kwargs forwarded toapply_keeps().keep_intermediate (
bool) – IfFalse, 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:
- Returns:
A
FillerRemovalResultwith 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:
- 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)formedia, 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.srtsuffix.reuse (
bool) – WhenTrue(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 tomixing.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:
- 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:
- 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
textto speech with ElevenLabs and return audio bytes.- Parameters:
text (
str) – The text to speak.voice_id (
str) – ElevenLabs voice id (seelist_voices()).api_key (
str|None) – API key. Falls back to env varELEVENLABS_API_KEY.model_id (
str) – TTS model id. Defaults toeleven_multilingual_v2.output_format (
str) – ElevenLabsoutput_formatquery value, e.g.mp3_44100_128(default),mp3_44100_192(needs a paid tier), orpcm_44100.voice_settings (
Optional[Mapping[str,Any]]) – Override the default voice settings (stability, similarity_boost, style, use_speaker_boost). Merged overDFLT_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) → usedefault_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) – WhenTrueandcacheis enabled, force a re-call and overwrite the cached entry.return_cache_status (
bool) – WhenTrue, return(audio, was_cached)instead of justaudio.was_cachedisTrueiff the bytes came from the on-disk cache (no ElevenLabs call = no spend), so a caller can attribute real cost. DefaultFalsekeeps thebytesreturn.
- Return type:
- Returns:
Raw audio bytes in
output_format(MP3 by default) — or, whenreturn_cache_statusisTrue, a(audio, was_cached)tuple.- Raises:
RuntimeError – No API key supplied and
ELEVENLABS_API_KEYunset.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
audiowith 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 varELEVENLABS_API_KEY.model_id (
str) – Scribe model id (currentlyscribe_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→ usedefault_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) – WhenTrueandcacheis enabled, force a re-call and overwrite the cached entry. Useful for invalidating stale entries when Scribe upgrades its model.
- Return type:
- 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. whentag_audio_events=True.- Raises:
RuntimeError – No API key supplied and
ELEVENLABS_API_KEYunset.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 ofCueobjects.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 todefault_translate_fn()(LLM-backed).
- Return type:
- Returns:
Translated SRT text with the original cue timings.
- Raises:
ValueError – The translator returned a different number of segments than it was given.
Modules
Detect chapter markers from a transcript — platform-agnostic. |
|
Text-to-speech dubbing: re-voice or translate a video from its SRT. |
|
Canonical egress: route a produced result to its destination. |
|
Typed errors |
|
Canonical SRT / timeline parsing and formatting (pure, dependency-free). |
|
Transcript-driven media editing. |
|
General utilities for mixing video, audio, etc. |