| name | mixing |
| description | 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, 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". Start here to pick the right tool and the right sub-skill (mixing-audio / mixing-video / mixing-transcript / mixing-dubbing).
|
mixing — router & core conventions
mixing edits audio/video from Python. This skill orients you; then jump to the
focused sub-skill for the medium you're working in.
Pick a sub-skill
| You want to… | Sub-skill |
|---|
| Edit audio (slice, fade, crop, concat, overlay, normalize, align, segment) | mixing-audio |
| Edit video (slice, crop, loop, speed, replace/normalize audio, Ken Burns, concat, thumbnail, subtitles) | mixing-video |
| Transcribe speech, remove fillers, make SRT/prose, detect chapters | mixing-transcript |
| Re-voice / translate via text-to-speech | mixing-dubbing |
First moves (every task)
- Check ffmpeg — most operations need it:
import mixing
assert mixing.has_ffmpeg(), "install ffmpeg (brew install ffmpeg / apt install ffmpeg)"
- Import lazily / specifically.
import mixing is cheap (no moviepy/opencv
loaded until you touch a heavy name). Either use the facade
(mixing.Audio, mixing.Video, mixing.replace_audio, …) or import the
subpackage (from mixing.audio import Audio).
- Know the
output convention (below) before calling anything that writes.
The output protocol (read this once, applies everywhere)
Every function that produces a result takes one output argument:
crop_video("in.mp4", 5, 15, output="out.mp4")
crop_video("in.mp4", 5, 15, output="clips/")
crop_video("in.mp4", 5, 15)
seg = mixing.Audio("song.mp3")[10:30]
seg.fade_in(2).save(output="clip.mp3")
fade_in("in.mp3", output=lambda a: a.duration)
None → object producers return the in-memory object; file producers save
beside the input and return the Path.
- a file path → write there, return
Path.
- a directory → write an auto-named file inside, return
Path.
- a callable →
output(result) is returned (general escape hatch).
Functions that emit several artifacts qualify the destinations instead
(remove_fillers(media, output_dir=..., output_media=...)).
Sliceable media
Audio and Video are lazy, sliceable views:
from mixing.audio import Audio
from mixing.video import Video
Audio("song.mp3")[10:30].fade_out(3).save(output="end.mp3")
Audio("song.mp3", time_unit="samples")[0:44100]
Video("clip.mp4")[5:15].save(output="cut.mp4")
Video("clip.mp4")[100]
with Video("clip.mp4") as v: dur = v.duration
Optional extras & keys
pip install mixing[audio] (pydub/soundfile), [gen] (Veo), [llm] (aix
for chapter titles + SRT translation), [widget], [clipboard].
- ElevenLabs features (
mixing.transcript, mixing.dubbing) need
ELEVENLABS_API_KEY (or pass api_key=). Responses are cached on disk, so a
re-run of the same input is free and offline.
- Veo (
mixing.video.genai) needs Google Cloud auth (GOOGLE_CLOUD_PROJECT +
application-default or service-account credentials).
Gotchas
output=None for file producers writes next to the input — pass an
explicit output to control location.
- ElevenLabs/Veo calls cost money; rely on the disk cache and pass
cache=True (default for these) / refresh=True to force a re-call.
mixing.video.replace_audio(..., mix_ratio=): 1.0 = only the new audio,
0.0 = keep original, 0.5 = equal blend.