> built 2026-10-18 02:54 UTC from 715b89c (main) · mixing 0.0.57. Details: build_info.json

# index.html.md

<!-- generated by epythet -->

# mixing

Tools for **video and audio editing** in Python — slicing, fades, audio
replace/mix, Ken Burns, thumbnails, subtitles, speech-to-text + filler removal,
TTS dubbing, and chapter detection.

```bash
pip install 'mixing[media]'   # audio + video editing (moviepy, opencv, scipy, pydub)
pip install mixing            # base: only the pure modules (srt, chapters, errors, util, egress, transcript, TTS)
```

The base install is light on purpose: it carries no media backend (and so no bundled ffmpeg binary). `mixing.audio` and `mixing.video` need the `media` extra; importing them without it raises an `ImportError` that says so.

---

## 🤖 AI-first: start with the skills & agents

`mixing` is built to be driven by AI coding agents. The fastest, most reliable
way to use it is through the bundled **skills** and **agents** in
[`.claude/`]() — task-oriented playbooks that encode the right
functions, the calling conventions, and the gotchas, so an agent gets it right
the first time.

**Skills** (`.claude/skills/`) — *how to use mixing for a task*:

| Skill                   | Use it for                                                                          |
|-------------------------|-------------------------------------------------------------------------------------|
| [`mixing`]()            | **Start here** — router, architecture, the `output` protocol, extras                |
| [`mixing-audio`]()      | slice, fade, crop, concat, overlay, normalize, align, segment audio                 |
| [`mixing-video`]()      | slice, crop, loop, speed, replace/normalize audio, Ken Burns, thumbnails, subtitles |
| [`mixing-transcript`]() | transcribe, remove fillers, SRT/prose, chapters                                     |
| [`mixing-dubbing`]()    | TTS re-voicing & SRT translation                                                    |

**Agents** (`.claude/agents/`):

- [`mixing-editor`]() — performs an editing task
  end-to-end (“edit this media …”).
- [`mixing-dev`]() — works *on* the mixing codebase.

Using Claude Code? These are discovered automatically from `.claude/`. The
architectural contract they share lives in [`.claude/CLAUDE.md`]().

---

## Quick start

```python
import mixing  # cheap: no moviepy/opencv loaded yet

assert mixing.has_ffmpeg()  # most ops need ffmpeg on PATH

# --- audio ---
from mixing.audio import Audio

Audio("song.mp3")[10:30].fade_in(2).fade_out(3).save(output="clip.mp3")

# --- video ---
from mixing.video import replace_audio, ken_burns_video

replace_audio("clip.mp4", "music.mp3", mix_ratio=0.7, output="mixed.mp4")
ken_burns_video("cover.jpg", duration=8, output="cover.mp4")

# --- transcript: remove "uh"/"um" (ElevenLabs Scribe + ffmpeg) ---
from mixing.transcript import remove_fillers

result = remove_fillers("talk.mov", "out/")  # uses $ELEVENLABS_API_KEY
print(result.cleaned_media)
```

## Core ideas (learn once)

**The `output` protocol.** Every result-producing function takes one `output`
argument whose *role* is constant and *type* is open:

```python
crop_video("in.mp4", 5, 15, output="out.mp4")  # file → writes it, returns Path
crop_video("in.mp4", 5, 15, output="clips/")  # dir  → auto-named file inside
crop_video("in.mp4", 5, 15)  # None → saves beside the input
fade_in("in.mp3", output=lambda audio: audio)  # callable → receives the result
```

Object producers (audio editing) return the in-memory object when `output=None`,
so you can chain and save when ready. Functions that emit *several* artifacts
qualify the destinations (`remove_fillers(media, output_dir=…, output_media=…)`).

**Lazy & tiered.** `import mixing` (and `import mixing.chapters` /
`mixing.srt`) pull **no** heavy backends; moviepy/opencv/pydub load only when you
touch a name that needs them. Import the facade (`mixing.Video`) or the
subpackage (`from mixing.video import Video`).

**Sliceable media.** `Audio` and `Video` are lazy, sliceable, context-managed
views:

```python
from mixing.video import Video

with Video("movie.mp4") as v:
    v[10:30].save(output="cut.mp4")  # seconds; v[100] returns a single frame
```

**Logging, not printing.** `mixing` never writes progress messages to stdout —
every module logs through `logging.getLogger(__name__)`, and the package root
carries a `NullHandler`, so `mixing` is silent by default. This keeps library
calls usable from a stdout-parsing consumer. To see mixing’s own progress
messages (e.g. while working interactively, or when an operation like
`mixing.video.genai.generate_video` fails and you want the full diagnostic),
opt in with:

```python
import logging

logging.basicConfig(level=logging.INFO)  # or level=logging.ERROR for errors only
```

## Requirements

- **ffmpeg** on PATH (most operations). macOS: `brew install ffmpeg`;
  Debian/Ubuntu: `sudo apt install ffmpeg`; Windows: download from
  [ffmpeg.org](https://ffmpeg.org/download.html) and add `bin` to PATH.
- **ElevenLabs API key** for `mixing.transcript` / `mixing.dubbing`
  (`ELEVENLABS_API_KEY`, or pass `api_key=`). Responses are cached on disk, so
  re-runs are free and offline.
- **Google Cloud** auth for AI video generation
  (`mixing.video.genai`, Vertex AI Veo).

### Optional extras

```bash
pip install mixing[media]      # moviepy / opencv / scipy / pydub: mixing.audio + mixing.video
pip install mixing[audio]      # ffmpeg-python / soundfile (on top of media)
pip install mixing[widget]     # interactive Jupyter audio widget
pip install mixing[gen]        # Google Vertex AI Veo generation
pip install mixing[llm]        # aix — chapter titling + SRT translation
pip install mixing[clipboard]  # get file paths from the clipboard
pip install mixing[beats]      # librosa — beat/downbeat/onset analysis
pip install mixing[ml]         # torch + transformers — find_segments(strategy="audioset"): songs vs jingles vs talk
```

## What’s inside

| Subpackage           | Highlights                                                                                                                                                                                                                                                                                              |
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `mixing.audio`       | `Audio`, `fade_in/out`, `crop_audio`, `concatenate_audio`, `overlay_audio`, `loop_audio`, `duck_audio`, `find_audio_offset`, `find_segments`/`extract_segments` (incl. `strategy="audioset"`, extra `ml`), `text_before_segments`, `beat_grid`/`BeatGrid` (extra `beats`)                               |
| `mixing.video`       | `Video`, `crop_video`, `loop_video`, `change_speed`, `replace_audio`, `normalize_audio`, `overlay_ambient_bed`, `ken_burns_video`/`ken_burns_film`, `concatenate_videos` (+ transitions: `crossfade_transition`, `fade_through_black`, …), `make_thumbnail`, `write_subtitles_in_video`, `SOCIAL_SIZES` |
| `mixing.video.genai` | `generate_video` (Vertex AI Veo)                                                                                                                                                                                                                                                                        |
| `mixing.transcript`  | `transcribe`, `remove_fillers`, `srt_for_media`, `words_to_srt`/`words_to_prose`                                                                                                                                                                                                                        |
| `mixing.chapters`    | `detect_chapters`, `Chapter`                                                                                                                                                                                                                                                                            |
| `mixing.dubbing`     | `text_to_speech`, `list_voices`, `translate_srt`, `dub_video_from_srt`                                                                                                                                                                                                                                  |
| `mixing.srt`         | `Cue`, `parse_srt`, `dump_srt`, `seconds_to_srt_time`                                                                                                                                                                                                                                                   |

See the [skills]() for the full, example-driven API of each.

## License

MIT

<p class="epythet-aggregates">This documentation as a single file: <a href="mixing.md">mixing.md</a> (Markdown, for agents).</p>


# _autosummary/mixing.chapters.html.md

# mixing.chapters

Detect chapter markers from a transcript — platform-agnostic.

A *chapter* is a `(start_seconds, title)` pair marking a topic shift. This
module turns a transcript (ElevenLabs Scribe response/words, SRT text, or a
list of cues) into a list of [`Chapter`](_autosummary/mixing.chapters.html.md#mixing.chapters.Chapter) objects, with titles produced by
a pluggable LLM segmenter. The result is intentionally **target-neutral** —
formatting chapters into YouTube description timestamps, podcast PSC, or ID3
chapter frames is the job of a publication layer (e.g. the `yb` package).

The detector enforces the constraints common to chapter-aware players:
first chapter at `0:00`, a minimum spacing between chapters, and a minimum
count below which chapters are not worth showing (an empty list is returned so
callers can cleanly skip them — e.g. for a very short clip).

### Module Attributes

| [`SegmentFn`](_autosummary/mixing.chapters.html.md#mixing.chapters.SegmentFn)                     | str}].                                                                                                                                       |
|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| [`SECONDS_PER_CHAPTER_HEURISTIC`](_autosummary/mixing.chapters.html.md#mixing.chapters.SECONDS_PER_CHAPTER_HEURISTIC) | Heuristic spacing used to pick a default chapter count when `target_count` is not given: roughly one chapter per this many seconds of media. |

### Functions

| [`default_segment_fn`](_autosummary/mixing.chapters.html.md#mixing.chapters.default_segment_fn)(segments, target_count, \*)   | LLM-backed segmenter using `aix.chat`.      |
|---------------------------------------------------------------------------------------------------|---------------------------------------------|
| [`detect_chapters`](_autosummary/mixing.chapters.html.md#mixing.chapters.detect_chapters)(transcript, \*[, duration, ...]) | Detect chapter markers from a `transcript`. |

### Classes

| [`Chapter`](_autosummary/mixing.chapters.html.md#mixing.chapters.Chapter)(start, title)   | A chapter marker: a start time (seconds) and a short title.   |
|--------------------------------------------------------------------------|---------------------------------------------------------------|

### *class* mixing.chapters.Chapter(start, title)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

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

### mixing.chapters.SECONDS_PER_CHAPTER_HEURISTIC *= 90.0*

Heuristic spacing used to pick a default chapter count when `target_count`
is not given: roughly one chapter per this many seconds of media. ~90s (1.5
minutes) is a common chapter cadence for talks/tutorials.

### mixing.chapters.SegmentFn

str}].
`segments` is a list of `{"start", "end", "text"}` sentence-ish units.

* **Type:**
  A segmenter maps (segments, target_count) -> [{“start”
* **Type:**
  [*float*](https://docs.python.org/3/builtins/functions.html#float), ”title”

alias of `Callable`[[[`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`int`](https://docs.python.org/3/builtins/functions.html#int)], [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]

### mixing.chapters.default_segment_fn(segments, target_count, , model=None)

LLM-backed segmenter using `aix.chat`.

Presents the timestamped sentences and asks for `target_count` chapter
boundaries as a JSON array of `{"start", "title"}`. Pass your own
`segment_fn` to [`detect_chapters()`](_autosummary/mixing.chapters.html.md#mixing.chapters.detect_chapters) to avoid the `aix` dependency.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### mixing.chapters.detect_chapters(transcript, , duration=None, min_chapters=3, max_chapters=8, min_spacing=10.0, target_count=None, segment_fn=None, model=None)

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`](https://docs.python.org/3/builtins/functions.html#float) | [`None`](https://docs.python.org/3/builtins/constants.html#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`](https://docs.python.org/3/builtins/functions.html#int)) – Minimum chapters worth showing. If fewer survive the
    constraints, an **empty list** is returned.
  * **max_chapters** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Upper bound on chapter count.
  * **min_spacing** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Minimum seconds between consecutive chapters (players such
    as YouTube require >= 10s).
  * **target_count** ([`int`](https://docs.python.org/3/builtins/functions.html#int) | [`None`](https://docs.python.org/3/builtins/constants.html#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`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`int`](https://docs.python.org/3/builtins/functions.html#int)], [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]]) – Pluggable segmenter `(segments, target_count) -> [{start,
    title}]`. Defaults to [`default_segment_fn()`](_autosummary/mixing.chapters.html.md#mixing.chapters.default_segment_fn) (LLM-backed).
  * **model** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional LLM model override for the default segmenter.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Chapter`](_autosummary/mixing.chapters.html.md#mixing.chapters.Chapter)]
* **Returns:**
  A list of [`Chapter`](_autosummary/mixing.chapters.html.md#mixing.chapters.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).


# _autosummary/mixing.dubbing.html.md

# mixing.dubbing

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

The transcript side of [`mixing`](_autosummary/mixing.html.md#module-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:

> - [`text_to_speech()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.text_to_speech) / [`synthesize_to_file()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.synthesize_to_file) — ElevenLabs TTS.
> - [`list_voices()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.list_voices) / [`find_voice()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.find_voice) — discover account voices.
> - [`parse_srt()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.parse_srt) / [`dump_srt()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.dump_srt) / [`translate_srt()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.translate_srt) — SRT I/O
>   and LLM-backed translation.
> - `dub_video_from_srt()` — the end-to-end pipeline.

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`](_autosummary/mixing.html.md#module-mixing) facade, this subpackage
uses PEP 562 `__getattr__` so that the TTS / SRT building blocks
([`text_to_speech()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.text_to_speech), [`list_voices()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.list_voices), [`parse_srt()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.parse_srt),
[`Cue`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.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:

```pycon
>>> 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`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.default_cache_dir)()                                | Default on-disk cache for synthesized audio.                                                |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| [`text_to_speech`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.text_to_speech)(text, voice_id, \*[, api_key, ...]) | Synthesize `text` to speech with ElevenLabs and return audio bytes.                         |
| [`synthesize_to_file`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.synthesize_to_file)(text, voice_id, path, ...)      | Synthesize `text` and write the audio to `path`.                                            |
| [`list_voices`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.list_voices)(\*[, api_key, timeout])                | List the voices available on the account.                                                   |
| [`find_voice`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.find_voice)(query, \*[, api_key, voices])           | Return the first voice whose name or labels contain `query` (case-insensitive).             |
| [`search_shared_voices`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.search_shared_voices)(\*[, language, ...])          | Search ElevenLabs' public *shared voice library* (not just the account).                    |
| [`add_shared_voice`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.add_shared_voice)(public_owner_id, voice_id, ...)   | Add a shared-library voice to the account so it can be synthesized.                         |
| [`parse_srt`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.parse_srt)(srt_text)                                | Parse SRT text into a list of [`Cue`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.Cue) objects. |
| [`dump_srt`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.dump_srt)(cues)                                     | Serialize cues back to SRT text, renumbering from 1.                                        |
| [`srt_time_to_seconds`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.srt_time_to_seconds)(timestamp)                     | Parse an `HH:MM:SS,mmm` (or `.mmm`) SRT timestamp to seconds.                               |
| [`translate_srt`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.translate_srt)(srt, target_language, \*[, ...])     | Translate the cue text of an SRT to `target_language`, keeping timings.                     |
| [`default_translate_fn`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.default_translate_fn)(texts, target_language)       | LLM-backed translator (segment-count preserving) using `aix.chat`.                          |

### Classes

| [`Cue`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.Cue)(index, start, end, text)   | One SRT subtitle cue.   |
|---------------------------------------------------------------------------------|-------------------------|

### *class* mixing.dubbing.Cue(index, start, end, text)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One SRT subtitle cue.

#### index

1-based cue number.

* **Type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### start

Start time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### end

End time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### text

Cue text (may contain embedded newlines).

* **Type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### *property* duration *: [float](https://docs.python.org/3/builtins/functions.html#float)*

Cue duration in seconds (never negative).

### mixing.dubbing.add_shared_voice(public_owner_id, voice_id, name, , api_key=None, timeout=60.0)

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`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `public_owner_id` from [`search_shared_voices()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.search_shared_voices).
  * **voice_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The shared voice’s `voice_id`.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name to give the added voice on the account.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The account-local `voice_id` to pass to [`text_to_speech()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.text_to_speech).

### mixing.dubbing.default_cache_dir()

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`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)

### mixing.dubbing.default_translate_fn(texts, target_language, source_language=None, , model=None)

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`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### mixing.dubbing.dump_srt(cues)

Serialize cues back to SRT text, renumbering from 1.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.dubbing.find_voice(query, , api_key=None, voices=None)

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`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)

### mixing.dubbing.list_voices(, api_key=None, timeout=60.0)

List the voices available on the account.

* **Parameters:**
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Request timeout in seconds.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.

### mixing.dubbing.parse_srt(srt_text)

Parse SRT text into a list of [`Cue`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.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`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.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)

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()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.add_shared_voice), then synthesize with the returned id.

* **Parameters:**
  * **language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – ISO-639-1 language filter, e.g. `"fr"`.
  * **use_cases** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – e.g. `"advertisement"`, `"narrative_story"`.
  * **gender** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – `"male"` / `"female"` / `"neutral"`.
  * **category** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – e.g. `"professional"`, `"high_quality"`.
  * **sort** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – e.g. `"trending"`.
  * **page_size** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max results.
  * **extra_params** – Any other query params the endpoint accepts.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]
* **Returns:**
  A list of shared-voice dicts. Each has `voice_id` and
  `public_owner_id` (both needed by [`add_shared_voice()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.add_shared_voice)), plus
  `name`, `language`, `accent`, `gender`, `use_case`,
  `description`.

### mixing.dubbing.srt_time_to_seconds(timestamp)

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

```pycon
>>> srt_time_to_seconds('00:43:12,187')
2592.187
>>> srt_time_to_seconds('00:00:01.500')
1.5
```

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### mixing.dubbing.synthesize_to_file(text, voice_id, path, \*\*kwargs)

Synthesize `text` and write the audio to `path`.

Thin convenience wrapper over [`text_to_speech()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.text_to_speech). Extra keyword
arguments are forwarded unchanged.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.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)

Synthesize `text` to speech with ElevenLabs and return audio bytes.

* **Parameters:**
  * **text** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The text to speak.
  * **voice_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – ElevenLabs voice id (see [`list_voices()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.list_voices)).
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **model_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – TTS model id. Defaults to `eleven_multilingual_v2`.
  * **output_format** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#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`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]) – Override the default voice settings (stability,
    similarity_boost, style, use_speaker_boost). Merged over
    `DFLT_VOICE_SETTINGS`.
  * **language_code** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional ISO-639-1 hint (e.g. `"fr"`). Only some
    models honor it; ignored by others.
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Total request timeout in seconds.
  * **cache** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – `True` (default) → use [`default_cache_dir()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.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`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` and `cache` is enabled, force a re-call and
    overwrite the cached entry.
  * **return_cache_status** ([`bool`](https://docs.python.org/3/builtins/functions.html#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`](https://docs.python.org/3/builtins/stdtypes.html#bytes) | [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`bool`](https://docs.python.org/3/builtins/functions.html#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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.
  * [**HTTPError**](https://docs.python.org/3/library/urllib.error.html#urllib.error.HTTPError) – Request failed (4xx/5xx).

### mixing.dubbing.translate_srt(srt, target_language, , source_language=None, translate_fn=None)

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

* **Parameters:**
  * **srt** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue)]]) – SRT text or a list of [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue) objects.
  * **target_language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Human-readable target language (e.g. `"French"`).
  * **source_language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional source language hint.
  * **translate_fn** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)], [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]]) – A callable `(texts, target_language, source_language)
    -> list[str]` returning one translation per input text, in order.
    Defaults to [`default_translate_fn()`](_autosummary/mixing.dubbing.html.md#mixing.dubbing.default_translate_fn) (LLM-backed).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  Translated SRT text with the original cue timings.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The translator returned a different number of segments than
      it was given.

### Modules

| [`srt`](_autosummary/mixing.dubbing.srt.html.md#module-mixing.dubbing.srt)   | SRT translation for dubbing — built on the canonical [`mixing.srt`](_autosummary/mixing.srt.html.md#module-mixing.srt).   |
|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| [`tts`](_autosummary/mixing.dubbing.tts.html.md#module-mixing.dubbing.tts)   | ElevenLabs text-to-speech (TTS) HTTP client.                                                                                          |


# _autosummary/mixing.dubbing.srt.html.md

# mixing.dubbing.srt

SRT translation for dubbing — built on the canonical [`mixing.srt`](_autosummary/mixing.srt.html.md#module-mixing.srt).

SRT parsing/serialization and the [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue) model live in
[`mixing.srt`](_autosummary/mixing.srt.html.md#module-mixing.srt); this module re-exports them and adds the dubbing-specific
piece: translating cue *text* into another language while preserving timings.

Translation is pluggable: pass any `translate_fn` that maps a list of strings
to a list of the same length. The default uses an LLM via the `aix` package
(provider-agnostic) when it is importable.

### Module Attributes

| [`TranslateFn`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.TranslateFn)   | A translator maps (texts, target_language, source_language) -> texts.   |
|----------------------------------------------------------------|-------------------------------------------------------------------------|

### Functions

| [`parse_srt`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.parse_srt)(srt_text)                            | Parse SRT text into a list of [`Cue`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.Cue) objects.   |
|-------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|
| [`dump_srt`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.dump_srt)(cues)                                 | Serialize cues back to SRT text, renumbering from 1.                                          |
| [`srt_time_to_seconds`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.srt_time_to_seconds)(timestamp)                 | Parse an `HH:MM:SS,mmm` (or `.mmm`) SRT timestamp to seconds.                                 |
| [`translate_srt`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.translate_srt)(srt, target_language, \*[, ...]) | Translate the cue text of an SRT to `target_language`, keeping timings.                       |
| [`default_translate_fn`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.default_translate_fn)(texts, target_language)   | LLM-backed translator (segment-count preserving) using `aix.chat`.                            |

### Classes

| [`Cue`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.Cue)(index, start, end, text)   | One SRT subtitle cue.   |
|---------------------------------------------------------------------------------|-------------------------|

### *class* mixing.dubbing.srt.Cue(index, start, end, text)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One SRT subtitle cue.

#### index

1-based cue number.

* **Type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### start

Start time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### end

End time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### text

Cue text (may contain embedded newlines).

* **Type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### *property* duration *: [float](https://docs.python.org/3/builtins/functions.html#float)*

Cue duration in seconds (never negative).

### mixing.dubbing.srt.TranslateFn

A translator maps (texts, target_language, source_language) -> texts.

alias of `Callable`[[[`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), `str | None`], `list[str]`]

### mixing.dubbing.srt.default_translate_fn(texts, target_language, source_language=None, , model=None)

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`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### mixing.dubbing.srt.dump_srt(cues)

Serialize cues back to SRT text, renumbering from 1.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.dubbing.srt.parse_srt(srt_text)

Parse SRT text into a list of [`Cue`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.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`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue)]

### mixing.dubbing.srt.srt_time_to_seconds(timestamp)

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

```pycon
>>> srt_time_to_seconds('00:43:12,187')
2592.187
>>> srt_time_to_seconds('00:00:01.500')
1.5
```

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### mixing.dubbing.srt.translate_srt(srt, target_language, , source_language=None, translate_fn=None)

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

* **Parameters:**
  * **srt** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue)]]) – SRT text or a list of [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue) objects.
  * **target_language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Human-readable target language (e.g. `"French"`).
  * **source_language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional source language hint.
  * **translate_fn** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)], [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]]) – A callable `(texts, target_language, source_language)
    -> list[str]` returning one translation per input text, in order.
    Defaults to [`default_translate_fn()`](_autosummary/mixing.dubbing.srt.html.md#mixing.dubbing.srt.default_translate_fn) (LLM-backed).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  Translated SRT text with the original cue timings.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The translator returned a different number of segments than
      it was given.


# _autosummary/mixing.dubbing.tts.html.md

# mixing.dubbing.tts

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

Stdlib-only — no `requests` or `elevenlabs` SDK dependency, mirroring
[`mixing.transcript.scribe`](_autosummary/mixing.transcript.scribe.html.md#module-mixing.transcript.scribe). Synthesizes speech from text and lists the
voices available on the account.

The default model is `eleven_multilingual_v2` — ElevenLabs’ high-quality,
multilingual model (handles English, French, and ~30 other languages with the
same voice), which is the right default for dubbing into multiple languages.

An optional on-disk cache (enabled by default here, since the same line is
often re-synthesized across runs) skips the network call when the same text +
voice + model + settings have been synthesized before.

Quick start:

```pycon
>>> from mixing.dubbing import list_voices, synthesize_to_file
>>> voices = list_voices()
>>> synthesize_to_file("Hello world", voices[0]["voice_id"], "hello.mp3")
```

### Module Attributes

| [`DFLT_MODEL_ID`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.DFLT_MODEL_ID)       | High-quality, multilingual default model (one voice speaks many languages).                                                                           |
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`DFLT_OUTPUT_FORMAT`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.DFLT_OUTPUT_FORMAT)  | Highest standard MP3 quality that does not require a paid tier.                                                                                       |
| [`DFLT_VOICE_SETTINGS`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.DFLT_VOICE_SETTINGS) | Voice settings for dubbing fidelity — see [`DFLT_MODEL_ID`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.DFLT_MODEL_ID) on why this is not tuned for expressiveness. |

### Functions

| [`add_shared_voice`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.add_shared_voice)(public_owner_id, voice_id, ...)   | Add a shared-library voice to the account so it can be synthesized.             |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`default_cache_dir`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.default_cache_dir)()                                | Default on-disk cache for synthesized audio.                                    |
| [`find_voice`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.find_voice)(query, \*[, api_key, voices])           | Return the first voice whose name or labels contain `query` (case-insensitive). |
| [`list_voices`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.list_voices)(\*[, api_key, timeout])                | List the voices available on the account.                                       |
| [`search_shared_voices`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.search_shared_voices)(\*[, language, ...])          | Search ElevenLabs' public *shared voice library* (not just the account).        |
| [`synthesize_to_file`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.synthesize_to_file)(text, voice_id, path, ...)      | Synthesize `text` and write the audio to `path`.                                |
| [`text_to_speech`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.text_to_speech)(text, voice_id, \*[, api_key, ...]) | Synthesize `text` to speech with ElevenLabs and return audio bytes.             |

### mixing.dubbing.tts.DFLT_MODEL_ID *= 'eleven_multilingual_v2'*

High-quality, multilingual default model (one voice speaks many languages).

**This is a DUBBING default, and deliberately not an expressive one.** A dub
has to track a performance that already exists: the timing is fixed by the
source, the delivery belongs to the original actor, and per-take variance is
a defect rather than life. `eleven_multilingual_v2` is steady and speaks
many languages in one voice, which is what that job wants.

**If you are writing NARRATION or COMMENTARY, this is the wrong tool.** Use
`braidio`, which owns expressive delivery and defaults to `eleven_v3`
so inline `[audio tags]` fire — the single biggest lever on whether a read
sounds alive (measured: plain text 70.8 Hz of pitch range, densely tagged
124.4 Hz). v2 cannot render those tags at all, so a narration built on this
default is capped at the flat end before a word is written.

### mixing.dubbing.tts.DFLT_OUTPUT_FORMAT *= 'mp3_44100_128'*

Highest standard MP3 quality that does not require a paid tier.

### mixing.dubbing.tts.DFLT_VOICE_SETTINGS *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [Any](https://docs.python.org/3/library/typing.html#typing.Any)]* *= {'similarity_boost': 0.8, 'stability': 0.5, 'style': 0.0, 'use_speaker_boost': True}*

Voice settings for dubbing fidelity — see [`DFLT_MODEL_ID`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.DFLT_MODEL_ID) on why this
is not tuned for expressiveness. `stability` ~0.5 keeps the read steady;
`similarity_boost` ~0.8 keeps timbre faithful to the chosen voice.

### mixing.dubbing.tts.add_shared_voice(public_owner_id, voice_id, name, , api_key=None, timeout=60.0)

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`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `public_owner_id` from [`search_shared_voices()`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.search_shared_voices).
  * **voice_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The shared voice’s `voice_id`.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name to give the added voice on the account.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The account-local `voice_id` to pass to [`text_to_speech()`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.text_to_speech).

### mixing.dubbing.tts.default_cache_dir()

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`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)

### mixing.dubbing.tts.find_voice(query, , api_key=None, voices=None)

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`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)

### mixing.dubbing.tts.list_voices(, api_key=None, timeout=60.0)

List the voices available on the account.

* **Parameters:**
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Request timeout in seconds.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.

### mixing.dubbing.tts.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)

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()`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.add_shared_voice), then synthesize with the returned id.

* **Parameters:**
  * **language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – ISO-639-1 language filter, e.g. `"fr"`.
  * **use_cases** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – e.g. `"advertisement"`, `"narrative_story"`.
  * **gender** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – `"male"` / `"female"` / `"neutral"`.
  * **category** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – e.g. `"professional"`, `"high_quality"`.
  * **sort** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – e.g. `"trending"`.
  * **page_size** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max results.
  * **extra_params** – Any other query params the endpoint accepts.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]
* **Returns:**
  A list of shared-voice dicts. Each has `voice_id` and
  `public_owner_id` (both needed by [`add_shared_voice()`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.add_shared_voice)), plus
  `name`, `language`, `accent`, `gender`, `use_case`,
  `description`.

### mixing.dubbing.tts.synthesize_to_file(text, voice_id, path, \*\*kwargs)

Synthesize `text` and write the audio to `path`.

Thin convenience wrapper over [`text_to_speech()`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.text_to_speech). Extra keyword
arguments are forwarded unchanged.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)
* **Returns:**
  The path written.

### mixing.dubbing.tts.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)

Synthesize `text` to speech with ElevenLabs and return audio bytes.

* **Parameters:**
  * **text** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The text to speak.
  * **voice_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – ElevenLabs voice id (see [`list_voices()`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.list_voices)).
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **model_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – TTS model id. Defaults to `eleven_multilingual_v2`.
  * **output_format** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#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`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]) – Override the default voice settings (stability,
    similarity_boost, style, use_speaker_boost). Merged over
    [`DFLT_VOICE_SETTINGS`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.DFLT_VOICE_SETTINGS).
  * **language_code** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional ISO-639-1 hint (e.g. `"fr"`). Only some
    models honor it; ignored by others.
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Total request timeout in seconds.
  * **cache** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – `True` (default) → use [`default_cache_dir()`](_autosummary/mixing.dubbing.tts.html.md#mixing.dubbing.tts.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`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` and `cache` is enabled, force a re-call and
    overwrite the cached entry.
  * **return_cache_status** ([`bool`](https://docs.python.org/3/builtins/functions.html#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`](https://docs.python.org/3/builtins/stdtypes.html#bytes) | [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`bool`](https://docs.python.org/3/builtins/functions.html#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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.
  * [**HTTPError**](https://docs.python.org/3/library/urllib.error.html#urllib.error.HTTPError) – Request failed (4xx/5xx).


# _autosummary/mixing.egress.html.md

# mixing.egress

Canonical *egress*: route a produced result to its destination.

Across `mixing`, every result-producing function takes a single `output`
parameter whose **role** is constant — “what to do with the result” — while its
**type** is open:

| `output` value   | behavior                                                                                                 |
|------------------|----------------------------------------------------------------------------------------------------------|
| `None`           | return the in-memory result (object producers) or<br/>save next to the input (file producers); see below |
| a **file path**  | write the result there; return the `Path`                                                                |
| a **directory**  | write with an auto-derived name; return the `Path`                                                       |
| a **callable**   | return `output(result)` — the general escape hatch                                                       |

There are two flavors of producer, modeled by two helpers:

- [`deliver()`](_autosummary/mixing.egress.html.md#mixing.egress.deliver) — *object-first*: `output=None` returns the in-memory
  object (e.g. an `Audio`). Used by editing functions that
  build a value you may want to keep chaining.
- [`write_egress()`](_autosummary/mixing.egress.html.md#mixing.egress.write_egress) — *file-first*: `output=None` saves to a default path
  (typically beside the input) and returns it. Used by file→file operations
  whose whole point is to produce a file.

Both accept the same `output` forms, so callers learn one mental model.
`Output` is the shared type alias.

### Module Attributes

| [`Output`](_autosummary/mixing.egress.html.md#mixing.egress.Output)   | nothing, a filesystem path/dir, or a result-consuming sink.   |
|-----------------------------------------------------------|---------------------------------------------------------------|

### Functions

| [`is_path_output`](_autosummary/mixing.egress.html.md#mixing.egress.is_path_output)(output)                           | True if `output` denotes a filesystem path (`str` / `os.PathLike`).       |
|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
| [`is_sink`](_autosummary/mixing.egress.html.md#mixing.egress.is_sink)(output)                                  | True if `output` is a result-consuming callable (not a path).             |
| [`resolve_output_path`](_autosummary/mixing.egress.html.md#mixing.egress.resolve_output_path)(output, \*, default_name)    | Resolve a path/dir `output` to a concrete file `Path` (parents created).  |
| [`deliver`](_autosummary/mixing.egress.html.md#mixing.egress.deliver)(result, output, \*, write, default_name) | Object-first egress: route an in-memory `result` to `output`.             |
| [`write_egress`](_autosummary/mixing.egress.html.md#mixing.egress.write_egress)(output, \*, default_path, write)    | File-first egress: for operations whose primary effect is writing a file. |

### mixing.egress.Output

nothing, a filesystem path/dir, or a result-consuming sink.

* **Type:**
  An egress target

alias of [`None`](https://docs.python.org/3/builtins/constants.html#None) | [`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | `os.PathLike[str]` | `Callable`[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]

### mixing.egress.deliver(result, output, , write, default_name)

Object-first egress: route an in-memory `result` to `output`.

- `None` → return `result` unchanged (no I/O).
- sink callable → return `output(result)`.
- path/dir → `write(result, path)` then return the `Path`.

* **Parameters:**
  * **result** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – the produced in-memory value (e.g. an `Audio`).
  * **output** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`None`](https://docs.python.org/3/builtins/constants.html#None), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]) – the egress target (see module docstring).
  * **write** ([`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – `write(result, path)` persists `result` to `path`.
  * **default_name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – filename to use when `output` is a directory.
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)

### mixing.egress.is_path_output(output)

True if `output` denotes a filesystem path (`str` / `os.PathLike`).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### mixing.egress.is_sink(output)

True if `output` is a result-consuming callable (not a path).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### mixing.egress.resolve_output_path(output, , default_name)

Resolve a path/dir `output` to a concrete file `Path` (parents created).

A directory `output` (existing, or with a trailing separator) is joined
with `default_name`. Only call this when `output` is a path — not
`None` and not a sink.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)

### mixing.egress.write_egress(output, , default_path, write)

File-first egress: for operations whose primary effect is writing a file.

- `None` → write to `default_path` (e.g. beside the input) and return it.
- sink callable → write to `default_path`, then return `output(path)`.
- path/dir → write to that file (dir → `default_path`’s name inside it).

* **Parameters:**
  * **output** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`None`](https://docs.python.org/3/builtins/constants.html#None), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]) – the egress target (see module docstring).
  * **default_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – where to write when `output` is `None` / a sink, and
    the source of the auto filename when `output` is a directory.
  * **write** ([`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – `write(path)` performs the actual encode to `path`.
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)


# _autosummary/mixing.errors.html.md

# mixing.errors

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

Pure stdlib, imported by the lazy facade without pulling any backend, so a consumer
can `from mixing import WindowTooWideForClip` in a light module and still catch what
a heavy one raises.

The convention: an error class carries the MEASURED numbers as attributes, not only in
its message, so a caller can act on them (retry at the largest valid window, say) rather
than parse prose.

### Exceptions

| [`MixingError`](_autosummary/mixing.errors.html.md#mixing.errors.MixingError)                               | Base for every error `mixing` raises on its own behalf.                |
|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| [`WindowTooWideForClip`](_autosummary/mixing.errors.html.md#mixing.errors.WindowTooWideForClip)(\*, clip_index, ...) | An explicit analysis window leaves a clip no second, independent look. |

### *exception* mixing.errors.MixingError

Bases: [`Exception`](https://docs.python.org/3/builtins/exceptions.html#Exception)

Base for every error `mixing` raises on its own behalf.

### *exception* mixing.errors.WindowTooWideForClip(, clip_index, clip_duration_s, window_s, hop_s, max_window_s)

Bases: [`MixingError`](_autosummary/mixing.errors.html.md#mixing.errors.MixingError), [`ValueError`](https://docs.python.org/3/builtins/exceptions.html#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`](_autosummary/mixing.errors.html.md#mixing.errors.WindowTooWideForClip.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`](https://docs.python.org/3/builtins/exceptions.html#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.


# _autosummary/mixing.html.md

# 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`](_autosummary/mixing.transcript.html.md#module-mixing.transcript) — speech-to-text (ElevenLabs Scribe, stdlib HTTP),
  filler removal, transcript formats (no heavy deps).
- [`mixing.dubbing`](_autosummary/mixing.dubbing.html.md#module-mixing.dubbing) — text-to-speech re-voicing / translation from SRT.
- [`mixing.srt`](_autosummary/mixing.srt.html.md#module-mixing.srt) — canonical SRT/timeline parsing & formatting (pure).
- [`mixing.chapters`](_autosummary/mixing.chapters.html.md#module-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`](_autosummary/mixing.html.md#mixing.detect_chapters)(transcript, \*[, duration, ...])   | Detect chapter markers from a `transcript`.                                                 |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| [`dump_srt`](_autosummary/mixing.html.md#mixing.dump_srt)(cues)                                     | Serialize cues back to SRT text, renumbering from 1.                                        |
| [`has_ffmpeg`](_autosummary/mixing.html.md#mixing.has_ffmpeg)()                                       | Return True if the `ffmpeg` binary is available on `PATH`.                                  |
| [`parse_srt`](_autosummary/mixing.html.md#mixing.parse_srt)(srt_text)                                | Parse SRT text into a list of [`Cue`](_autosummary/mixing.html.md#mixing.Cue) objects. |
| [`remove_fillers`](_autosummary/mixing.html.md#mixing.remove_fillers)(input_media, output_dir, \*[, ...]) | End-to-end filler removal.                                                                  |
| [`seconds_to_srt_time`](_autosummary/mixing.html.md#mixing.seconds_to_srt_time)(seconds)                       | Format a time in seconds as an SRT timestamp `HH:MM:SS,mmm`.                                |
| [`srt_for_media`](_autosummary/mixing.html.md#mixing.srt_for_media)(media, \*[, srt_path, reuse, ...])   | Return `(srt_text, srt_path)` for `media`, transcribing if needed.                          |
| [`srt_time_to_seconds`](_autosummary/mixing.html.md#mixing.srt_time_to_seconds)(timestamp)                     | Parse an `HH:MM:SS,mmm` (or `.mmm`) SRT timestamp to seconds.                               |
| [`text_to_speech`](_autosummary/mixing.html.md#mixing.text_to_speech)(text, voice_id, \*[, api_key, ...]) | Synthesize `text` to speech with ElevenLabs and return audio bytes.                         |
| [`transcribe`](_autosummary/mixing.html.md#mixing.transcribe)(audio, \*[, api_key, model_id, ...])    | Transcribe `audio` with ElevenLabs Scribe.                                                  |
| [`translate_srt`](_autosummary/mixing.html.md#mixing.translate_srt)(srt, target_language, \*[, ...])     | Translate the cue text of an SRT to `target_language`, keeping timings.                     |

### Classes

| [`Chapter`](_autosummary/mixing.html.md#mixing.Chapter)(start, title)                          | A chapter marker: a start time (seconds) and a short title.                                              |
|-------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| [`Cue`](_autosummary/mixing.html.md#mixing.Cue)(index, start, end, text)                   | One SRT subtitle cue.                                                                                    |
| [`FillerRemovalResult`](_autosummary/mixing.html.md#mixing.FillerRemovalResult)(cleaned_media, ...[, ...]) | Paths and computed ranges produced by [`remove_fillers()`](_autosummary/mixing.html.md#mixing.remove_fillers). |

### Exceptions

| [`MixingError`](_autosummary/mixing.html.md#mixing.MixingError)                               | Base for every error `mixing` raises on its own behalf.                |
|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| [`WindowTooWideForClip`](_autosummary/mixing.html.md#mixing.WindowTooWideForClip)(\*, clip_index, ...) | An explicit analysis window leaves a clip no second, independent look. |

### *class* mixing.Chapter(start, title)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

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

### *class* mixing.Cue(index, start, end, text)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One SRT subtitle cue.

#### index

1-based cue number.

* **Type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### start

Start time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### end

End time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### text

Cue text (may contain embedded newlines).

* **Type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### *property* duration *: [float](https://docs.python.org/3/builtins/functions.html#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())

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Paths and computed ranges produced by [`remove_fillers()`](_autosummary/mixing.html.md#mixing.remove_fillers).

### *exception* mixing.MixingError

Bases: [`Exception`](https://docs.python.org/3/builtins/exceptions.html#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)

Bases: [`MixingError`](_autosummary/mixing.errors.html.md#mixing.errors.MixingError), [`ValueError`](https://docs.python.org/3/builtins/exceptions.html#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`](_autosummary/mixing.html.md#mixing.WindowTooWideForClip.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`](https://docs.python.org/3/builtins/exceptions.html#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)

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`](https://docs.python.org/3/builtins/functions.html#float) | [`None`](https://docs.python.org/3/builtins/constants.html#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`](https://docs.python.org/3/builtins/functions.html#int)) – Minimum chapters worth showing. If fewer survive the
    constraints, an **empty list** is returned.
  * **max_chapters** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Upper bound on chapter count.
  * **min_spacing** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Minimum seconds between consecutive chapters (players such
    as YouTube require >= 10s).
  * **target_count** ([`int`](https://docs.python.org/3/builtins/functions.html#int) | [`None`](https://docs.python.org/3/builtins/constants.html#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`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`int`](https://docs.python.org/3/builtins/functions.html#int)], [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]]]) – Pluggable segmenter `(segments, target_count) -> [{start,
    title}]`. Defaults to `default_segment_fn()` (LLM-backed).
  * **model** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional LLM model override for the default segmenter.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Chapter`](_autosummary/mixing.chapters.html.md#mixing.chapters.Chapter)]
* **Returns:**
  A list of [`Chapter`](_autosummary/mixing.html.md#mixing.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)

Serialize cues back to SRT text, renumbering from 1.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.has_ffmpeg()

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.

```pycon
>>> isinstance(has_ffmpeg(), bool)
True
```

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### mixing.parse_srt(srt_text)

Parse SRT text into a list of [`Cue`](_autosummary/mixing.html.md#mixing.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`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.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)

End-to-end filler removal.

* **Parameters:**
  * **input_media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Video or audio file to clean.
  * **output_dir** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Directory to write transcripts and intermediate files into.
    Created if missing.
  * **output_media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Output cleaned media path. Defaults to
    `output_dir / f"{stem}.cleaned.mp4"`.
  * **audio_events** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default filler / event sets.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – ElevenLabs API key (else env `ELEVENLABS_API_KEY`).
  * **scribe_kwargs** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`object`](https://docs.python.org/3/builtins/functions.html#object)]]) – Extra kwargs forwarded to [`transcribe()`](_autosummary/mixing.html.md#mixing.transcribe).
  * **apply_keeps_kwargs** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`object`](https://docs.python.org/3/builtins/functions.html#object)]]) – Extra kwargs forwarded to `apply_keeps()`.
  * **keep_intermediate** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, delete the extracted audio file.
  * **scribe_data** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – If provided, skip the network call and use this dict
    (must match Scribe’s response shape). Useful for tests / replays.
* **Return type:**
  [`FillerRemovalResult`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.FillerRemovalResult)
* **Returns:**
  A [`FillerRemovalResult`](_autosummary/mixing.html.md#mixing.FillerRemovalResult) with file paths and the computed
  cut/keep ranges.

### mixing.seconds_to_srt_time(seconds)

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.

```pycon
>>> 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`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.srt_for_media(media, , srt_path=None, reuse=True, refresh=False, cache=True, max_chars=80, \*\*transcribe_kwargs)

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

* **Parameters:**
  * **media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Audio or video file (Scribe extracts audio from video).
  * **srt_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Where the SRT lives/should be written. Defaults to the media
    path with an `.srt` suffix.
  * **reuse** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` (default) and the SRT already exists, read and
    return it without re-transcribing.
  * **refresh** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Force re-transcription even if the SRT exists (overwrites it).
  * **cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Pass-through to [`mixing.transcript.transcribe()`](_autosummary/mixing.transcript.html.md#mixing.transcript.transcribe)’s on-disk
    response cache.
  * **max_chars** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max characters per SRT cue when generating.
  * **transcribe_kwargs** – Extra args forwarded to `transcribe` (e.g.
    `language_code`, `diarize`).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]
* **Returns:**
  The SRT text and the path it lives at.

### mixing.srt_time_to_seconds(timestamp)

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

```pycon
>>> srt_time_to_seconds('00:43:12,187')
2592.187
>>> srt_time_to_seconds('00:00:01.500')
1.5
```

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#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)

Synthesize `text` to speech with ElevenLabs and return audio bytes.

* **Parameters:**
  * **text** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The text to speak.
  * **voice_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – ElevenLabs voice id (see `list_voices()`).
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **model_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – TTS model id. Defaults to `eleven_multilingual_v2`.
  * **output_format** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#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`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]) – Override the default voice settings (stability,
    similarity_boost, style, use_speaker_boost). Merged over
    `DFLT_VOICE_SETTINGS`.
  * **language_code** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional ISO-639-1 hint (e.g. `"fr"`). Only some
    models honor it; ignored by others.
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Total request timeout in seconds.
  * **cache** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.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`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` and `cache` is enabled, force a re-call and
    overwrite the cached entry.
  * **return_cache_status** ([`bool`](https://docs.python.org/3/builtins/functions.html#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`](https://docs.python.org/3/builtins/stdtypes.html#bytes) | [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`bool`](https://docs.python.org/3/builtins/functions.html#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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.
  * [**HTTPError**](https://docs.python.org/3/library/urllib.error.html#urllib.error.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)

Transcribe `audio` with ElevenLabs Scribe.

* **Parameters:**
  * **audio** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – Path to an audio/video file, or raw bytes.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **model_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Scribe model id (currently `scribe_v1`).
  * **timestamps_granularity** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `"word"` (default), `"character"`, or `"none"`.
  * **tag_audio_events** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to surface `(laughs)` / `(coughs)` etc.
  * **diarize** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Speaker diarization on/off.
  * **language_code** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional BCP-47 hint to skip language detection.
  * **extra_fields** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Additional multipart fields to send (forward compatible).
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Total request timeout in seconds.
  * **cache** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.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`](https://docs.python.org/3/builtins/functions.html#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`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.
  * [**HTTPError**](https://docs.python.org/3/library/urllib.error.html#urllib.error.HTTPError) – Request failed (4xx/5xx).

### mixing.translate_srt(srt, target_language, , source_language=None, translate_fn=None)

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

* **Parameters:**
  * **srt** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue)]]) – SRT text or a list of [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue) objects.
  * **target_language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Human-readable target language (e.g. `"French"`).
  * **source_language** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional source language hint.
  * **translate_fn** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)], [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#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`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  Translated SRT text with the original cue timings.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – The translator returned a different number of segments than
      it was given.

### Modules

| [`chapters`](_autosummary/mixing.chapters.html.md#module-mixing.chapters)     | Detect chapter markers from a transcript — platform-agnostic.                      |
|--------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
| [`dubbing`](_autosummary/mixing.dubbing.html.md#module-mixing.dubbing)       | Text-to-speech dubbing: re-voice or translate a video from its SRT.                |
| [`egress`](_autosummary/mixing.egress.html.md#module-mixing.egress)         | Canonical *egress*: route a produced result to its destination.                    |
| [`errors`](_autosummary/mixing.errors.html.md#module-mixing.errors)         | Typed errors `mixing` raises — the home for exceptions a caller may want to catch. |
| [`srt`](_autosummary/mixing.srt.html.md#module-mixing.srt)               | Canonical SRT / timeline parsing and formatting (pure, dependency-free).           |
| [`transcript`](_autosummary/mixing.transcript.html.md#module-mixing.transcript) | Transcript-driven media editing.                                                   |
| [`util`](_autosummary/mixing.util.html.md#module-mixing.util)             | General utilities for mixing video, audio, etc.                                    |


# _autosummary/mixing.srt.html.md

# mixing.srt

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

This is the single source of truth for SubRip (`.srt`) time handling in
`mixing`. Before consolidation the same logic was reimplemented in three
places (`dubbing.srt`, `video.video_subtitles`, `transcript.formats`)
with subtly different rounding and tolerance; those modules now re-export from
here.

The vocabulary:

- A **timestamp** is the `HH:MM:SS,mmm` string SRT uses; [`seconds_to_srt_time()`](_autosummary/mixing.srt.html.md#mixing.srt.seconds_to_srt_time)
  and [`srt_time_to_seconds()`](_autosummary/mixing.srt.html.md#mixing.srt.srt_time_to_seconds) convert between it and seconds (a `float`).
- A [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue) is one subtitle block: a 1-based `index`, `start`/`end`
  in seconds, and `text` (which may contain embedded newlines).

Times are always seconds (`float`) everywhere except the timestamp string.

### Module Attributes

| [`TIME_RE`](_autosummary/mixing.srt.html.md#mixing.srt.TIME_RE)               | Matches an SRT cue time line, tolerant of `,` or `.` as the ms separator.   |
|------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [`fmt_srt_time`](_autosummary/mixing.srt.html.md#mixing.srt.fmt_srt_time)(seconds) | Back-compat aliases — historically these names lived in different modules.  |

### Functions

| [`srt_time_to_seconds`](_autosummary/mixing.srt.html.md#mixing.srt.srt_time_to_seconds)(timestamp)                  | Parse an `HH:MM:SS,mmm` (or `.mmm`) SRT timestamp to seconds.                               |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| [`seconds_to_srt_time`](_autosummary/mixing.srt.html.md#mixing.srt.seconds_to_srt_time)(seconds)                    | Format a time in seconds as an SRT timestamp `HH:MM:SS,mmm`.                                |
| [`fmt_srt_time`](_autosummary/mixing.srt.html.md#mixing.srt.fmt_srt_time)(seconds)                           | Back-compat aliases — historically these names lived in different modules.                  |
| [`to_srt_time`](_autosummary/mixing.srt.html.md#mixing.srt.to_srt_time)(seconds)                            | Format a time in seconds as an SRT timestamp `HH:MM:SS,mmm`.                                |
| [`parse_srt`](_autosummary/mixing.srt.html.md#mixing.srt.parse_srt)(srt_text)                             | Parse SRT text into a list of [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue) objects. |
| [`dump_srt`](_autosummary/mixing.srt.html.md#mixing.srt.dump_srt)(cues)                                  | Serialize cues back to SRT text, renumbering from 1.                                        |
| [`shift_srt_timestamps`](_autosummary/mixing.srt.html.md#mixing.srt.shift_srt_timestamps)(srt_text[, shift_seconds]) | Shift every cue time line in `srt_text` by `shift_seconds`.                                 |
| [`parse_vtt_words`](_autosummary/mixing.srt.html.md#mixing.srt.parse_vtt_words)(vtt_text)                       | Parse a WebVTT caption file into a word list shaped like Scribe's.                          |

### Classes

| [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue)(index, start, end, text)   | One SRT subtitle cue.   |
|---------------------------------------------------------------------------------|-------------------------|

### *class* mixing.srt.Cue(index, start, end, text)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One SRT subtitle cue.

#### index

1-based cue number.

* **Type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### start

Start time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### end

End time in seconds.

* **Type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

#### text

Cue text (may contain embedded newlines).

* **Type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### *property* duration *: [float](https://docs.python.org/3/builtins/functions.html#float)*

Cue duration in seconds (never negative).

### mixing.srt.TIME_RE *= re.compile('(\\\\d{1,2}):(\\\\d{2}):(\\\\d{2})[,.](\\\\d{1,3})\\\\s\*-->\\\\s\*(\\\\d{1,2}):(\\\\d{2}):(\\\\d{2})[,.](\\\\d{1,3})')*

Matches an SRT cue time line, tolerant of `,` or `.` as the ms separator.

### mixing.srt.dump_srt(cues)

Serialize cues back to SRT text, renumbering from 1.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.srt.fmt_srt_time(seconds)

Back-compat aliases — historically these names lived in different modules.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.srt.parse_srt(srt_text)

Parse SRT text into a list of [`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.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`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Cue`](_autosummary/mixing.srt.html.md#mixing.srt.Cue)]

### mixing.srt.parse_vtt_words(vtt_text)

Parse a WebVTT caption file into a word list shaped like Scribe’s.

Each entry is `{"text", "start", "end", "type": "word"}` (seconds), the same
shape [`mixing.transcript.transcribe()`](_autosummary/mixing.transcript.html.md#mixing.transcript.transcribe) returns under `"words"`, so the
result plugs into the word-level helpers (`words_to_srt`,
`mixing.audio.segmentation.text_before_segments()`, …).

YouTube auto-captions carry per-word timings as inline `<hh:mm:ss.mmm>`
tags; text before a line’s first tag takes the cue start (or, on a later
line of the same cue, the latest timing seen), and the rolling duplicate lines
(no inline timings) are ignored. A file with no inline timings falls back to
one entry per cue (its whole text). A word ends where the next one starts
(capped at its cue end). Tags are stripped and HTML entities unescaped.

```pycon
>>> vtt = '''WEBVTT
...
... 00:00:12.240 --> 00:00:15.509 align:start position:0%
... Music<00:00:12.639><c> with</c><00:00:12.800><c> meaning</c>
...
... 00:00:15.509 --> 00:00:15.519 align:start position:0%
... Music with meaning
... '''
>>> [(w["text"], w["start"]) for w in parse_vtt_words(vtt)]
[('Music', 12.24), ('with', 12.639), ('meaning', 12.8)]
>>> parse_vtt_words("1\n00:01.000 --> 00:02.500\nRock &amp; roll\n\nNOTE hi\n")
[{'text': 'Rock & roll', 'start': 1.0, 'end': 2.5, 'type': 'word'}]
```

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### mixing.srt.seconds_to_srt_time(seconds)

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.

```pycon
>>> 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`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.srt.shift_srt_timestamps(srt_text, shift_seconds=0.0)

Shift every cue time line in `srt_text` by `shift_seconds`.

Negative values shift earlier; resulting negative times clamp to zero.

```pycon
>>> srt = '1\n00:43:12,187 --> 00:43:13,817\nHello world'
>>> '00:00:00,187 --> 00:00:01,817' in shift_srt_timestamps(srt, -2592)
True
```

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.srt.srt_time_to_seconds(timestamp)

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

```pycon
>>> srt_time_to_seconds('00:43:12,187')
2592.187
>>> srt_time_to_seconds('00:00:01.500')
1.5
```

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### mixing.srt.to_srt_time(seconds)

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.

```pycon
>>> 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`](https://docs.python.org/3/builtins/stdtypes.html#str)


# _autosummary/mixing.transcript.fillers.html.md

# mixing.transcript.fillers

Filler-word detection over Scribe-shaped word lists.

A “word list” here is the `words` array in the ElevenLabs Scribe JSON
response — each entry has `text`, `start`, `end`, and `type`
(one of `"word"`, `"spacing"`, `"audio_event"`).

The two main outputs are:

- [`build_cuts()`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.build_cuts): time ranges to REMOVE (fillers + optional audio events).
- [`keeps_from_cuts()`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.keeps_from_cuts): complementary KEEP ranges, suitable for ffmpeg.

### Module Attributes

| [`DEFAULT_FILLER_TOKENS`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.DEFAULT_FILLER_TOKENS)       | Default filler tokens (lowercased, alpha-only, see [`normalize_token()`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.normalize_token)).   |
|------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
| [`DEFAULT_AUDIO_EVENTS_TO_CUT`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.DEFAULT_AUDIO_EVENTS_TO_CUT) | Default audio-event tags to remove.                                                                                       |

### Functions

| [`build_cuts`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.build_cuts)(words, \*[, fillers, ...])        | Compute time ranges to REMOVE.                                       |
|-----------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
| [`is_filler`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.is_filler)(item, \*[, fillers, audio_events]) | Return `True` if `item` is a filler word or a removable audio event. |
| [`keeps_from_cuts`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.keeps_from_cuts)(cuts, duration)              | Return ranges to KEEP given the cut ranges and total duration.       |
| [`normalize_token`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.normalize_token)(text)                        | Lowercase and strip non-alpha so `"Uh,"` -> `"uh"`.                  |

### mixing.transcript.fillers.DEFAULT_AUDIO_EVENTS_TO_CUT *: [frozenset](https://docs.python.org/3/builtins/stdtypes.html#frozenset)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)]* *= frozenset({'(coughs)'})*

Default audio-event tags to remove. `(laughs)` is intentionally kept.

### mixing.transcript.fillers.DEFAULT_FILLER_TOKENS *: [frozenset](https://docs.python.org/3/builtins/stdtypes.html#frozenset)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)]* *= frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'})*

Default filler tokens (lowercased, alpha-only, see [`normalize_token()`](_autosummary/mixing.transcript.fillers.html.md#mixing.transcript.fillers.normalize_token)).

### mixing.transcript.fillers.build_cuts(words, , fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}), absorb_trailing_space=True, merge_gap=0.08)

Compute time ranges to REMOVE.

* **Parameters:**
  * **words** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Scribe `words` array.
  * **fillers** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default filler set.
  * **audio_events** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default audio-event set.
  * **absorb_trailing_space** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Extend each cut to the end of the trailing
    `"spacing"` token, which avoids leaving a stranded pause.
  * **merge_gap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Merge adjacent cuts when their gap is below this many seconds.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  List of `{"start": float, "end": float, "label": str}` dicts,
  sorted by start time and non-overlapping.

### mixing.transcript.fillers.is_filler(item, , fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}))

Return `True` if `item` is a filler word or a removable audio event.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### mixing.transcript.fillers.keeps_from_cuts(cuts, duration)

Return ranges to KEEP given the cut ranges and total duration.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### mixing.transcript.fillers.normalize_token(text)

Lowercase and strip non-alpha so `"Uh,"` -> `"uh"`.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)


# _autosummary/mixing.transcript.formats.html.md

# mixing.transcript.formats

Transcript output formats: SRT, plain prose, time remapping.

### Functions

| [`remap_time_after_cuts`](_autosummary/mixing.transcript.formats.html.md#mixing.transcript.formats.remap_time_after_cuts)(t, cuts)                    | Map `t` from the original timeline onto the post-cut timeline.                  |
|----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`words_to_prose`](_autosummary/mixing.transcript.formats.html.md#mixing.transcript.formats.words_to_prose)(words, \*[, paragraph_pause, ...]) | Render a Scribe word list as plain prose, with paragraph breaks on long pauses. |
| [`words_to_srt`](_autosummary/mixing.transcript.formats.html.md#mixing.transcript.formats.words_to_srt)(words, \*[, max_chars, ...])         | Render an SRT from a Scribe word list (no filler removal, no remapping).        |
| [`words_to_srt_remapped`](_autosummary/mixing.transcript.formats.html.md#mixing.transcript.formats.words_to_srt_remapped)(words, cuts, \*[, ...])     | SRT aligned to a post-cut timeline.                                             |

### mixing.transcript.formats.remap_time_after_cuts(t, cuts)

Map `t` from the original timeline onto the post-cut timeline.

If `t` falls inside a cut, snaps to the moment that cut would have
started in the post-cut timeline.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### mixing.transcript.formats.words_to_prose(words, , paragraph_pause=1.2, drop_fillers=False, fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}))

Render a Scribe word list as plain prose, with paragraph breaks on long pauses.

* **Parameters:**
  * **words** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Scribe `words` array.
  * **paragraph_pause** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Insert a blank line when the gap between two
    consecutive non-filler words exceeds this many seconds.
  * **drop_fillers** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, omit filler words and removable audio events.
  * **audio_events** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default filler / event sets.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.transcript.formats.words_to_srt(words, , max_chars=80, sentence_endings='.?!')

Render an SRT from a Scribe word list (no filler removal, no remapping).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.transcript.formats.words_to_srt_remapped(words, cuts, , max_chars=80, sentence_endings='.?!', drop_fillers=True, fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}))

SRT aligned to a post-cut timeline.

Each word’s timestamp is shifted earlier by the cumulative duration
of all cuts that ended before it, so the resulting SRT drops in over
the cleaned media.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)


# _autosummary/mixing.transcript.html.md

# mixing.transcript

Transcript-driven media editing.

Speech-to-text via ElevenLabs Scribe, filler-word detection, and
timeline-aware cuts of audio/video. Produces editable transcripts
(plain prose, SRT, word-level JSON) and applies cuts via ffmpeg.

The HTTP client uses stdlib only (no `requests` or `elevenlabs` SDK
dependency), so the package adds no required deps. The runtime needs
ffmpeg on PATH for media operations, and an ElevenLabs API key (env
var `ELEVENLABS_API_KEY` or explicit `api_key=`) for transcription.

Quick start:

```pycon
>>> from mixing.transcript import remove_fillers
>>> result = remove_fillers("input.mov", "out/")
>>> print(result.cleaned_media)
```

Lower-level building blocks:

```pycon
>>> from mixing.transcript import (
...     transcribe, build_cuts, keeps_from_cuts,
...     words_to_srt, words_to_prose, apply_keeps,
... )
```

### Functions

| [`transcribe`](_autosummary/mixing.transcript.html.md#mixing.transcript.transcribe)(audio, \*[, api_key, model_id, ...])    | Transcribe `audio` with ElevenLabs Scribe.                                      |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`default_cache_dir`](_autosummary/mixing.transcript.html.md#mixing.transcript.default_cache_dir)()                                | Default on-disk cache for Scribe responses.                                     |
| [`is_filler`](_autosummary/mixing.transcript.html.md#mixing.transcript.is_filler)(item, \*[, fillers, audio_events])       | Return `True` if `item` is a filler word or a removable audio event.            |
| [`build_cuts`](_autosummary/mixing.transcript.html.md#mixing.transcript.build_cuts)(words, \*[, fillers, ...])              | Compute time ranges to REMOVE.                                                  |
| [`keeps_from_cuts`](_autosummary/mixing.transcript.html.md#mixing.transcript.keeps_from_cuts)(cuts, duration)                    | Return ranges to KEEP given the cut ranges and total duration.                  |
| [`normalize_token`](_autosummary/mixing.transcript.html.md#mixing.transcript.normalize_token)(text)                              | Lowercase and strip non-alpha so `"Uh,"` -> `"uh"`.                             |
| [`fmt_srt_time`](_autosummary/mixing.transcript.html.md#mixing.transcript.fmt_srt_time)(seconds)                              | Back-compat aliases — historically these names lived in different modules.      |
| [`words_to_srt`](_autosummary/mixing.transcript.html.md#mixing.transcript.words_to_srt)(words, \*[, max_chars, ...])          | Render an SRT from a Scribe word list (no filler removal, no remapping).        |
| [`words_to_srt_remapped`](_autosummary/mixing.transcript.html.md#mixing.transcript.words_to_srt_remapped)(words, cuts, \*[, ...])      | SRT aligned to a post-cut timeline.                                             |
| [`words_to_prose`](_autosummary/mixing.transcript.html.md#mixing.transcript.words_to_prose)(words, \*[, paragraph_pause, ...])  | Render a Scribe word list as plain prose, with paragraph breaks on long pauses. |
| [`remap_time_after_cuts`](_autosummary/mixing.transcript.html.md#mixing.transcript.remap_time_after_cuts)(t, cuts)                     | Map `t` from the original timeline onto the post-cut timeline.                  |
| [`extract_audio`](_autosummary/mixing.transcript.html.md#mixing.transcript.extract_audio)(input_path, output_path, \*[, ...])  | Extract a mono mp3 from `input_path` (suitable for STT).                        |
| [`apply_keeps`](_autosummary/mixing.transcript.html.md#mixing.transcript.apply_keeps)(input_path, output_path, keeps, \*)    | Re-encode `input_path` keeping only the listed time ranges.                     |
| [`srt_for_media`](_autosummary/mixing.transcript.html.md#mixing.transcript.srt_for_media)(media, \*[, srt_path, reuse, ...])   | Return `(srt_text, srt_path)` for `media`, transcribing if needed.              |
| [`remove_fillers`](_autosummary/mixing.transcript.html.md#mixing.transcript.remove_fillers)(input_media, output_dir, \*[, ...]) | End-to-end filler removal.                                                      |

### Classes

| [`FillerRemovalResult`](_autosummary/mixing.transcript.html.md#mixing.transcript.FillerRemovalResult)(cleaned_media, ...[, ...])   | Paths and computed ranges produced by [`remove_fillers()`](_autosummary/mixing.transcript.html.md#mixing.transcript.remove_fillers).   |
|---------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|

### *class* mixing.transcript.FillerRemovalResult(cleaned_media, transcript_md, transcript_srt, cleaned_md, cleaned_srt, scribe_json, cuts_json, keeps_json, duration, cuts=list(), keeps=list())

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Paths and computed ranges produced by [`remove_fillers()`](_autosummary/mixing.transcript.html.md#mixing.transcript.remove_fillers).

### mixing.transcript.apply_keeps(input_path, output_path, keeps, , video_codec='libx264', audio_codec='aac', crf=20, preset='medium', audio_bitrate='160k', faststart=True, overwrite=True)

Re-encode `input_path` keeping only the listed time ranges.

* **Parameters:**
  * **input_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Input audio/video file.
  * **output_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Output file (extension picks the container).
  * **keeps** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – `[{"start": float, "end": float}, ...]` time ranges in seconds.
  * **audio_bitrate** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Re-encode
    knobs forwarded to ffmpeg.
  * **faststart** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Add `-movflags +faststart` (mp4 web-streaming).
  * **overwrite** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Pass `-y` to ffmpeg.
* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)
* **Returns:**
  The output path.

### mixing.transcript.build_cuts(words, , fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}), absorb_trailing_space=True, merge_gap=0.08)

Compute time ranges to REMOVE.

* **Parameters:**
  * **words** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Scribe `words` array.
  * **fillers** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default filler set.
  * **audio_events** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default audio-event set.
  * **absorb_trailing_space** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Extend each cut to the end of the trailing
    `"spacing"` token, which avoids leaving a stranded pause.
  * **merge_gap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Merge adjacent cuts when their gap is below this many seconds.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  List of `{"start": float, "end": float, "label": str}` dicts,
  sorted by start time and non-overlapping.

### mixing.transcript.default_cache_dir()

Default on-disk cache for Scribe responses.

Honors `$MIXING_TRANSCRIPT_CACHE_DIR`, then `$XDG_CACHE_HOME`,
then `~/.cache/`. Final segment is always `mixing/transcript`.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)

### mixing.transcript.extract_audio(input_path, output_path, , sample_rate=16000, channels=1, bitrate='96k', overwrite=True)

Extract a mono mp3 from `input_path` (suitable for STT).

Returns the output path.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)

### mixing.transcript.fmt_srt_time(seconds)

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.

```pycon
>>> 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`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.transcript.is_filler(item, , fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}))

Return `True` if `item` is a filler word or a removable audio event.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### mixing.transcript.keeps_from_cuts(cuts, duration)

Return ranges to KEEP given the cut ranges and total duration.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### mixing.transcript.normalize_token(text)

Lowercase and strip non-alpha so `"Uh,"` -> `"uh"`.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.transcript.remap_time_after_cuts(t, cuts)

Map `t` from the original timeline onto the post-cut timeline.

If `t` falls inside a cut, snaps to the moment that cut would have
started in the post-cut timeline.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### mixing.transcript.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)

End-to-end filler removal.

* **Parameters:**
  * **input_media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Video or audio file to clean.
  * **output_dir** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Directory to write transcripts and intermediate files into.
    Created if missing.
  * **output_media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Output cleaned media path. Defaults to
    `output_dir / f"{stem}.cleaned.mp4"`.
  * **audio_events** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default filler / event sets.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – ElevenLabs API key (else env `ELEVENLABS_API_KEY`).
  * **scribe_kwargs** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`object`](https://docs.python.org/3/builtins/functions.html#object)]]) – Extra kwargs forwarded to [`transcribe()`](_autosummary/mixing.transcript.html.md#mixing.transcript.transcribe).
  * **apply_keeps_kwargs** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`object`](https://docs.python.org/3/builtins/functions.html#object)]]) – Extra kwargs forwarded to [`apply_keeps()`](_autosummary/mixing.transcript.html.md#mixing.transcript.apply_keeps).
  * **keep_intermediate** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, delete the extracted audio file.
  * **scribe_data** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – If provided, skip the network call and use this dict
    (must match Scribe’s response shape). Useful for tests / replays.
* **Return type:**
  [`FillerRemovalResult`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.FillerRemovalResult)
* **Returns:**
  A [`FillerRemovalResult`](_autosummary/mixing.transcript.html.md#mixing.transcript.FillerRemovalResult) with file paths and the computed
  cut/keep ranges.

### mixing.transcript.srt_for_media(media, , srt_path=None, reuse=True, refresh=False, cache=True, max_chars=80, \*\*transcribe_kwargs)

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

* **Parameters:**
  * **media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Audio or video file (Scribe extracts audio from video).
  * **srt_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Where the SRT lives/should be written. Defaults to the media
    path with an `.srt` suffix.
  * **reuse** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` (default) and the SRT already exists, read and
    return it without re-transcribing.
  * **refresh** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Force re-transcription even if the SRT exists (overwrites it).
  * **cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Pass-through to [`mixing.transcript.transcribe()`](_autosummary/mixing.transcript.html.md#mixing.transcript.transcribe)’s on-disk
    response cache.
  * **max_chars** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max characters per SRT cue when generating.
  * **transcribe_kwargs** – Extra args forwarded to `transcribe` (e.g.
    `language_code`, `diarize`).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]
* **Returns:**
  The SRT text and the path it lives at.

### mixing.transcript.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)

Transcribe `audio` with ElevenLabs Scribe.

* **Parameters:**
  * **audio** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – Path to an audio/video file, or raw bytes.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **model_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Scribe model id (currently `scribe_v1`).
  * **timestamps_granularity** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `"word"` (default), `"character"`, or `"none"`.
  * **tag_audio_events** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to surface `(laughs)` / `(coughs)` etc.
  * **diarize** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Speaker diarization on/off.
  * **language_code** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional BCP-47 hint to skip language detection.
  * **extra_fields** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Additional multipart fields to send (forward compatible).
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Total request timeout in seconds.
  * **cache** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – `False` (default) → no cache. `True` → use
    [`default_cache_dir()`](_autosummary/mixing.transcript.html.md#mixing.transcript.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`](https://docs.python.org/3/builtins/functions.html#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`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.
  * [**HTTPError**](https://docs.python.org/3/library/urllib.error.html#urllib.error.HTTPError) – Request failed (4xx/5xx).

### mixing.transcript.words_to_prose(words, , paragraph_pause=1.2, drop_fillers=False, fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}))

Render a Scribe word list as plain prose, with paragraph breaks on long pauses.

* **Parameters:**
  * **words** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Scribe `words` array.
  * **paragraph_pause** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Insert a blank line when the gap between two
    consecutive non-filler words exceeds this many seconds.
  * **drop_fillers** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, omit filler words and removable audio events.
  * **audio_events** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default filler / event sets.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.transcript.words_to_srt(words, , max_chars=80, sentence_endings='.?!')

Render an SRT from a Scribe word list (no filler removal, no remapping).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.transcript.words_to_srt_remapped(words, cuts, , max_chars=80, sentence_endings='.?!', drop_fillers=True, fillers=frozenset({'ah', 'eh', 'er', 'erm', 'hmm', 'mhm', 'mm', 'mmm', 'uh', 'uhh', 'uhm', 'um', 'umm'}), audio_events=frozenset({'(coughs)'}))

SRT aligned to a post-cut timeline.

Each word’s timestamp is shifted earlier by the cumulative duration
of all cuts that ended before it, so the resulting SRT drops in over
the cleaned media.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### Modules

| [`fillers`](_autosummary/mixing.transcript.fillers.html.md#module-mixing.transcript.fillers)   | Filler-word detection over Scribe-shaped word lists.         |
|---------------------------------------------------------------------------------------------|--------------------------------------------------------------|
| [`formats`](_autosummary/mixing.transcript.formats.html.md#module-mixing.transcript.formats)   | Transcript output formats: SRT, plain prose, time remapping. |
| [`media`](_autosummary/mixing.transcript.media.html.md#module-mixing.transcript.media)       | ffmpeg-backed audio extraction and timeline-cut application. |
| [`persist`](_autosummary/mixing.transcript.persist.html.md#module-mixing.transcript.persist)   | Reuse-or-create the SRT that lives next to a media file.     |
| [`pipeline`](_autosummary/mixing.transcript.pipeline.html.md#module-mixing.transcript.pipeline) | High-level filler-removal pipeline.                          |
| [`scribe`](_autosummary/mixing.transcript.scribe.html.md#module-mixing.transcript.scribe)     | ElevenLabs Scribe (speech-to-text) HTTP client.              |


# _autosummary/mixing.transcript.media.html.md

# mixing.transcript.media

ffmpeg-backed audio extraction and timeline-cut application.

These functions shell out to `ffmpeg` (which must be on PATH); they
have no Python dependencies beyond the stdlib.

### Functions

| [`apply_keeps`](_autosummary/mixing.transcript.media.html.md#mixing.transcript.media.apply_keeps)(input_path, output_path, keeps, \*)   | Re-encode `input_path` keeping only the listed time ranges.   |
|----------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
| [`extract_audio`](_autosummary/mixing.transcript.media.html.md#mixing.transcript.media.extract_audio)(input_path, output_path, \*[, ...]) | Extract a mono mp3 from `input_path` (suitable for STT).      |

### mixing.transcript.media.apply_keeps(input_path, output_path, keeps, , video_codec='libx264', audio_codec='aac', crf=20, preset='medium', audio_bitrate='160k', faststart=True, overwrite=True)

Re-encode `input_path` keeping only the listed time ranges.

* **Parameters:**
  * **input_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Input audio/video file.
  * **output_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Output file (extension picks the container).
  * **keeps** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – `[{"start": float, "end": float}, ...]` time ranges in seconds.
  * **audio_bitrate** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Re-encode
    knobs forwarded to ffmpeg.
  * **faststart** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Add `-movflags +faststart` (mp4 web-streaming).
  * **overwrite** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Pass `-y` to ffmpeg.
* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)
* **Returns:**
  The output path.

### mixing.transcript.media.extract_audio(input_path, output_path, , sample_rate=16000, channels=1, bitrate='96k', overwrite=True)

Extract a mono mp3 from `input_path` (suitable for STT).

Returns the output path.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)


# _autosummary/mixing.transcript.persist.html.md

# mixing.transcript.persist

Reuse-or-create the SRT that lives next to a media file.

Encodes the “transcribe once, persist, reuse” rule: the SRT lives alongside
the media (same folder, same basename, `.srt`). If it already exists it is
returned untouched — re-transcribing costs API credits and the file may have
been hand-corrected. Otherwise it is generated via ElevenLabs Scribe (whose
raw response is itself cached on disk) and written next to the media.

### Functions

| [`srt_for_media`](_autosummary/mixing.transcript.persist.html.md#mixing.transcript.persist.srt_for_media)(media, \*[, srt_path, reuse, ...])   | Return `(srt_text, srt_path)` for `media`, transcribing if needed.   |
|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|

### mixing.transcript.persist.srt_for_media(media, , srt_path=None, reuse=True, refresh=False, cache=True, max_chars=80, \*\*transcribe_kwargs)

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

* **Parameters:**
  * **media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Audio or video file (Scribe extracts audio from video).
  * **srt_path** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Where the SRT lives/should be written. Defaults to the media
    path with an `.srt` suffix.
  * **reuse** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` (default) and the SRT already exists, read and
    return it without re-transcribing.
  * **refresh** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Force re-transcription even if the SRT exists (overwrites it).
  * **cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Pass-through to [`mixing.transcript.transcribe()`](_autosummary/mixing.transcript.html.md#mixing.transcript.transcribe)’s on-disk
    response cache.
  * **max_chars** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max characters per SRT cue when generating.
  * **transcribe_kwargs** – Extra args forwarded to `transcribe` (e.g.
    `language_code`, `diarize`).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]
* **Returns:**
  The SRT text and the path it lives at.


# _autosummary/mixing.transcript.pipeline.html.md

# mixing.transcript.pipeline

High-level filler-removal pipeline.

Orchestrates: extract audio -> Scribe transcribe -> filler detection ->
ffmpeg cut -> write transcript outputs.

### Functions

| [`remove_fillers`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.remove_fillers)(input_media, output_dir, \*[, ...])   | End-to-end filler removal.   |
|-------------------------------------------------------------------------------------------------------|------------------------------|

### Classes

| [`FillerRemovalResult`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.FillerRemovalResult)(cleaned_media, ...[, ...])   | Paths and computed ranges produced by [`remove_fillers()`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.remove_fillers).   |
|---------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|

### *class* mixing.transcript.pipeline.FillerRemovalResult(cleaned_media, transcript_md, transcript_srt, cleaned_md, cleaned_srt, scribe_json, cuts_json, keeps_json, duration, cuts=list(), keeps=list())

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Paths and computed ranges produced by [`remove_fillers()`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.remove_fillers).

### mixing.transcript.pipeline.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)

End-to-end filler removal.

* **Parameters:**
  * **input_media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Video or audio file to clean.
  * **output_dir** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – Directory to write transcripts and intermediate files into.
    Created if missing.
  * **output_media** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Output cleaned media path. Defaults to
    `output_dir / f"{stem}.cleaned.mp4"`.
  * **audio_events** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Override the default filler / event sets.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – ElevenLabs API key (else env `ELEVENLABS_API_KEY`).
  * **scribe_kwargs** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`object`](https://docs.python.org/3/builtins/functions.html#object)]]) – Extra kwargs forwarded to `transcribe()`.
  * **apply_keeps_kwargs** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`object`](https://docs.python.org/3/builtins/functions.html#object)]]) – Extra kwargs forwarded to `apply_keeps()`.
  * **keep_intermediate** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, delete the extracted audio file.
  * **scribe_data** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – If provided, skip the network call and use this dict
    (must match Scribe’s response shape). Useful for tests / replays.
* **Return type:**
  [`FillerRemovalResult`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.FillerRemovalResult)
* **Returns:**
  A [`FillerRemovalResult`](_autosummary/mixing.transcript.pipeline.html.md#mixing.transcript.pipeline.FillerRemovalResult) with file paths and the computed
  cut/keep ranges.


# _autosummary/mixing.transcript.scribe.html.md

# mixing.transcript.scribe

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

Stdlib-only — no `requests` or `elevenlabs` SDK dependency, so this
module adds nothing to the project’s required deps. Returns the raw
JSON response which includes word-level timestamps (with per-word
`confidence`) when `timestamps_granularity="word"` (the default).

Optional on-disk cache: pass `cache=True` (default location) or
`cache=<path>` to skip a re-call when the same audio + params have
been transcribed before.

### Functions

| [`default_cache_dir`](_autosummary/mixing.transcript.scribe.html.md#mixing.transcript.scribe.default_cache_dir)()                             | Default on-disk cache for Scribe responses.   |
|--------------------------------------------------------------------------------------------------|-----------------------------------------------|
| [`transcribe`](_autosummary/mixing.transcript.scribe.html.md#mixing.transcript.scribe.transcribe)(audio, \*[, api_key, model_id, ...]) | Transcribe `audio` with ElevenLabs Scribe.    |

### mixing.transcript.scribe.default_cache_dir()

Default on-disk cache for Scribe responses.

Honors `$MIXING_TRANSCRIPT_CACHE_DIR`, then `$XDG_CACHE_HOME`,
then `~/.cache/`. Final segment is always `mixing/transcript`.

* **Return type:**
  [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)

### mixing.transcript.scribe.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)

Transcribe `audio` with ElevenLabs Scribe.

* **Parameters:**
  * **audio** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – Path to an audio/video file, or raw bytes.
  * **api_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – API key. Falls back to env var `ELEVENLABS_API_KEY`.
  * **model_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Scribe model id (currently `scribe_v1`).
  * **timestamps_granularity** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `"word"` (default), `"character"`, or `"none"`.
  * **tag_audio_events** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to surface `(laughs)` / `(coughs)` etc.
  * **diarize** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Speaker diarization on/off.
  * **language_code** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional BCP-47 hint to skip language detection.
  * **extra_fields** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Additional multipart fields to send (forward compatible).
  * **timeout** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Total request timeout in seconds.
  * **cache** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]) – `False` (default) → no cache. `True` → use
    [`default_cache_dir()`](_autosummary/mixing.transcript.scribe.html.md#mixing.transcript.scribe.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`](https://docs.python.org/3/builtins/functions.html#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`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.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**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – No API key supplied and `ELEVENLABS_API_KEY` unset.
  * [**HTTPError**](https://docs.python.org/3/library/urllib.error.html#urllib.error.HTTPError) – Request failed (4xx/5xx).


# _autosummary/mixing.util.html.md

# mixing.util

General utilities for mixing video, audio, etc.

### Functions

| [`copy_to_clipboard`](_autosummary/mixing.util.html.md#mixing.util.copy_to_clipboard)(data)                     | Copy data to system clipboard.                                            |
|----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
| [`ffmpeg_exe`](_autosummary/mixing.util.html.md#mixing.util.ffmpeg_exe)()                                | Resolve an ffmpeg executable, preferring the pip-bundled binary.          |
| [`get_path_from_clipboard`](_autosummary/mixing.util.html.md#mixing.util.get_path_from_clipboard)()                   | Get file path from clipboard and validate it.                             |
| [`has_ffmpeg`](_autosummary/mixing.util.html.md#mixing.util.has_ffmpeg)()                                | Return True if the `ffmpeg` binary is available on `PATH`.                |
| [`require_media_extra`](_autosummary/mixing.util.html.md#mixing.util.require_media_extra)(feature, \*[, modules]) | Raise an informative `ImportError` if the `media` extra is not installed. |
| [`require_ml_extra`](_autosummary/mixing.util.html.md#mixing.util.require_ml_extra)(feature, \*[, modules])    | Raise an informative `ImportError` if the `ml` extra is not installed.    |
| [`require_package`](_autosummary/mixing.util.html.md#mixing.util.require_package)(package_name)               | Import a package, raising an informative error if not installed.          |
| [`to_seconds`](_autosummary/mixing.util.html.md#mixing.util.to_seconds)(value, \*, unit, rate)           | Convert time value to seconds based on unit.                              |

### mixing.util.copy_to_clipboard(data)

Copy data to system clipboard.

* **Parameters:**
  **data** ([`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes) | [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Bytes (for binary data like images) or string to copy
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### mixing.util.ffmpeg_exe()

Resolve an ffmpeg executable, preferring the pip-bundled binary.

Resolution order is `imageio-ffmpeg` (which itself honors the
`IMAGEIO_FFMPEG_EXE` env var, then its bundled static binary, then the
system `PATH`) and finally a bare `PATH` lookup. This is what lets
subprocess-ffmpeg features work on a machine where only `pip install`
ran — `imageio-ffmpeg` is a moviepy dependency, so it is present
wherever mixing’s video stack is.

* **Raises:**
  [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – when no ffmpeg executable can be found, naming both
      remedies (installing `imageio-ffmpeg` or a system ffmpeg).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

```pycon
>>> isinstance(ffmpeg_exe(), str)
True
```

### mixing.util.get_path_from_clipboard()

Get file path from clipboard and validate it.

Intelligently extracts file paths from various clipboard contents including:

- Direct file paths
- File paths within error messages or tracebacks
- Quoted paths

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### mixing.util.has_ffmpeg()

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.

```pycon
>>> isinstance(has_ffmpeg(), bool)
True
```

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### mixing.util.require_media_extra(feature, , modules=('numpy', 'pydub', 'scipy', 'moviepy', 'cv2', 'PIL', 'burns', 'looks'))

Raise an informative `ImportError` if the `media` extra is not installed.

The base `pip install mixing` ships only the pure modules (`srt`,
`chapters`, `errors`, `util`, `egress`, `transcript`, TTS); the
moviepy / opencv / scipy / pydub backends come with `mixing[media]`.

```pycon
>>> require_media_extra("demo", modules=("math",))
>>> require_media_extra("demo", modules=("no_such_module_xyz",))
Traceback (most recent call last):
  ...
ImportError: demo requires the 'media' extra ...
```

### mixing.util.require_ml_extra(feature, , modules=('torch', 'transformers'))

Raise an informative `ImportError` if the `ml` extra is not installed.

`torch` / `transformers` (the pretrained AudioSet tagger) come with
`mixing[ml]`; nothing imports them until a learned feature is used.

```pycon
>>> require_ml_extra("demo", modules=("math",))
>>> require_ml_extra("demo", modules=("no_such_module_xyz",))
Traceback (most recent call last):
  ...
ImportError: demo requires the 'ml' extra ...
```

### mixing.util.require_package(package_name)

Import a package, raising an informative error if not installed.

Packages supplied by the `media` or `ml` extra get a hint naming that extra.

```pycon
>>> math = require_package('math')
>>> math.pi
3.141592653589793
```

### mixing.util.to_seconds(value, , unit, rate)

Convert time value to seconds based on unit.

* **Parameters:**
  * **value** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Time value to convert
  * **unit** ([`Union`](https://docs.python.org/3/library/typing.html#typing.Union)[[`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)[`'seconds'`, `'frames'`, `'milliseconds'`], [`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)[`'seconds'`, `'samples'`, `'milliseconds'`]]) – Unit of the value (‘seconds’, ‘frames’, ‘milliseconds’, ‘samples’)
  * **rate** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Frame rate (fps) for ‘frames’ or sample rate (Hz) for ‘samples’
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  Time in seconds

### Examples

```pycon
>>> to_seconds(10, unit="seconds", rate=24)
10.0
>>> to_seconds(240, unit="frames", rate=24)
10.0
>>> to_seconds(10000, unit="milliseconds", rate=24)
10.0
```


# about-this-build.html.md

<!-- generated by epythet -->

# About this build

This documentation was built on **2026-10-18 02:54 UTC** from commit <a href="https://github.com/reeleehq/mixing/commit/715b89c7383ef3e4b199fd731a9fc5a7f388afb7"><code>715b89c</code></a> on branch <code>main</code>, for **mixing 0.0.57** (from <code>pyproject.toml</code>).

#### NOTE
Nothing suggests a mismatch: the tree was clean at the commit above, and the documented version is the one on PyPI.

## Source

|                     |                                                                                                                                                        |
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
| Commit              | <a href="https://github.com/reeleehq/mixing/commit/715b89c7383ef3e4b199fd731a9fc5a7f388afb7"><code>715b89c7383ef3e4b199fd731a9fc5a7f388afb7</code></a> |
| Branch              | <code>main</code>                                                                                                                                      |
| Tags at this commit | <code>0.0.57</code>                                                                                                                                    |
| Working tree        | clean                                                                                                                                                  |
| Remote              | <code>https://github.com/reeleehq/mixing</code>                                                                                                        |

## Continuous integration

|              |                                                                                            |
|--------------|--------------------------------------------------------------------------------------------|
| Repository   | <code>reeleehq/mixing</code>                                                               |
| Run          | <a href="https://github.com/reeleehq/mixing/actions/runs/39091289767">39091289767</a>      |
| Ref          | <code>refs/heads/main</code>                                                               |
| Event commit | <code>ee8b37c52622fc78f328289788bf84ea4c53deac</code> (in the history of the built commit) |

## Tools

|          |         |
|----------|---------|
| epythet  | 0.2.17  |
| Sphinx   | 9.1.0   |
| docutils | 0.22.4  |
| Python   | 3.12.15 |

## Configuration as resolved

|               |                                                                   |
|---------------|-------------------------------------------------------------------|
| theme         | <code>auto</code> (Sphinx theme <code>sphinxawesome_theme</code>) |
| accent        | <code>#8d325a</code>                                              |
| api_generator | <code>autosummary</code>                                          |
| ignore        | <code>tests/</code>, <code>scrap/</code>, <code>examples/</code>  |
| agent_outputs | <code>true</code>                                                 |
| aggregates    | <code>md</code>                                                   |
| ai_artifacts  | <code>true</code>                                                 |

## Package on PyPI

Latest release: <a href="https://pypi.org/project/mixing/0.0.57/">0.0.57</a>, the same as the documented version.

## Reproduce

```bash
git clone https://github.com/reeleehq/mixing && cd mixing
git checkout 715b89c7383ef3e4b199fd731a9fc5a7f388afb7
pip install "epythet==0.2.17"
epythet quickstart . --ignore tests/ scrap/ examples/
```

The same data, for machines: <a href="build_info.json"><code>build_info.json</code></a> (schema version 1).


# ai-agents.html.md

<!-- generated by epythet -->

# For AI agents

`mixing` ships artifacts for coding agents alongside its code. This page lists
them, says where each lives in the repository, and points at the
machine-readable copies of this documentation.

## Skills

Skills are folders holding a `SKILL.md` (the [Agent Skills](https://agentskills.io) format): a description that tells an agent when to use it and a body with the procedure. Install one into your agent with `gh skill` (any host: `--agent claude-code`, `copilot`, `cursor`, `codex`, `gemini`), or use the copy bundled in the wheel.

### `mixing`

Use the `mixing` Python package for video and audio editing tasks: slicing audio/video, fades, cropping, looping, changing speed, replacing or mixing a video’s audio, laying a looping ambient bed under a cut, normalizing levels, Ken Burns pan/zoom, concatenating clips, thumbnails, burning in subtitles, speech-to-text + filler removal, chapter detection, and TTS dubbing/translation. Trigger whenever someone wants to edit, transform, transcribe, dub, or assemble audio/video files with Python — e.g. “trim this clip”, “add music to a video”, “remove the ums”, “make a thumbnail”, “turn this audio into a podcast”, “dub this in French”, “add room tone under the whole cut”. Start here to pick the right tool and the right sub-skill (mixing-audio / mixing-video / mixing-transcript / mixing-dubbing).

Source: [`.claude/skills/mixing`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/skills/mixing).

### `mixing-audio`

Use for AUDIO editing with the `mixing` package: trim/crop a clip, fade in or fade out, join/concatenate clips (with optional crossfade), mix music under a voice or overlay one track on another, loop a short bed to a target length, duck a bed under dialogue, normalize loudness, convert to mono, resample, peek at raw samples, align two recordings of the same performance, or split a long recording (concert, DJ mix, radio show, podcast) into segments. Trigger on phrasings like “trim audio”, “fade out the end”, “join these audio clips”, “add background music under the narration”, “make it louder / normalize”, “convert to mono”, “align two recordings”, “find the offset between these takes”, “split a concert into songs”, “separate speech from music”, “find the songs in this radio show / tape”, “songs vs jingles vs DJ talk”, “what did the host say before each song”, “loop this ambient clip to N seconds”, “duck the music under the dialogue”. For video audio (replace/normalize a video’s track) use mixing-video; this skill is audio-file → audio-file.

Source: [`.claude/skills/mixing-audio`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/skills/mixing-audio).

### `mixing-dubbing`

Use when re-voicing or translating audio/video with text-to-speech via the `mixing` package: turning text into spoken audio, synthesizing narration, translating subtitles, or producing a foreign-language dub of a video from its SRT. Trigger on requests like “dub this video”, “re-voice this in another language”, “text to speech / TTS this”, “synthesize narration”, “translate these subtitles”, “make a French version of this clip”, or “read this script in voice X”. For transcribing speech → SRT in the first place, see mixing-transcript; for the `output` protocol and ffmpeg, see the mixing router skill.

Source: [`.claude/skills/mixing-dubbing`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/skills/mixing-dubbing).

### `mixing-transcript`

Transcribe speech to text, remove filler words (ums/uhs), build SRT subtitles or clean prose, and detect chapter markers — using the `mixing` package’s `transcript` and `chapters` modules (ElevenLabs Scribe under the hood). Trigger whenever someone wants to “transcribe this audio/video”, “get an SRT/captions”, “remove the ums and uhs”, “clean up the fillers”, “make chapters/timestamps”, “detect topic shifts”, or “get word-level timestamps” from a media file. For re-voicing/translation use mixing-dubbing; for plain audio/video edits use mixing-audio / mixing-video. Read the `mixing` router skill first for the `output` egress protocol and ffmpeg/key setup.

Source: [`.claude/skills/mixing-transcript`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/skills/mixing-transcript).

### `mixing-video`

Use for VIDEO editing with the `mixing` Python package: trim/crop a clip, loop it, speed it up or down, replace or mix in a new audio track, normalize audio levels, pan/zoom a still image (Ken Burns), build a film from panels, concatenate clips, grab/extract frames, make a YouTube-style thumbnail, burn in subtitles, or resize for shorts/tiktok/square. Trigger on phrasings like “trim this video”, “loop the intro”, “speed up / slow down this clip”, “replace the audio with this music”, “lay room tone / ambience under the cut”, “add a pan-zoom over this photo”, “make a thumbnail”, “burn the subtitles in”, “resize for YouTube Shorts”, or “stitch these clips together”. For audio-only work use mixing-audio; for transcription / filler removal use mixing-transcript; for TTS re-voicing use mixing-dubbing.

Source: [`.claude/skills/mixing-video`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/skills/mixing-video).

## Subagents

Subagents are Markdown files with a frontmatter (`name`, `description`, `tools`) and a system prompt as the body. Copy one into your project’s `.claude/agents/` (or your agent host’s equivalent) to delegate the task it describes.

### `mixing-dev`

Works ON the `mixing` codebase itself — adding or changing functions, refactoring, fixing bugs, extending a subpackage, or reviewing a change in the mixing repo. Knows the architecture (lazy facade, subpackage dependency tiers), the `output` egress protocol, the naming/keyword-only conventions, and the characterization-test guardrails. Use for any code change inside the mixing package (not for merely *using* mixing — that’s the mixing-editor agent and the mixing-\* skills).

Source: [`.claude/agents/mixing-dev.md`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/agents/mixing-dev.md); tools: `Bash, Read, Edit, Write, Glob, Grep`.

### `mixing-editor`

Performs an audio/video editing task end-to-end with the `mixing` Python package — trim, concat, fade, crop, loop, change speed, replace/mix audio, Ken Burns pan/zoom, thumbnails, burn-in subtitles, transcribe, remove fillers, detect chapters, and TTS dub/translate. Use when the user hands over one or more media files and an editing goal (“trim this to the chorus”, “add this music under the narration”, “make a thumbnail”, “burn in subtitles”, “dub this in French”, “cut the ums”). The agent writes a short Python script, runs it, verifies the produced file, and reports the exact output path(s).

Source: [`.claude/agents/mixing-editor.md`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/agents/mixing-editor.md); tools: `Bash, Read, Write, Edit, Glob, Grep`.

## Instruction files

Files agents read before working in this repository.

- [`.claude/CLAUDE.md`](https://github.com/thorwhalen/mixing/tree/HEAD/.claude/CLAUDE.md): read by Claude Code

## Machine-readable documentation

This site publishes the same documentation in forms that fit an agent’s context window:

- [`llms.txt`](https://thorwhalen.github.io/mixing/llms.txt): an index of every page with a one-line description ([llms.txt](https://llmstxt.org) format)
- [`mixing.md`](https://thorwhalen.github.io/mixing/mixing.md): the whole documentation as one Markdown file
- `<page>.html.md`: a rendered Markdown twin of every page, advertised from each page’s `<head>` with `<link rel="alternate" type="text/markdown">`
- [`objects.inv`](https://thorwhalen.github.io/mixing/objects.inv): the Sphinx inventory: a symbol-to-URL index (`sphobjinv convert plain objects.inv -`)


# api.html.md

# API reference

| [`mixing`](_autosummary/mixing.html.md#module-mixing)   | Tools for video and audio editing.   |
|-------------------------------------------------------------------------|--------------------------------------|


