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

RENDERERS

function", resolved lazily so listing costs no import.

Functions

register_renderer(name, target)

Register a renderer backend as "module:function".

resolve_renderer(name)

Import and return a renderer backend.

select_renderer([name])

Resolve "auto" to a backend that can actually run here.

build_timed_text(*, audio[, lyrics, ...])

Get measured word times from whichever input the caller actually has.

render(request)

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_dict JSON — e.g. one refined to per-glyph times by muvid.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:

TimedText

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:

None

>>> 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.inputs takes audio (required) and any of lyrics, subtitles, project. request.params takes treatment (a treatment spec as a mapping, or omitted to have one proposed), renderer, width, height, fps, aligner and persona, and captions — {language: transform} (see muvid.lyricvid.captions), each written beside the video as an .srt artifact captions.<language>.

Return type:

RenderResult

muvid.lyricvid.pipeline.resolve_renderer(name)[source]

Import and return a renderer backend. Accepts a callable unchanged.

Return type:

Callable

muvid.lyricvid.pipeline.select_renderer(name='auto')[source]

Resolve "auto" to a backend that can actually run here.

auto is a selector, not a registered renderer — the same shape as visual="auto" in muvid.visualize. It picks ass when this ffmpeg can burn subtitles in (it needs a libass-enabled build) and web otherwise.

This is deliberately not a fallback inside ass. Asking for ass on 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. auto is the caller opting in to “whichever works”, and the choice it made is recorded in the result’s meta.

Return type:

str

>>> select_renderer('web')
'web'
>>> select_renderer('auto') in {'ass', 'web'}
True