feat: add media-lyrics plugin (synced lyrics media panel) - #592
Conversation
Karaoke-style time-synced lyrics panel for Noctalia: 14-line lyric carousel, album cover, transport controls, progress bar, marquee titles. Pure Luau, no playerctl/python/GTK — MPRIS via busctl, lyrics from LRCLIB with local .lrc support and on-disk cache.
…roll, absolute seek fix) - Clickable lyric lines: click or Return/Space seeks to the line timestamp - Manual lyric scroll: Up/Down cursor with chevron marker (capture_keys + onKey, keyboard_focus = exclusive) - FIX: busctlCall dropped D-Bus signature — seek/shuffle/loop silently failed; types (b/s/x) now passed, seek uses SetPositionActive (absolute) - FIX: bar widget empty state renders a disc tree instead of setGlyph/setText (dead after first render()) - version 0.8.5
|
… declare curl dep, panel strings via tr(), official thumbnail)
…eftovers, network error surfacing, nil-child fix, UTF-8 cuts, docs)
|
Thanks for the review — all three points are addressed. Summary of what changed since your pass (media-lyrics is now at v0.8.13 in this branch): 0 (blocking) — thumbnail 2 (non-blocking) — curl/sleep accounting
3 (non-blocking) — i18n
Extra hardening in this branch (0.8.11–0.8.13)
Both CI checks ( |
Plugin
tranzem/media-lyricsplugin.toml)What it does
Karaoke-style time-synced lyrics media panel for the Noctalia shell. Shows the
currently playing track (album cover, marquee title/artist, transport controls,
progress bar) and a synced-lyrics carousel (10/14/16 lines per size preset)
where the active line is
highlighted and neighbours fade by distance.
overlays. Reads MPRIS via Noctalia's own D-Bus aggregator; the only runtime
binaries are
busctl(MPRIS poll) andcurl(LRCLIB HTTPS fetch, see below).are soft-wrapped into balanced sub-lines; the karaoke window fits a vertical
sub-line budget so the active line is never pushed below the panel edge.
/api/getlookup first,/api/searchfallback,LRC parsed in pure Luau. Fetched with
curl -sSf -m 8 -4 <url>(argv-only,no shell; IPv4 forced because the built-in HTTP client times out against
lrclib.net on some setups).
keyboard cursor) to seek the player to that line's timestamp; a chevron
marker highlights the selected line. Up/Down scroll the lyrics manually.
.lrcfiles — take priority over the network (offline-friendly);LRCLIB responses are cached on disk (opt-in).
noctalia.tr()(Englishships in
translations/en.json; other locales arrive via NoctaliaTranslate), with lyric timing offset, cache and lyrics-folder settings.
now-playing(click → panel, middle-click → play/pause,scroll → next/prev), panel
panel(520×520, floating), serviceservice,shortcut
toggle.External dependencies
busctl— polls the MPRIS D-Bus aggregator (dev.noctalia.Mpris) at a150 ms cadence. Declared in
dependencies = ["busctl", "curl"]inplugin.toml.curl— LRCLIB HTTPS fetch (curl -sSf -m 8 -4 <url>, argv-form, noshell, no stored config). Declared in
dependenciesand README Requirements.sleep(coreutils) — 0.35 s refresh delay after transport commands(argv-form, no shell); documented in README Notes.
https://lrclib.net— HTTPS network calls only:/api/get(exact match),then
/api/search(fallback). Nothing is uploaded; no analytics, no tracking.use_cachesetting); local.lrcfiles read fromlocal_lyrics_dir(default
~/.local/share/media-lyrics).busctl,curl,sleep— all argv-form, no shell.Testing
Opened the panel (
noctalia msg panel-toggle tranzem/media-lyrics:panel) ontracks with synced and plain lyrics — both render correctly.
Clickable lines: seek verified live via MPRIS position — 3s → 136s jump to
the target line's timestamp on a synced track.
Keyboard: Up/Down move the lyric cursor (chevron marker), Return/Space seek;
plain lyrics scroll but don't seek (no timestamps).
Bar widget
now-playing: click opens panel, middle click toggles play/pause,scroll switches tracks (
[widget.actions] middle = "none"declared).Settings toggled:
offset_ms,use_cache,local_lyrics_dir.luac -ppasses on every entry script.Community review round 1 (2026-09-02):
curldeclared independenciesand README; plugin description no longer claims "no external dependencies";
panel strings wrapped in
noctalia.tr()(keys added toen.json);thumbnail regenerated with the official thumbnail generator (960×540 WebP).
Long-line regression test (v0.8.11): 47/56/64/72-char lyric fixtures on the
compact preset wrap into balanced sub-lines with no right-edge clipping;
pixel-verified that the active line stays fully above the panel edge.
Review-hardening round 2 (v0.8.13): widget/provider/error strings via
noctalia.tr(); transport failures surface a real error state (verified bycode path — curl exit ≠ 0/22); UTF-8 Cyrillic fixture renders with no
mojibake and no "ui tree node is not a table" warnings (WRN delta 0 on
synced and plain tracks).
Tested on Niri
Tested on Hyprland
Tested on Sway
Tested on another compositor: Umbriel (Wayland)
Noctalia version tested against: v5 beta (5.0.0_beta.10)
Plugin API level: 24
Screenshots / Videos
Checklist
idafter the/inplugin.tomlexactly.plugin.toml,README.md,thumbnail.webp, andtranslations/en.json.README.mdfollows theREADME template, documents
every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable.
thumbnail.webpwith the thumbnail generator.versionfollows semver and is bumped in this PR;plugin_apiis the oldest API level this plugin requires.understand that language well enough to review and maintain it (no unreviewed machine/LLM translations).
catalog.toml; CI generates it.Code review attestation
licensedeclared inplugin.toml.