From d4a053370a7ee8471b5803fe0ddc89582f6332db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deluan=20Quint=C3=A3o?= Date: Wed, 28 May 2025 08:43:07 -0400 Subject: [PATCH] feat(server): add option `Lastfm.ScrobbleFirstArtistOnly` to send only the first artist (#4131) fixes #3791 Signed-off-by: Deluan --- conf/configuration.go | 10 ++++++---- core/agents/lastfm/agent.go | 11 +++++++++-- core/agents/lastfm/agent_test.go | 23 +++++++++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/conf/configuration.go b/conf/configuration.go index 8561f343..67f43294 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -154,10 +154,11 @@ type TagConf struct { } type lastfmOptions struct { - Enabled bool - ApiKey string - Secret string - Language string + Enabled bool + ApiKey string + Secret string + Language string + ScrobbleFirstArtistOnly bool } type spotifyOptions struct { @@ -528,6 +529,7 @@ func setViperDefaults() { viper.SetDefault("lastfm.language", "en") viper.SetDefault("lastfm.apikey", "") viper.SetDefault("lastfm.secret", "") + viper.SetDefault("lastfm.scrobblefirstartistonly", false) viper.SetDefault("spotify.id", "") viper.SetDefault("spotify.secret", "") viper.SetDefault("listenbrainz.enabled", true) diff --git a/core/agents/lastfm/agent.go b/core/agents/lastfm/agent.go index 3f5f44d2..ec732f17 100644 --- a/core/agents/lastfm/agent.go +++ b/core/agents/lastfm/agent.go @@ -279,6 +279,13 @@ func (l *lastfmAgent) callArtistGetTopTracks(ctx context.Context, artistName str return t.Track, nil } +func (l *lastfmAgent) getArtistForScrobble(track *model.MediaFile) string { + if conf.Server.LastFM.ScrobbleFirstArtistOnly && len(track.Participants[model.RoleArtist]) > 0 { + return track.Participants[model.RoleArtist][0].Name + } + return track.Artist +} + func (l *lastfmAgent) NowPlaying(ctx context.Context, userId string, track *model.MediaFile) error { sk, err := l.sessionKeys.Get(ctx, userId) if err != nil || sk == "" { @@ -286,7 +293,7 @@ func (l *lastfmAgent) NowPlaying(ctx context.Context, userId string, track *mode } err = l.client.updateNowPlaying(ctx, sk, ScrobbleInfo{ - artist: track.Artist, + artist: l.getArtistForScrobble(track), track: track.Title, album: track.Album, trackNumber: track.TrackNumber, @@ -312,7 +319,7 @@ func (l *lastfmAgent) Scrobble(ctx context.Context, userId string, s scrobbler.S return nil } err = l.client.scrobble(ctx, sk, ScrobbleInfo{ - artist: s.Artist, + artist: l.getArtistForScrobble(&s.MediaFile), track: s.Title, album: s.Album, trackNumber: s.TrackNumber, diff --git a/core/agents/lastfm/agent_test.go b/core/agents/lastfm/agent_test.go index de4fac6d..8790f032 100644 --- a/core/agents/lastfm/agent_test.go +++ b/core/agents/lastfm/agent_test.go @@ -196,6 +196,12 @@ var _ = Describe("lastfmAgent", func() { TrackNumber: 1, Duration: 180, MbzRecordingID: "mbz-123", + Participants: map[model.Role]model.ParticipantList{ + model.RoleArtist: []model.Participant{ + {Artist: model.Artist{ID: "ar-1", Name: "First Artist"}}, + {Artist: model.Artist{ID: "ar-2", Name: "Second Artist"}}, + }, + }, } }) @@ -247,6 +253,23 @@ var _ = Describe("lastfmAgent", func() { Expect(sentParams.Get("timestamp")).To(Equal(strconv.FormatInt(ts.Unix(), 10))) }) + When("ScrobbleFirstArtistOnly is true", func() { + BeforeEach(func() { + conf.Server.LastFM.ScrobbleFirstArtistOnly = true + }) + + It("uses only the first artist", func() { + ts := time.Now() + httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString("{}")), StatusCode: 200} + + err := agent.Scrobble(ctx, "user-1", scrobbler.Scrobble{MediaFile: *track, TimeStamp: ts}) + + Expect(err).ToNot(HaveOccurred()) + sentParams := httpClient.SavedRequest.URL.Query() + Expect(sentParams.Get("artist")).To(Equal("First Artist")) + }) + }) + It("skips songs with less than 31 seconds", func() { track.Duration = 29 httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString("{}")), StatusCode: 200}