muvid.lyrics

Lyrics — transcription and markdown round-trip.

Two surfaces for the user:

  1. transcribe(audio_path) — calls mixing.transcript.transcribe (ElevenLabs Scribe) and writes the raw word-timestamped JSON to lyrics/transcript.json. This is the seed; the user is expected to correct it.

  2. write_lyrics_md / parse_lyrics_md — the canonical, editable form. A simple markdown with [section] headers, one line per sung line, and an optional // <seconds> end-of-line anchor.

The alignment module consumes both: it reads lyrics.md for the text the user committed to, and transcript.json for the timing to splice in.

class muvid.lyrics.LyricLine(*, text: str, line_index: int, section_label: str = '', start_s: float | None = None)[source]

One line of lyric text, optionally with a known start time.

class muvid.lyrics.LyricSection(*, label: str, title: str = '', start_s: float | None = None, end_s: float | None = None, lines: tuple[LyricLine, ...] = ())[source]

A user-tagged section in the lyrics markdown.

Times are optional — if not present, alignment is computed from transcripts; if present, they override.

class muvid.lyrics.LyricsDoc(*, sections: tuple[LyricSection, ...])[source]

Full parsed view of the user’s lyrics markdown.

muvid.lyrics.lyrics_from_transcript(transcript: dict[str, Any]) LyricsDoc[source]

Build a default LyricsDoc from a transcription response.

Heuristic: split lines on punctuation (. ? !) or on long pauses (>0.6 s gap between consecutive words). One [transcribed] section holds everything; the user is expected to re-tag with real sections.

muvid.lyrics.parse_lyrics_md(md: str) LyricsDoc[source]

Parse the user-editable lyrics markdown.

Format:

[section_label] optional title
line of lyric            // 12.5
another line

[next section]
...

Empty lines are separators between sections. (instrumental) or any line starting with ( and ending with ) is treated as a non-lyric placeholder (no LyricLine emitted).

muvid.lyrics.render_lyrics_md(doc: LyricsDoc) str[source]

Inverse of parse_lyrics_md. Stable round-trip.

muvid.lyrics.transcribe(audio_path: str | Path, *, api_key: str | None = None, out_path: str | Path | None = None, cache: bool | str | Path = True) dict[str, Any][source]

Run ElevenLabs Scribe on the audio and (optionally) write the JSON.

Returns the raw response dict (which contains words: [...] with per-word text, start, end, confidence).

The on-disk Scribe cache (in mixing.transcript) is enabled by default, so re-running on the same audio is free. Pass cache=False to force a fresh round-trip.

muvid.lyrics.words_from_transcript(transcript: dict[str, Any]) list[dict[str, Any]][source]

Normalize Scribe’s word entries: [{text, start, end, confidence}, ...].

Filters out non-word events (Scribe surfaces (laughs) etc. with typeword) and ones missing timing. confidence is pass-through; absent → None.