muvid.footage.service¶
The footage operations — one function per thing you can do to a music-video project.
**This module is the single source of truth for the music_video genre’s operations.**
Every surface reaches them from here and lists them from FOOTAGE_OP_SPECS, never
again by hand:
the MCP connector (
muvid.mcp.footage_tools,muvid.mcp.scoring_tools) resolvesproject_idto the caller’s workspace project, calls the function, and turns aFootageErrorinto aToolError;a host that serves the genre (reelee’s studio) reads the catalogue nw holds —
nw.genre_ops("music_video"), registered fromFOOTAGE_OP_SPECSbymuvid.genre_music_video— and calls the same functions on a host-placedmuvid.Project’sfootage.
Contract of every operation here:
the first argument is a
MusicVideoFootageProject(fp), everything else is keyword-only and JSON-able;the result is a JSON-able
dict(noproject_id— the transport knows which project it opened);a refusal raises
FootageErrorwith a message naming the next action — never a transport’s error type, never a raw traceback for a caller mistake;the docstring is model-facing: it is what an assistant reads to decide whether and how to call the operation, so it says what the operation changes and what to read in its reply.
Media arrives as a LOCAL file (set_song(fp, path=...), add_clip(fp, path=...)):
fetching a URL is the MCP connector’s business, streaming an upload the host’s. Either
way the file is COPIED into the project, so the caller may delete it afterwards.
Import-light by design (stdlib + muvid.footage.edl at module top): a host
imports the genre module, which imports this one to read the operations’ signatures, and
must not pay for numpy/ffmpeg/fastmcp to build a catalogue.
The named-edit operations (save_edit, set_cut, split_cut, merge_cut,
replace_edit, render(edit_id=...)) are what make “change cut 7 and render again”
possible: before them, an EDL was persisted only inside a render’s meta.json. An edit
lives at footage/edits/<edit_id>.json and every change to it goes through
validate_edl — structurally, with allow_unreliable=True (an edit is a plan; the
trust refusal belongs where the encode does, in render()).
Module Attributes
Every optional |
Functions
|
Where the music video stands: the song, the videos and where each sits on the song, the saved edits, the finished videos, and the next useful step. |
|
Set the project's song — the clean master every video is aligned to and whose audio the finished video uses. |
|
Add one video or photo to the project. |
|
Remove one footage video from the project — its stored file and its entry. |
|
Find where each video sits on the song by listening to its own audio, and save it. |
|
Place one video on the song BY HAND: the song time at which the video's own first frame plays (negative = the video starts before the song does). |
|
Forget where I placed this video: remove a hand-declared offset, so the next |
|
Say whether a video has the song in its own sound — overriding what listening found. |
|
Which videos cover which spans of the song (overlaps shown), from the saved alignment — the map for choosing what to cut to. |
|
The song's beat grid — tempo and beat instants on the song timeline — without looking at the footage. |
|
The song's waveform, to draw under the timeline: |
|
Where the beat is in the song or in one video — CONTINUOUS signals, to look at, threshold and bend, not only beat instants. |
|
Every video's filmstrip — thumbnails to draw each camera's lane. |
|
One video's filmstrip (the same record |
|
Look at the footage: score every placed video, on the song's own timeline — picture quality and how its movement sits on the beat — and save the curves. |
|
The saved footage curves — for the lanes under each video, and for inspection. |
|
The ways to cut on offer — the selection strategies |
|
Cut it for me: build an edit of the whole song — or of |
|
Save a cut list as a new named edit. |
|
The saved edits, oldest first: each one's |
|
Why each cut of an edit is where it is — the post-mortem, in plain words. |
|
One saved edit: its cut list ( |
|
Replace a saved edit's whole cut list — the power tool for rewriting an edit at once. |
|
Change one cut of a saved edit ( |
|
Split the cut playing at song time |
|
Join cut |
|
Fit the moves to the beat: for each cut that shows a video in the part of the song the edit covers (its span) — or the cuts at |
|
Choose which part of the song the video covers — where it starts and ends. |
|
Give an edit a new name — what the edit picker and the renders made from it show. |
|
The looks a cut can take — camera moves (punch in, slow push, slow pull, pans) and grades (vivid, black and white, posterize, cartoon) — each with its |
|
Undo the last change to a saved edit (a cut changed, split, joined, the span, a whole replacement — by a person or by the assistant). |
|
Redo the change |
|
Delete a saved edit. |
|
Make the video: render a saved edit onto the canvas, over the clean song. |
|
The finished videos, newest first: each one's |
|
The project as lacing-native standoff annotations, for a multitrack editor. |
|
Assemble and render a music video — auto (a |
|
The alignment record of a DECLARED offset — coverage computed, nothing measured. |
|
The editor's DECISION-tier annotations turned back into a cut list ( |
|
One EDL entry as JSON — full precision (it must feed back verbatim), gaps as null. |
|
What the song's timeline looks like under |
One |
|
|
|
|
The render canvas: an explicit per-render override, else the project's. |
|
Take the project's cover frame again — from the newest render, else from the first clip — and register it. |
|
Write one JPEG frame of |
|
Bring a video finished ELSEWHERE into the project as a render (the importer's). |
|
|
|
A video's average frame rate (frames per second), or |
|
A render record as a HOSTED surface may return it: |
|
The alignments a scoring run would use; refuses without a song or an alignment. |
|
Score every aligned clip and persist the tensor (the engine behind |
Classes
|
One operation's catalogue row: the function (by |
Exceptions
An operation refused — the message says why and what to do next. |
|
An operation stopped between steps because its host asked it to. |
- muvid.footage.service.EDL_OPTIONAL_FIELDS = (('transition', <function <lambda>>, None), ('crop', <function <lambda>>, None), ('crop_end', <function <lambda>>, None), ('look', <class 'str'>, None), ('look_time_varying', <class 'bool'>, False), ('look_spec', <class 'dict'>, None), ('slip_s', <class 'float'>, 0.0), ('rate', <class 'float'>, 1.0), ('source_in', <class 'float'>, None))¶
Every optional
EdlEntryfieldedl_json()carries, and how to render it. The list is the round trip._as_entryreads all of these back by name, so a field missing from here is a direction the caller gave, the renderer honoured, and the returned/persisted edit does not contain. Each row is(field, render, absent), whereabsentis the value that means “omit this key” — a column rather than a hardcodedNonebecauselook_time_varying(muvid#73) is a boolean whose absent value isFalse.
- exception muvid.footage.service.FootageCancelled[source]¶
Bases:
ExceptionAn operation stopped between steps because its host asked it to.
- exception muvid.footage.service.FootageError[source]¶
Bases:
ValueErrorAn operation refused — the message says why and what to do next.
- class muvid.footage.service.OpSpec(name, title, effect, runs='now', hide=(), host_params=(), max_upload_bytes=None, limits=<factory>)[source]¶
Bases:
objectOne operation’s catalogue row: the function (by
namein this module), a plain-languagetitle(it becomes a button and a command title), what it does to the project (effect: read | write | render | destroy) and how a host runs it (runs: now | job).hidenames parameters a transport fills that a caller must not (duration_s— a probed fact, not a claim a caller makes;annotate— a transport’s hook).Host-agnostic data:
muvid.genre_music_videoturns these intonw.GenreOprows, andmuvid.mcpderives its footage tools from the same names.- host_params: tuple[str, ...] = ()¶
an upload’s server-side
pathand originalfilename. Never in a client’s schema.- Type:
Parameters only the HOST supplies (
nw.GenreOp.host_params)
- muvid.footage.service.add_clip(fp, *, path, name='', filename='', clip_id='', ext='', duration_s=None)[source]¶
Add one video or photo to the project.
A video that is a recording of the song (a concert, a dance) is synced to it once
alignhas listened to it; any other video, and every photo, is cut to the music instead (propose_edit). A photo (a file ending in one ofSTILL_SUFFIXES) is stored upright and at mostSTILL_MAX_SIDEpixels on its longest side, and moves on screen with a pan or zoom.The file arrives from the host (an upload):
pathis where the host put it andfilenameits original name; it is copied into the project.nameis what the video is called on screen (default: the original file name without its extension).clip_idfixes the id (default: a fresh one); an id already in the project is refused. Size-, duration- and count-capped.Run
alignafterwards: listening is how a video with the song in it gets its place on the song. Returns theclip_id, itsnameandduration(andartifact_idwhen hosted); a photo alsokind: "still",width,height.- Return type:
- muvid.footage.service.align(fp)[source]¶
Find where each video sits on the song by listening to its own audio, and save it.
Returns each clip’s offset, a confidence in [0,1], its
support(the fraction of the clip that agrees on that offset,nullwhen the aligner took a single whole-clip measurement), and its coverage of the song, plus these lists:low_confidence— clips that matched weakly, for reporting;unreliable— clips whose offset the aligner will NOT vouch for: most often a video that simply does not have the song in it (a day out, b-roll). These stay in the project, andpropose_editcuts them TO the music instead of syncing them (footage_roles) — unless listening heard the song in them without being able to place it (heard_the_song), or a person said they have the song (set_has_song yes). Those stay synced: the auto path prefers other clips over them and sets aside a span only they cover (coverage.excluded, muvid#88), which is then filled from the footage cut to the music, if there is any. Rendering still REFUSES an explicit edit that cuts to one, and still refuses an auto edit when NO clip is trustworthy, unless called withallow_unreliable=true— because a wrong offset does not fail, it renders a video out of sync with the song (muvid#59). Re-align, place the clip by hand (set_offset), accept the smaller edit, or opt in deliberately;no_consensus— clips too short to be put to a vote at all (under about 4.5 s). A clip in this list can be marked reliable and still be wrong, and no other field will say so: its offset rests on one measurement, judged by a confidence score that does not rank correctness in this band — measured on the muvid#59 shoot, the WRONG offset scored highest of three (0.834 against 0.566 and 0.621), and on a repeating fixture a 4.4 s clip landing 8 s out is vouched at 0.381. Nothing is refused on this basis, because refusing would take the correct short clips with it. So if a short clip looks out of sync in the render, this list is the first place to look — and muvid#91 is where that trade-off is being decided.
A clip whose offset a person DECLARED (
set_offset) is ALWAYS left alone — a person placed it, usually because the aligner got it wrong (muvid#59) — and is named inkept_declared. To have one measured again,clear_offsetit first. Every measured record sayssource: "measured".Run this after adding/removing clips and before cutting.
- Return type:
- muvid.footage.service.assemble(fp, *, strategy='', edl=None, preset='', weights=None, config=None, canvas='', allow_unreliable=False, edit_id=None, label='', annotate=None, span=None, should_cancel=None)[source]¶
Assemble and render a music video — auto (a
strategy) or an explicitedl.The engine behind
render()and the MCPassemble_music_videotool (whose docstring is the full caller-facing contract). Validation is the ONE gate (validate_edl), with the trust refusal ON unlessallow_unreliable. Writesrenders/<render_id>/final.mp4+meta.jsonand returns the meta.edit_id/labelare recorded in the meta;annotate(render_id, ref_n)is the transport’s hook for keys only it can fill (the MCP download claim) — merged into the meta before it is written.span(an explicitedl’s part of the song) renders only that stretch: the video AND the song cut to it, the song faded out overTAIL_FADE_Swhen the span ends before the song does.- Return type:
- muvid.footage.service.assemble_refusal(entries, aligns, song_dur, canvas)[source]¶
Noneif rendering this edit would go ahead; the refusal if it would not — put to the GATE rather than re-implemented here.
- muvid.footage.service.beat_grid(fp)[source]¶
The song’s beat grid — tempo and beat instants on the song timeline — without looking at the footage.
Computed once on the song (never per clip — clips map to it through their offsets) and cached under the project keyed on the song’s content hash, so the second call is a file read; a project that has been scored is served from that run instead.
sourcesays which (computed|cache|scores).Needs the
scoringextra (librosa); without it the refusal names the install. Needs a song; no alignment is required.Returns
tempo_bpm,beats(seconds, ascending),n_beats,song_durationandsource, and for numbering bars:downbeats(the beats that start a bar),downbeats_source—measured(the estimator found them),derived(the beat phase carrying the most onset energy,beats_per_barbeats to a bar — muvid.montage’s rule) orfirst_beat(no onset energy to vote with, so bars start on the first beat) —beats_per_barandbar_of_beat(each beat’s bar number, 1 for the first bar, 0 for a pickup before it).- Return type:
- muvid.footage.service.beat_signals(fp, *, source='song', max_points=1000)[source]¶
Where the beat is in the song or in one video — CONTINUOUS signals, to look at, threshold and bend, not only beat instants.
sourceis"song"or a clip id. The song gets its sound (audio_onset: the onset envelope the beat grid is estimated from). A video gets its own soundtrack’saudio_onsetwhen it has one, and three visual signals:motion(how much the people in the picture move, the camera’s own move taken out),motion_stops(moves stopping dead and turning — the movement accents) andmotion_stops_local(the same, place by place).Each signal is in the media’s OWN time: sample
iis att0 + i * hop_ss of the song, or of the clip (song timeoffset + t). Values are unnormalised, withmin,maxandp99beside them;Noneis a sample that was not measured.max_pointspools each signal to at most that many samples by their maximum, so a peak survives (0 = every sample; an editor drawing it wants that).Measured once per media and kept (a video’s first call reads every frame and takes tens of seconds; a second call for the same video waits for the first rather than measuring again). Needs the
scoringextra.Returns
{source, kind: audio|video, duration_s, tempo_bpm, beats, signals: {name: {name, label, domain, t0, hop_s, n, min, max, p99, values}}}—beatsandtempo_bpmare the soundtrack’s ([]/Nonewithout one).- Return type:
- muvid.footage.service.clear_offset(fp, *, clip_id)[source]¶
Forget where I placed this video: remove a hand-declared offset, so the next
alignmeasures the clip by its audio instead.Only a DECLARED offset can be forgotten (a measured one is replaced by aligning again); an unknown clip, or one with no declared offset, is refused. Until
alignruns again the clip has no place on the song, and footage scores made with the old offset are dropped.- Return type:
- muvid.footage.service.coverage_report(entries, aligns, song_dur, *, excluded=(), span=None)[source]¶
What the song’s timeline looks like under
entries— covered, weak, MISSING.Pass only FOOTAGE entries: a gap renders fill, and filled is not covered. Uncovered audio is named with explicit start/end times; a span whose only footage is weakly aligned is listed with the numbers that make it weak;
excluded(muvid#88) names spans the auto path gave up because only an unvouched clip covered them.span(a trimmed edit’s(start, end)) bounds what counts as uncovered.- Return type:
- muvid.footage.service.declared_alignment(clip_id, offset_s, *, clip_duration, song_duration)[source]¶
The alignment record of a DECLARED offset — coverage computed, nothing measured.
confidenceis 1.0 andreliableTrue because a person vouched for the offset;support/marginstayNonebecause no vote was held.sourcesays so.- Return type:
>>> a = declared_alignment("c1", -8.5, clip_duration=260.0, song_duration=249.6) >>> a.coverage, a.overlaps, a.source ((0.0, 249.6), True, 'declared')
- muvid.footage.service.delete_edit(fp, *, edit_id)[source]¶
Delete a saved edit. Videos already rendered from it are kept (they still name the edit they came from). An unknown
edit_idis refused, naming the edits.- Return type:
- muvid.footage.service.editor_document(fp)[source]¶
The project as lacing-native standoff annotations, for a multitrack editor.
One tier per clip (its
clip-alignment/v1and, once scored, itsclip-score-track/v1curves) plus aDECISIONtier holding the current default proposal asmusic-video-edl/v1entries — everything referenced to the song by content hash, on one shared song-time axis. Needs theeditorextra (lacing) and an alignment.- Return type:
- muvid.footage.service.edits(fp)[source]¶
The saved edits, oldest first: each one’s
edit_id,name, how it was made, how many cuts it has, andproblem— why it would not validate against the current alignment (nullwhen it does).unreliablenames clips it cuts to whose offsets rendering would refuse.- Return type:
- muvid.footage.service.edl_from_annotations(fp, *, annotations)[source]¶
The editor’s DECISION-tier annotations turned back into a cut list (
edl), ready forsave_edit/replace_edit— a faithful read, not a re-selection. Annotations referencing another song are refused (muvid#35).- Return type:
- muvid.footage.service.edl_json(e)[source]¶
One EDL entry as JSON — full precision (it must feed back verbatim), gaps as null.
Optional fields are emitted ONLY when set (
EDL_OPTIONAL_FIELDS), which keeps every existingrenders/*/meta.jsonbyte-identical and the render -> edit -> re-render round trip (muvid#21 item 3) exact.- Return type:
- muvid.footage.service.exclusion_note(x)[source]¶
One
warningsline per span the auto path set aside (muvid#88).- Return type:
- muvid.footage.service.explain_edit(fp, *, edit_id, start_s=None, end_s=None)[source]¶
Why each cut of an edit is where it is — the post-mortem, in plain words.
For every cut: where it lands (on which beat of which bar, or how far off one), how the picture changes (a hard cut, or a fade and where that fade really starts), what picture it shows and why that stretch (its picture changes on the beats inside the cut; a photo’s camera move; a video synced where it was filmed), and
flagsfor the ones worth a look (straddles_the_beat,off_the_beat,long_hold,repeated_stretch). Plus asummaryof the whole edit (fades vs cuts, the song’s pace and shape, the longest shot).Every clock time in a sentence is listed in
times({label, s}) so a screen can link it to the player.start_s/end_skeep only the cuts overlapping that part of the song (the summary stays whole). Explains the edit as it stands now, hand changes included. Free; reads only.- Return type:
- muvid.footage.service.filmstrip(fp, *, clip_id)[source]¶
One video’s filmstrip (the same record
filmstripsgives per clip, with itsclip_idandfps).- Return type:
- muvid.footage.service.filmstrips(fp)[source]¶
Every video’s filmstrip — thumbnails to draw each camera’s lane.
Per clip: sprite sheets of
frame_wxframe_hframes (colsxrowsto a sheet, left to right then down), sampled atfpsframes per second of the CLIP’s own time — frameiis the clip ati / fpss, which sits at song timeoffset + i / fps. Each sheet is anartifact_id(when the project is hosted), with itsfirst_frameandn_frames. Made once per clip and kept; a clip that has none yet takes a few seconds the first time.Returns
{fps, clips: {clip_id: {duration_s, n_frames, frame_w, frame_h, sheets: [{artifact_id, cols, rows, first_frame, n_frames}]}}}.- Return type:
- muvid.footage.service.fit_to_beat(fp, *, edit_id, indices=None, min_z=2.5, apply=True)[source]¶
Fit the moves to the beat: for each cut that shows a video in the part of the song the edit covers (its span) — or the cuts at
indices— find the slip and speed that put its video’s movement accents on the song’s beat, and apply it where the evidence is strong.Per cut, every slip within +-0.25 s (a frame at 60 fps apart) and every speed from x0.92 to x1.08 (1 % apart) is tried; the best is scored against a null of the same search on the video’s accents with their relation to the beat destroyed (
muvid.footage.beat_fit), giving az. A cut changes only whenz >= min_zAND the fit is better than its current timing — and the video still holds the footage it then reads. Cuts shorter than three beats, gaps and videos with nothing measurable are left alone, each with its reason.apply=Falsereports without changing anything. All the changes are ONE step of the edit’s history, so oneundo_edittakes them all back.How much to expect. On three phone videos of a crowd dancing, the accents locked to the beat on one of them only; each video’s own SOUND locked on all three. So on footage like that most cuts will be left as they are, which is the right answer when the picture does not show the beat. It works best on a clearly visible dancer.
Needs the
scoringextra: the first call measures each video’s movement (tens of seconds a video; kept for next time). Returns the edit (get_edit’s shape) plusfit:cuts({index, slip_s, rate, z, applied, reason}per cut looked at),fitted,keptandmin_z.- Return type:
- muvid.footage.service.frame_rate(video)[source]¶
A video’s average frame rate (frames per second), or
Noneif unreadable.
- muvid.footage.service.frame_size(video)[source]¶
[width, height]of a video as DISPLAYED (a ±90° rotation swaps them).
- muvid.footage.service.get_edit(fp, *, edit_id)[source]¶
One saved edit: its cut list (
edl, every span of the song, gaps asclip_id: null), its name and history, and acoveragereport. Cut indexes inset_cut/merge_cutrefer to positions in thisedl.- Return type:
- muvid.footage.service.grab_cover_frame(video, dest)[source]¶
Write one JPEG frame of
videotodest—COVER_AT_FRACTIONof the way in,COVER_WIDTHwide. The cover of every hosted muvid production.- Return type:
- muvid.footage.service.import_render(fp, *, path, render_id, label='', edit_id='')[source]¶
Bring a video finished ELSEWHERE into the project as a render (the importer’s).
Copied to
renders/<render_id>/final.mp4with a meta that says it was imported:edit_idnames the saved edit it was cut from (when known), and there are nochecks— muvid did not make it and does not claim to have verified it. Idempotent: the same bytes under the same id change nothing.- Return type:
- muvid.footage.service.looks(fp=None)[source]¶
The looks a cut can take — camera moves (punch in, slow push, slow pull, pans) and grades (vivid, black and white, posterize, cartoon) — each with its
params_schema. Give one toset_cutaslook={"name": ..., **params}.- Return type:
- muvid.footage.service.merge_cut(fp, *, edit_id, index, into='previous')[source]¶
Join cut
indexto its neighbour: the neighbour (into“previous” or “next”) takes over its span, so the neighbour’s video must cover it. The joined cut keeps the neighbour’s video, framing and look. Returns the changed edit.- Return type:
- muvid.footage.service.peaks(fp, *, n=2000)[source]¶
The song’s waveform, to draw under the timeline:
nequal slices of the song, each the loudest moment in it (mono), scaled so the loudest slice is 1.0.Returns
{duration_s, n, peaks: [0..1, ...]}; sliceicovers song timei * duration_s / nto(i + 1) * duration_s / n. Kept per song andn.- Return type:
- muvid.footage.service.propose_edit(fp, *, strategy='', preset='', weights=None, config=None, save=True, name='', span=None, pace='', style='auto')[source]¶
Cut it for me: build an edit of the whole song — or of
span([start_s, end_s], the part of the song the video covers) — from the placed videos, and (by default) save it as a named edit, without rendering anything.strategypicks how (seestrategies; defaultbest_confidence). Giving apreset(“energetic”/”contemplative”), per-metricweightsor aconfig(lambda_switch/l_min_s/l_max_s/boundary_mode) selects the score-drivenweightedstrategy, which needsscorefirst.Returns the
edl(spans the WHOLE song; spans no footage covers are explicit gap entries,clip_id: null, rendered as black), thestrategyused, acoveragereport naming every uncovered span and every weakly-aligned segment,warnings, andassemble_refusal(non-null when rendering it would be refused because no clip is trustworthy). Withsaveit also returns theedit_idto change it (set_cut…) and render it (render).Footage that does not contain the song is cut to the music. The strategy cuts only the videos that are recordings of the song (placed confidently by
align, or markedset_has_song yes); every span they leave is filled from the other videos and the photos, cut on the song’s beats and bars, each video shown at the stretch whose picture changes land on the beat (musicin the reply says how that went). With no recording of the song at all, the whole song is cut to the music — noalignneeded.pace(slow/steady/driving/frantic) sets how often those cuts come;stylehow the picture changes:cuts(hard, on the beat),fades(each starting on the beat), orauto(the song’s choice — fades for a slow song).explain_editsays why each cut is where it is.- Return type:
- muvid.footage.service.public_render(fp, meta)[source]¶
A render record as a HOSTED surface may return it:
videomade relative to the project (renders/<id>/final.mp4) — never an absolute server path; play it by itsartifact_id.- Return type:
- muvid.footage.service.redo_edit(fp, *, edit_id)[source]¶
Redo the change
undo_editlast took back. A new change after an undo discards what could be redone. Returns the edit as it now is.- Return type:
- muvid.footage.service.refresh_cover(fp)[source]¶
Take the project’s cover frame again — from the newest render, else from the first clip — and register it. Only for hosted projects (
Noneotherwise).
- muvid.footage.service.remove_clip(fp, *, clip_id)[source]¶
Remove one footage video from the project — its stored file and its entry.
Irreversible for the clip (add it again if it was a mistake); existing renders are untouched. Removal INVALIDATES every measured offset and every footage score, as changing the song does — the alignment describes the clip set it was measured on — so run
alignagain before cutting. Offsets a person DECLARED for the remaining clips are kept. An unknownclip_idis refused, naming the project’s clips.- Return type:
- muvid.footage.service.rename_edit(fp, *, edit_id, name)[source]¶
Give an edit a new name — what the edit picker and the renders made from it show.
Only the name changes; the cuts, the span and the edit’s id stay as they are, and the rename can be undone like any other change. Returns the edit.
- Return type:
- muvid.footage.service.render(fp, *, edit_id, canvas='', allow_unreliable=False, annotate=None, should_cancel=None)[source]¶
Make the video: render a saved edit onto the canvas, over the clean song.
Slow (minutes of encoding for a full song), so hosts run it in the background. The video is exactly as long as the part of the song the edit covers (its
span, default the whole song; seeset_span), with the song cut to match and faded out at the end when it stops early; gaps render black.canvas(“landscape” / “portrait” / “square”) re-renders the same edit in another shape; default: the project’s.Refused when the edit cuts to a clip whose offset the aligner will not vouch for — a wrong offset renders a video out of sync with the song (muvid#59) — unless
allow_unreliable; fix it withset_offsetor by changing those cuts. Returns the render record:render_id,edit_id, itscoverage,okand thechecksbehind it,warnings(read them — they are what the render plan found), andartifact_idto play it by when the project is hosted.- Return type:
- muvid.footage.service.renders(fp)[source]¶
The finished videos, newest first: each one’s
render_id, speakableref, theedit_idit was made from, itslabel, canvas,ok, the number ofwarnings, andartifact_idto play it by when the project is hosted.- Return type:
- muvid.footage.service.replace_edit(fp, *, edit_id, edl)[source]¶
Replace a saved edit’s whole cut list — the power tool for rewriting an edit at once. The new list is checked exactly as
save_editchecks one; on refusal the edit is left as it was. The previous list is not kept.- Return type:
- muvid.footage.service.require_scorable(fp)[source]¶
The alignments a scoring run would use; refuses without a song or an alignment.
- Return type:
- muvid.footage.service.resolve_canvas(fp, canvas)[source]¶
The render canvas: an explicit per-render override, else the project’s.
- muvid.footage.service.run_scoring(fp, *, hop_s=0.1, metrics=None, enable_lipsync=None, progress_cb=None, should_cancel=None)[source]¶
Score every aligned clip and persist the tensor (the engine behind
score(), with the job hooks a background runner passes).- Return type:
- muvid.footage.service.save_edit(fp, *, edl, name='', how_made='by hand', edit_id='', span=None)[source]¶
Save a cut list as a new named edit.
edlis a list of{song_start, song_end, clip_id}spans (plus optionaltransition/crop/crop_end/look/look_time_varying), in the same formget_editreturns andpropose_editproduces. Holes are filled with gap entries; the list is checked (order, overlap, every span inside its clip’s coverage) and refused with the reason if it does not hold.edit_idfixes the id (an existing one is refused — usereplace_edit).span([start_s, end_s]) makes the edit cover only that part of the song — its render is that long, the song cut to it; default the whole song. Returns the saved edit.- Return type:
- muvid.footage.service.score(fp, *, hop_s=0.1, metrics=None, should_cancel=None)[source]¶
Look at the footage: score every placed video, on the song’s own timeline — picture quality and how its movement sits on the beat — and save the curves.
Slow (it decodes every clip), so hosts run it in the background. Needs a song and an alignment. Every core metric is computed; weighting happens later, when cutting, so re-weighting never re-scores. The lip-sync tier is off unless the operator enabled it. Returns what was scored and what was skipped (and why).
- Return type:
- muvid.footage.service.scores(fp, *, clip_id='', metrics=None, max_points=1500)[source]¶
The saved footage curves — for the lanes under each video, and for inspection.
no
clip_id→ a SUMMARY (metrics, per-clip coverage, beats, tempo, the decimatedselection_margin, grid geometry) — bounded, safe as the default;clip_id→ that clip’s curves (values asnull-masked arrays, decimated tomax_pointsper metric).
- Return type:
- muvid.footage.service.set_cut(fp, *, edit_id, index, clip_id=None, song_start=None, song_end=None, look=None, look_time_varying=None, slip_s=None, rate=None)[source]¶
Change one cut of a saved edit (
indexis its position inget_edit’s edl).clip_id: show another video over this span (""makes it a gap). The new video must cover the span. Its framing (crop) is dropped, since it was chosen for the old video’s frame; itslookis kept. A video cut to the music (or a photo) is shown from its start; a video with the song in it, where it was filmed.song_start/song_end: move the cut’s boundaries. The neighbouring cut’s boundary moves with it, so the edit stays one continuous timeline; a move that would swallow a neighbour whole is refused (join them withmerge_cut).look: a NAMED look fromlooks—{"name": "slow_push", "zoom": 1.08}, compiled for this cut’s length and the project’s canvas and kept on the cut aslook_spec(with every parameter’s value) so it can be shown and changed — or, for power users, one raw ffmpeg filter chain (allowlisted; setlook_time_varyingfor one that moves).""removes it.slip_s: show a slightly different moment of the same video over the same span —0.1reads the footage 0.1 s later — to put a dancer’s moves on the beat where the clip’s alignment is right overall but a little off here. At most one beat either way (SLIP_MAX_S);0removes it. A new video (clip_id) starts unslipped.rate: play this cut’s video a little faster or slower —1.03shows 3 % more footage over the same span, so the moves run 3 % faster (the song is never touched). Within1 +- RATE_MAX_DEV;1removes it. The video must hold the footage the cut then reads. A new video starts at speed 1.
Parameters left out are unchanged. The changed edit is checked and saved; returns it.
- Return type:
- muvid.footage.service.set_has_song(fp, *, clip_id, has_song)[source]¶
Say whether a video has the song in its own sound — overriding what listening found.
"yes": it is a recording of the song (a concert, a dance); it is synced to the song and never cut freely to the music."no": it is not (a day out, b-roll); it is always cut to the music."auto"(the default) believes the listening: a videoalignplaced confidently is synced, any other is cut to the music. A photo is always cut to the music. Like a hand-placed offset, the choice survives listening again. Takes effect at the nextpropose_edit.- Return type:
- muvid.footage.service.set_offset(fp, *, clip_id, offset_s)[source]¶
Place one video on the song BY HAND: the song time at which the video’s own first frame plays (negative = the video starts before the song does).
Use it when
aligngets a clip wrong — on long, repetitive songs it can land a whole chorus away (muvid#59) — or when you already know the offset. The offset is recorded assource: "declared"and trusted for rendering (a person vouched for it); how much of the song the clip covers is computed from the two durations.alignkeeps it unless told otherwise. Changing an offset makes the footage scores stale, so they are dropped.- Return type:
- muvid.footage.service.set_song(fp, *, path, ext='', filename='', duration_s=None)[source]¶
Set the project’s song — the clean master every video is aligned to and whose audio the finished video uses. Replaces any previous song.
The file arrives from the host (an upload):
pathis where the host put it andfilenameits original name, so the song keeps its name and extension; it is copied into the project, so the host may delete its copy afterwards. Replacing the song THROWS AWAY every clip’s offset and every footage score, because they were measured against the old song — re-runalignafterwards. Size- and duration-capped.Returns
song_duration(seconds), the storedsong(name, and theartifact_idto play it by when the project is hosted) and whether an alignment was dropped.- Return type:
- muvid.footage.service.set_span(fp, *, edit_id, start_s, end_s)[source]¶
Choose which part of the song the video covers — where it starts and ends.
Trimming loses nothing. The span is a window on the edit, not a cut of it: every cut is kept whole, and only what is RENDERED is limited to
start_s..``end_s`` (the song cut to match, faded out at the end when it stops before the song does; cuts across an edge are shortened in the render only). Widening the span again —start_s=0andend_s= the song’s length is the whole song — brings back exactly what was there. Returns the edit, with itsspan.- Return type:
- muvid.footage.service.split_cut(fp, *, edit_id, at_s)[source]¶
Split the cut playing at song time
at_sinto two cuts of the same video.The two halves keep the cut’s video, framing and look; a moving framing (a pan) is divided where it was at
at_s. Refused on a boundary (nothing to split). Returns the changed edit;changedis the index of the second half.- Return type:
- muvid.footage.service.status(fp)[source]¶
Where the music video stands: the song, the videos and where each sits on the song, the saved edits, the finished videos, and the next useful step.
alignedlists the clips that have an offset;alignmentssays for each one whether the offset wasmeasured(byalign) ordeclared(set_offset) and whether it is trusted for rendering (reliable).rendersis newest first (the same rowsrendersgives — no server paths).next_stepnames the operation that moves the project forward and why.- Return type:
- muvid.footage.service.strategies(fp=None)[source]¶
The ways to cut on offer — the selection strategies
propose_editaccepts (weightedreads the footage scores; the rest use only the alignment).- Return type:
- muvid.footage.service.timeline(fp)[source]¶
Which videos cover which spans of the song (overlaps shown), from the saved alignment — the map for choosing what to cut to. Run
alignfirst.- Return type:
- muvid.footage.service.undo_edit(fp, *, edit_id)[source]¶
Undo the last change to a saved edit (a cut changed, split, joined, the span, a whole replacement — by a person or by the assistant). Returns the edit as it now is;
redo_editputs the change back. Up to 100 changes are kept per edit.- Return type: