muvid.montage.pipeline¶
The montage pipeline — the one path from a song and a pool to a finished video.
- audio + photos[] / clips[] (+ cover)
-> Analysis beat grid, bars, sections (muvid.montage.analysis) -> TreatmentSpec the creative decision (muvid.montage.spec) -> Plan every cut and framing computed (muvid.montage.plan) -> mp4 ffmpeg, bounded stages (muvid.montage.render)
Each arrow is a seam with a working default, so the whole thing runs on a bare
song.wav and three photos with no AI, no API key and no network — and the
plan is written to plan.json BEFORE any frame is rendered, so a render
that fails still leaves the edit list behind for inspection.
Resource bounds are REFUSED, not clamped: a clamped request silently produces
a different video than the one asked for. The bounds read the same env-var
shape as the lyric video (MUVID_MONTAGE_MAX_PIXELS etc.) with the same
defaults, plus a per-input file-count bound — 64 photos is a montage, 6,400
is a denial of service.
Module Attributes
Resource bounds, env-configurable like |
Functions
|
|
|
Refuse a render that would exceed the resource bounds. |
|
Render one montage. |
- muvid.montage.pipeline.MAX_PIXELS = 8294400¶
Resource bounds, env-configurable like
MUVID_LYRICVID_*. Same defaults: a 4K frame, 60 fps, a 15-minute song. The media bound is this subgenre’s own.
- muvid.montage.pipeline.build_treatment(params)[source]¶
(treatment, repair_notes, source)from a request’s params.A supplied
treatmentis coerced and repaired; otherwise a one-scene treatment is built fromarchetype(defaultbeat_cut).- Return type:
tuple[TreatmentSpec,list[str],str]
>>> t, notes, source = build_treatment({'archetype': 'grid'}) >>> t.scenes[0].archetype, notes, source ('grid', [], 'archetype') >>> build_treatment({'treatment': {'scenes': [{'archetype': 'nope'}]}})[1:] (["scenes[0].archetype 'nope' -> 'beat_cut'"], 'supplied')
- muvid.montage.pipeline.check_render_bounds(canvas, duration_s, *, n_photos=0, n_clips=0)[source]¶
Refuse a render that would exceed the resource bounds. Refuse, not clamp.
- Return type:
>>> check_render_bounds(Canvas(width=1920, height=1080, fps=30), 200.0, n_photos=12) >>> check_render_bounds(Canvas(width=30000, height=30000, fps=30), 10.0) Traceback (most recent call last): ... ValueError: canvas 30000x30000 is 900000000 px/frame; the bound is 8294400 (MUVID_MONTAGE_MAX_PIXELS) >>> check_render_bounds(Canvas(width=640, height=360, fps=24), 10.0, n_photos=65) Traceback (most recent call last): ... ValueError: 65 photos; the bound is 64 per input (MUVID_MONTAGE_MAX_MEDIA)
- muvid.montage.pipeline.render(request)[source]¶
Render one montage. Satisfies
muvid.subgenres.Renderer.request.inputstakesaudio(required),photosand/orclips(at least one non-empty) and an optionalcover.request.paramstakestreatmentorarchetype,strict,beats,beats_per_bar,sections,width,heightandfps.- Return type: