From ea1d78ed589a7e21e81f52c510922534a61d20bf Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Tue, 5 Mar 2024 08:53:57 -0800 Subject: [PATCH] add LyricsProvider / GetLyrics --- backend/mediaprovider/mediaprovider.go | 4 ++ backend/mediaprovider/model.go | 12 +++++ .../subsonic/subsonicmediaprovider.go | 48 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/backend/mediaprovider/mediaprovider.go b/backend/mediaprovider/mediaprovider.go index f7fe0b1..fde5fb7 100644 --- a/backend/mediaprovider/mediaprovider.go +++ b/backend/mediaprovider/mediaprovider.go @@ -154,6 +154,10 @@ type SupportsSharing interface { CanShareArtists() bool } +type LyricsProvider interface { + GetLyrics(track *Track) (*Lyrics, error) +} + type JukeboxProvider interface { JukeboxStart() error JukeboxStop() error diff --git a/backend/mediaprovider/model.go b/backend/mediaprovider/model.go index ac4201a..d7a820f 100644 --- a/backend/mediaprovider/model.go +++ b/backend/mediaprovider/model.go @@ -120,6 +120,18 @@ type PlaylistWithTracks struct { Tracks []*Track } +type Lyrics struct { + Title string + Artist string + Synced bool + Lines []LyricLine +} + +type LyricLine struct { + Text string + Start float64 // seconds +} + type ContentType int const ( diff --git a/backend/mediaprovider/subsonic/subsonicmediaprovider.go b/backend/mediaprovider/subsonic/subsonicmediaprovider.go index 5c858eb..4470bf7 100644 --- a/backend/mediaprovider/subsonic/subsonicmediaprovider.go +++ b/backend/mediaprovider/subsonic/subsonicmediaprovider.go @@ -6,6 +6,7 @@ import ( "io" "math" "net/url" + "slices" "strconv" "strings" "sync" @@ -345,6 +346,53 @@ func (s *subsonicMediaProvider) RescanLibrary() error { return err } +// LyricsProvider interface +var _ mediaprovider.LyricsProvider = (*subsonicMediaProvider)(nil) + +func (s *subsonicMediaProvider) GetLyrics(track *mediaprovider.Track) (*mediaprovider.Lyrics, error) { + ext, err := s.client.GetOpenSubsonicExtensions() + supportsSynced := err == nil && + slices.ContainsFunc(ext, func(ext *subsonic.OpenSubsonicExtension) bool { + return ext.Name == subsonic.SongLyricsExtension + }) + if supportsSynced { + lyrics, err := s.client.GetLyricsBySongId(track.ID) + if err != nil || len(lyrics.StructuredLyrics) == 0 { + return nil, err + } + lyric := lyrics.StructuredLyrics[0] + mpLyrics := &mediaprovider.Lyrics{ + Title: lyric.DisplayTitle, + Artist: lyric.DisplayArtist, + Synced: lyric.Synced, + } + for _, line := range lyric.Lines { + mpLyrics.Lines = append(mpLyrics.Lines, mediaprovider.LyricLine{ + Text: line.Text, + Start: float64(line.Start) / 1000, + }) + } + return mpLyrics, nil + } + // fallback to legacy getLyrics endpoint + lyrics, err := s.client.GetLyrics(track.Name, track.ArtistNames[0]) + if err != nil || lyrics == nil || lyrics.Text == "" { + return nil, err + } + mpLyrics := &mediaprovider.Lyrics{ + Title: lyrics.Title, + Artist: lyrics.Artist, + Synced: false, + } + lines := strings.Split(lyrics.Text, "\n") + for _, line := range lines { + mpLyrics.Lines = append(mpLyrics.Lines, mediaprovider.LyricLine{ + Text: line, + }) + } + return mpLyrics, nil +} + func toTrack(ch *subsonic.Child) *mediaprovider.Track { if ch == nil { return nil