navidrome/model
Yuuta 3a14faa033
feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076)
Expand backend lyrics support with richer sidecar formats and upgrade the
OpenSubsonic songLyrics implementation to the version 2 structured karaoke
contract, while preserving version 1 behavior by default.

Sidecar formats and parsing:
- Add a TTML parser (core/lyrics/ttml.go): clock time, offset time, bare
  decimal seconds, nested timing contexts, and token-level <span> timing for
  word/syllable karaoke. Parses Apple Music-style metadata tracks (translation
  and pronunciation/transliteration) and agent metadata into per-track agents[]
  plus per-cue-line agentId. Hydrates missing line timing from cue timing.
- Add an SRT parser (core/lyrics/srt.go).
- Add a LRCLIB Lyricsfile (.yaml/.yml) parser (model/lyricsfile.go): maps
  per-word lines[].words[] to cues with inclusive UTF-8 byte offsets and
  attributes overlapping lines to synthetic voice agents so parallel vocals
  split correctly in the enhanced response.
- Extend LRC parsing for Enhanced LRC inline <mm:ss.xx> word-timing markers.
- Add UTF-8 BOM and UTF-16 LE support for TTML/LRC sidecars.
- Parse the above formats from embedded tags as well as sidecar files.

Source resolution:
- Default lyricspriority is now
  ".ttml,.yaml,.yml,.elrc,.lrc,.srt,.txt,embedded" so the new formats are
  discoverable without manual configuration.
- Preserve configured source priority across duplicate media-file candidates
  instead of only checking the first DB match, so higher-priority sidecar
  lyrics on older duplicates can still win.
- Raise the embedded-lyrics tag maxLength to 1 MB to fit word-timed
  TTML/Enhanced-LRC karaoke for a full song.

OpenSubsonic songLyrics v2:
- Advertise songLyrics versions [1, 2].
- With enhanced=true, getLyricsBySongId may return structuredLyrics.kind
  (main/translation/pronunciation), cueLine[] line-level karaoke groupings,
  cueLine.cue[] timed words/syllables with required UTF-8 byteStart/byteEnd,
  reusable structuredLyrics.agents[], and cueLine.agentId references.
- Without enhanced=true, the response stays v1-compatible: no kind, no cueLine,
  no agents, no non-main tracks; the existing line[] payload is always
  populated so legacy clients keep working.

Contract details:
- cueLine is emitted only for synced lyrics with cue data.
- Within a cueLine, cue.end is normalized all-or-none and overlaps are removed;
  overlaps across separate cueLines remain valid for parallel vocal layers.
- Missing cue end-times are filled from the next cue or the parent line.
- When cueLines share an index, the one whose agent has role "main" is first.
- LyricCue.Value is serialized as XML chardata; cues with nil start are skipped
  rather than serialized as 0.

Refactoring:
- Move pure format parsers into model/ (lyrics.go, lyrics_ttml.go,
  lyrics_srt.go, lyrics_embedded.go, lyricsfile.go) and extract Subsonic
  response building into server/subsonic/lyrics.go.
- Centralize lyric-kind constants and add Lyrics.EffectiveKind/IsMainKind.
- Add gg.Clone helper.

Spec references:
  https://github.com/opensubsonic/open-subsonic-api/discussions/213
  https://github.com/opensubsonic/open-subsonic-api/pull/218 (songLyrics v2)
  https://github.com/opensubsonic/open-subsonic-api/pull/228 (cue byte offsets)
