navidrome/tests
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
..
fixtures feat(subsonic): add structured sidecar lyrics support with OpenSubsonic v2 karaoke cues and agent layers (#5076) 2026-06-19 12:00:58 -04:00
fake_http_client.go Retry calls to Last.FM without MBIDs when if returns artist invalid (#1138) 2021-05-27 20:53:24 -04:00
init_tests.go Allow configuring cache folder (#2357) 2023-06-02 17:14:11 -04:00
mock_album_repo.go feat(subsonic): sort search3 results by relevance (#5086) 2026-02-23 08:51:54 -05:00
mock_artist_repo.go feat(subsonic): sort search3 results by relevance (#5086) 2026-02-23 08:51:54 -05:00
mock_data_store.go refactor: move playlist business logic from repositories to service layer (#5027) 2026-02-21 19:57:13 -05:00
mock_ffmpeg.go fix(transcoding): include ffprobe in MSI and fall back gracefully when absent (#5326) 2026-04-07 20:11:38 -04:00
mock_genre_repo.go refactor: external_metadata -> external.Provider (#3903) 2025-04-08 21:11:09 -04:00
mock_library_repo.go refactor: run Go modernize (#5002) 2026-02-08 09:57:30 -05:00
mock_library_service.go feat(plugins): New Plugin System with multi-language PDK support (#4833) 2026-01-14 19:22:48 -05:00
mock_mediafile_repo.go perf(subsonic): speed up getRandomSongs with two-phase random-rowid selection (#5618) 2026-06-15 16:23:39 -04:00
mock_playlist_repo.go fix(playlists): allow toggling auto-import and avoid unnecessary artwork reloads (#5421) 2026-04-27 12:20:27 -04:00
mock_playlist_track_repo.go refactor: move playlist business logic from repositories to service layer (#5027) 2026-02-21 19:57:13 -05:00
mock_playqueue_repo.go feat(server): add update and clear play queue endpoints to native API (#4215) 2025-06-11 12:02:31 -04:00
mock_plugin_manager.go feat(plugins): allow mounting library directories as read-write (#5122) 2026-02-28 10:59:13 -05:00
mock_plugin_repo.go fix(plugins): clear plugin errors on startup to allow retrying 2026-03-02 08:56:56 -05:00
mock_property_repo.go refactor: external_metadata -> external.Provider (#3903) 2025-04-08 21:11:09 -04:00
mock_radio_repository.go feat(ui): add cover art support for internet radio stations (#5229) 2026-03-18 18:57:33 -04:00
mock_scanner.go fix(server): return correct scanType in startScan response (#5159) 2026-03-09 14:19:53 -04:00
mock_scrobble_buffer_repo.go feat(ui): add EnableNowPlaying configuration (default true) (#4219) 2025-06-13 00:06:08 -04:00
mock_scrobble_repo.go feat(server): track scrobble/linstens history (#4770) 2025-12-06 11:07:18 -05:00
mock_share_repo.go fix(sharing): validate JWT expiration and share existence on stream endpoint (#5426) 2026-04-27 19:36:57 -04:00
mock_transcoding_repo.go fix(transcoding): place -ss before -i for fast input seeking (#5492) 2026-05-13 17:17:20 -03:00
mock_user_props_repo.go refactor: external_metadata -> external.Provider (#3903) 2025-04-08 21:11:09 -04:00
mock_user_repo.go fix(scanner): import playlists skipped when no admin existed yet (#5609) 2026-06-14 13:39:16 -04:00
mock_user_service.go feat(plugins): New Plugin System with multi-language PDK support (#4833) 2026-01-14 19:22:48 -05:00
navidrome-test.toml feat(plugins): experimental support for plugins (#3998) 2025-06-22 20:45:38 -04:00
test_helpers.go ci: run Go tests on Windows (#5380) 2026-04-19 13:16:47 -04:00