muvid.lyricvid.pipeline¶
The lyric-video pipeline — the one path from a song to a finished video.
- audio (+ optional lyrics / subtitles / project)
-> TimedText measured word times (muvid.lyricvid.timed_text) -> TreatmentSpec the creative decision (muvid.lyricvid.director) -> Scene every number computed (muvid.lyricvid.scene) -> mp4 a renderer (render_ass | render_web)
Each arrow is a seam with a working default, so the whole thing runs on a bare
song.wav with no lyrics, no AI, no API key and no network — and every stage
can be replaced without touching its neighbours.
The renderer seam defaults to auto, which picks ASS where this ffmpeg can
burn subtitles in (frame-exact, no browser, and the .ass file it leaves behind
is an editable deliverable in its own right) and web otherwise. Asking for a
backend by name never falls back – it fails loudly.
Module Attributes
function", resolved lazily so listing costs no import. |
Functions
|
Register a renderer backend as |
|
Import and return a renderer backend. |
|
Resolve |
|
Get measured word times from whichever input the caller actually has. |
|
Render one lyric video. |
- muvid.lyricvid.pipeline.RENDERERS: dict[str, str] = {'ass': 'muvid.lyricvid.render_ass:render', 'web': 'muvid.lyricvid.render_web:render'}¶
function”, resolved lazily so listing costs no import.
- Type:
name -> “module
- muvid.lyricvid.pipeline.build_timed_text(*, audio, lyrics=None, subtitles=None, project=None, aligner=None, timed_text=None)[source]¶
Get measured word times from whichever input the caller actually has.
Order of preference is by how much the input is trusted: a saved timing file (
TimedText.to_dictJSON — e.g. one refined to per-glyph times bymuvid.lyricvid.glyph_align) is used as is; then an existing muvid alignment beats a subtitle file, which beats aligning lyrics ourselves, which beats transcribing from nothing.- Return type:
- muvid.lyricvid.pipeline.register_renderer(name, target)[source]¶
Register a renderer backend as
"module:function".The house lazy-registry idiom (cf.
muvid.footage.strategy): the target is a string so adding a backend costs the import path nothing.- Return type:
>>> register_renderer('doctest-demo', 'muvid.lyricvid.render_ass:render') >>> 'doctest-demo' in RENDERERS True >>> del RENDERERS['doctest-demo']
- muvid.lyricvid.pipeline.render(request)[source]¶
Render one lyric video. Satisfies
muvid.subgenres.Renderer.request.inputstakesaudio(required) and any oflyrics,subtitles,project.request.paramstakestreatment(a treatment spec as a mapping, or omitted to have one proposed),renderer,width,height,fps,alignerandpersona, andcaptions—{language: transform}(seemuvid.lyricvid.captions), each written beside the video as an.srtartifactcaptions.<language>.- Return type:
- muvid.lyricvid.pipeline.resolve_renderer(name)[source]¶
Import and return a renderer backend. Accepts a callable unchanged.
- Return type:
- muvid.lyricvid.pipeline.select_renderer(name='auto')[source]¶
Resolve
"auto"to a backend that can actually run here.autois a selector, not a registered renderer — the same shape asvisual="auto"inmuvid.visualize. It picksasswhen this ffmpeg can burn subtitles in (it needs a libass-enabled build) andwebotherwise.This is deliberately not a fallback inside
ass. Asking forasson a build without libass fails loudly, because a renderer that silently produces something other than what was asked for is the exact shape muvid has been bitten by before.autois the caller opting in to “whichever works”, and the choice it made is recorded in the result’smeta.- Return type:
>>> select_renderer('web') 'web' >>> select_renderer('auto') in {'ass', 'web'} True