2026-06-19 12:00:58 -04:00
..
criteria feat(smartplaylist): extend isMissing/isPresent to bpm, bitDepth and many text fields (#5603) 2026-06-13 13:15:20 -04:00
id refactor(nanoid): replace gonanoid with custom nanoid implementation for ID generation 2026-03-13 21:06:26 -04:00
metadata feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
request fix(scanner) artist stats not refreshing during quick scan and after missing file deletion (#4269) 2025-06-26 15:50:56 -04:00
album.go feat(subsonic): append album version to names in Subsonic API (#5111) 2026-02-26 10:50:12 -05:00
album_test.go feat(subsonic): append album version to names in Subsonic API (#5111) 2026-02-26 10:50:12 -05:00
annotation.go feat(subsonic): Add avgRating from subsonic spec (#4900) 2026-01-18 17:42:42 -05:00
artist.go feat: add artist image uploads and image-folder artwork source (#5198) 2026-03-15 22:19:55 -04:00
artist_info.go Get Similar Artists in parallel 2020-10-21 21:44:03 -04:00
artist_test.go refactor(conf): replace eager dir creation with lazy Dir type (#5495) 2026-05-13 17:44:22 -03:00
artwork_id.go feat(ui): add cover art support for internet radio stations (#5229) 2026-03-18 18:57:33 -04:00
artwork_id_test.go refactor: multiple syntax updates for Go 1.26 2026-05-19 18:02:36 -03:00
bookmark.go Use structs lib to map models to DB. Fix #1266 2021-08-01 12:04:45 -04:00
datastore.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
errors.go feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
file_types.go Fix M3U mimetype on Debian Bullseye 2024-05-09 22:26:15 -04:00
file_types_test.go Refactor file type functions 2022-12-28 15:31:56 -05:00
folder.go fix(scanner): import playlists skipped when no admin existed yet (#5609) 2026-06-14 13:39:16 -04:00
folder_test.go ci: run Go tests on Windows (#5380) 2026-04-19 13:16:47 -04:00
genre.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
get_entity.go feat(ui): add cover art support for internet radio stations (#5229) 2026-03-18 18:57:33 -04:00
image.go refactor(conf): replace eager dir creation with lazy Dir type (#5495) 2026-05-13 17:44:22 -03:00
library.go feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
lyrics.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyrics_embedded.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyrics_embedded_test.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyrics_srt.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyrics_test.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyrics_ttml.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyrics_ttml_test.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyricsfile.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
lyricsfile_test.go feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
mediafile.go perf(subsonic): speed up getRandomSongs with two-phase random-rowid selection (#5618) 2026-06-15 16:23:39 -04:00
mediafile_internal_test.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
mediafile_test.go feat(smartplaylist): extend isMissing/isPresent to bpm, bitDepth and many text fields (#5603) 2026-06-13 13:15:20 -04:00
model_suite_test.go Rename log.LevelCritical to log.LevelFatal 2022-12-21 14:53:36 -05:00
participants.go feat(server): expose main credit stat to reflect only album artist | artist credit (#4268) 2025-06-28 19:00:13 -04:00
participants_test.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
player.go chore: remove more outdated TODOs 2025-03-23 11:53:43 -04:00
playlist.go fix(playlists): allow toggling auto-import and avoid unnecessary artwork reloads (#5421) 2026-04-27 12:20:27 -04:00
playlist_test.go ci: run Go tests on Windows (#5380) 2026-04-19 13:16:47 -04:00
playqueue.go feat(server): add update and clear play queue endpoints to native API (#4215) 2025-06-11 12:02:31 -04:00
plugin.go fix(plugins): clear plugin errors on startup to allow retrying 2026-03-02 08:56:56 -05:00
properties.go Associate main entities with library 2024-05-12 21:37:42 -04:00
radio.go feat(ui): add cover art support for internet radio stations (#5229) 2026-03-18 18:57:33 -04:00
radio_test.go refactor(conf): replace eager dir creation with lazy Dir type (#5495) 2026-05-13 17:44:22 -03:00
scanner.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
scanner_test.go feat(scanner): implement selective folder scanning and file system watcher improvements (#4674) 2025-11-14 22:15:43 -05:00
scrobble.go feat(server): track scrobble/linstens history (#4770) 2025-12-06 11:07:18 -05:00
scrobble_buffer.go Fix scrobble error spam in the logs. 2024-04-26 16:59:14 -04:00
searchable.go feat(subsonic): sort search3 results by relevance (#5086) 2026-02-23 08:51:54 -05:00
share.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
tag.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
tag_mappings.go chore: go fix 2026-05-28 22:13:05 -03:00
tag_mappings_test.go fix(scanner): respect tag split config when multiple frames map to the same tag (#5193) 2026-05-23 19:20:18 -03:00
tag_test.go feat(bfr): Big Refactor: new scanner, lots of new fields and tags, improvements and DB schema changes (#2709) 2025-02-19 20:35:17 -05:00
transcoding.go Replace beego/orm with dbx (#2693) 2023-12-09 13:52:17 -05:00
user.go chore(deps): bump golangci-lint to v2.10.0 and suppress new gosec false positives 2026-02-17 09:28:42 -05:00
user_props.go Pass userId explicitly to UserPropsRepository methods 2021-06-25 22:21:37 -04:00
user_test.go feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00