EARHORNMUSIC UPnP · Android TV · Phones · Android Auto · GNU/Linux
Release 0.5.0

LyricsData.kt

69 lines · 2.8 KB

Changed in: 0.5.0

1package dev.stave.lyrics
2
3import kotlinx.serialization.Serializable
4import kotlin.time.Duration
5import kotlin.time.Duration.Companion.milliseconds
6
7/**
8 * Raw response shape from `https://lrclib.net/api/get`. LRCLIB is generous about which
9 * fields it returns — plain-text-only tracks get `syncedLyrics = null`, instrumentals
10 * get `instrumental = true` with both lyrics fields null, tracks it has no record of
11 * return a 404 which we surface as [LyricsResult.NotFound].
12 */
13@Serializable
14data class LrclibResponse(
15 val id: Long? = null,
16 val trackName: String? = null,
17 val artistName: String? = null,
18 val albumName: String? = null,
19 val duration: Double? = null,
20 val instrumental: Boolean = false,
21 val plainLyrics: String? = null,
22 val syncedLyrics: String? = null,
23)
24
25/**
26 * One LRC line = timestamp + text. Timestamp is the start of the line; the line's end
27 * is the next line's start (or the track's duration for the last one). We store as a
28 * Duration rather than raw millis so display code can format with our `toClock()`
29 * helper and math is unambiguous.
30 */
31data class LrcLine(val start: Duration, val text: String) {
32 companion object {
33 fun at(millis: Long, text: String) = LrcLine(millis.milliseconds, text)
34 }
35}
36
37/**
38 * Everything the lyrics UI needs for one track. Synced lyrics give us line-level
39 * highlighting during playback; plain lyrics are shown as a static block. Both may
40 * be present (LRCLIB sometimes returns both); if only plain is present we show that.
41 */
42sealed interface LyricsResult {
43 /** Still fetching from LRCLIB (or from the on-disk cache). */
44 data object Loading : LyricsResult
45
46 /** Track exists in LRCLIB and has lyrics. [synced] is non-empty when the track has
47 * timestamped lyrics; [plain] is the plain-text version (may also be non-null). */
48 data class Loaded(
49 val synced: List<LrcLine>,
50 val plain: String?,
51 ) : LyricsResult
52
53 /** LRCLIB knows this track but the rightsholder marked it instrumental — no lyrics
54 * will ever be returned. UI should say so clearly rather than pretend it's still
55 * searching. */
56 data object Instrumental : LyricsResult
57
58 /** LRCLIB has no record of this track. Happens for obscure titles, misspelled
59 * artists, or anything before LRCLIB's crawl date. */
60 data object NotFound : LyricsResult
61
62 /** User has turned off external calls in privacy settings. We never contacted
63 * LRCLIB at all — the UI must say that, not pretend we tried and found nothing.
64 * Toggling the setting back on triggers a fresh fetch via [LyricsViewModel.refresh]. */
65 data object PrivacyDisabled : LyricsResult
66
67 /** Network / parse / anything else went sideways. Rare but worth surfacing. */
68 data class Error(val message: String) : LyricsResult
69}