package agents import ( "context" "net/http" "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/log" "github.com/navidrome/navidrome/utils/lastfm" ) const ( lastFMAgentName = "lastfm" lastFMAPIKey = "c2918986bf01b6ba353c0bc1bdd27bea" //lastFMAPISecret = "3ff2aa214a6d8f2242515083bbb70e79" // Will be needed when implementing Scrobbling ) type lastfmAgent struct { ctx context.Context apiKey string lang string client *lastfm.Client } func lastFMConstructor(ctx context.Context) Interface { l := &lastfmAgent{ ctx: ctx, lang: conf.Server.LastFM.Language, } if conf.Server.LastFM.ApiKey != "" { l.apiKey = conf.Server.LastFM.ApiKey } else { l.apiKey = lastFMAPIKey } hc := NewCachedHTTPClient(http.DefaultClient, consts.DefaultCachedHttpClientTTL) l.client = lastfm.NewClient(l.apiKey, l.lang, hc) return l } func (l *lastfmAgent) AgentName() string { return lastFMAgentName } func (l *lastfmAgent) GetMBID(id string, name string) (string, error) { a, err := l.callArtistGetInfo(name, "") if err != nil { return "", err } if a.MBID == "" { return "", ErrNotFound } return a.MBID, nil } func (l *lastfmAgent) GetURL(id, name, mbid string) (string, error) { a, err := l.callArtistGetInfo(name, mbid) if err != nil { return "", err } if a.URL == "" { return "", ErrNotFound } return a.URL, nil } func (l *lastfmAgent) GetBiography(id, name, mbid string) (string, error) { a, err := l.callArtistGetInfo(name, mbid) if err != nil { return "", err } if a.Bio.Summary == "" { return "", ErrNotFound } return a.Bio.Summary, nil } func (l *lastfmAgent) GetSimilar(id, name, mbid string, limit int) ([]Artist, error) { resp, err := l.callArtistGetSimilar(name, mbid, limit) if err != nil { return nil, err } if len(resp) == 0 { return nil, ErrNotFound } var res []Artist for _, a := range resp { res = append(res, Artist{ Name: a.Name, MBID: a.MBID, }) } return res, nil } func (l *lastfmAgent) GetTopSongs(id, artistName, mbid string, count int) ([]Song, error) { resp, err := l.callArtistGetTopTracks(artistName, mbid, count) if err != nil { return nil, err } if len(resp) == 0 { return nil, ErrNotFound } var res []Song for _, t := range resp { res = append(res, Song{ Name: t.Name, MBID: t.MBID, }) } return res, nil } func (l *lastfmAgent) callArtistGetInfo(name string, mbid string) (*lastfm.Artist, error) { a, err := l.client.ArtistGetInfo(l.ctx, name, mbid) lfErr, isLastFMError := err.(*lastfm.Error) if mbid != "" && (err == nil && a.Name == "[unknown]") || (isLastFMError && lfErr.Code == 6) { log.Warn(l.ctx, "LastFM/artist.getInfo could not find artist by mbid, trying again", "artist", name, "mbid", mbid) return l.callArtistGetInfo(name, "") } if err != nil { log.Error(l.ctx, "Error calling LastFM/artist.getInfo", "artist", name, "mbid", mbid, err) return nil, err } return a, nil } func (l *lastfmAgent) callArtistGetSimilar(name string, mbid string, limit int) ([]lastfm.Artist, error) { s, err := l.client.ArtistGetSimilar(l.ctx, name, mbid, limit) lfErr, isLastFMError := err.(*lastfm.Error) if mbid != "" && (err == nil && s.Attr.Artist == "[unknown]") || (isLastFMError && lfErr.Code == 6) { log.Warn(l.ctx, "LastFM/artist.getSimilar could not find artist by mbid, trying again", "artist", name, "mbid", mbid) return l.callArtistGetSimilar(name, "", limit) } if err != nil { log.Error(l.ctx, "Error calling LastFM/artist.getSimilar", "artist", name, "mbid", mbid, err) return nil, err } return s.Artists, nil } func (l *lastfmAgent) callArtistGetTopTracks(artistName, mbid string, count int) ([]lastfm.Track, error) { t, err := l.client.ArtistGetTopTracks(l.ctx, artistName, mbid, count) lfErr, isLastFMError := err.(*lastfm.Error) if mbid != "" && (err == nil && t.Attr.Artist == "[unknown]") || (isLastFMError && lfErr.Code == 6) { log.Warn(l.ctx, "LastFM/artist.getTopTracks could not find artist by mbid, trying again", "artist", artistName, "mbid", mbid) return l.callArtistGetTopTracks(artistName, "", count) } if err != nil { log.Error(l.ctx, "Error calling LastFM/artist.getTopTracks", "artist", artistName, "mbid", mbid, err) return nil, err } return t.Track, nil } func init() { conf.AddHook(func() { if conf.Server.LastFM.Enabled { Register(lastFMAgentName, lastFMConstructor) } }) }