Refactor Agents to be singleton

Initial work for Last.fm scrobbler
This commit is contained in:
Deluan
2021-06-22 11:15:51 -04:00
committed by Deluan Quintão
parent f9fa9667a3
commit d5461d0ae9
17 changed files with 289 additions and 212 deletions
+31 -29
View File
@@ -5,145 +5,147 @@ import (
"strings"
"time"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/utils"
)
type Agents struct {
ctx context.Context
ds model.DataStore
agents []Interface
}
func NewAgents(ctx context.Context) *Agents {
func New(ds model.DataStore) *Agents {
order := strings.Split(conf.Server.Agents, ",")
order = append(order, PlaceholderAgentName)
var res []Interface
for _, name := range order {
init, ok := Map[name]
if !ok {
log.Error(ctx, "Agent not available. Check configuration", "name", name)
log.Error("Agent not available. Check configuration", "name", name)
continue
}
res = append(res, init(ctx))
res = append(res, init(ds))
}
return &Agents{ctx: ctx, agents: res}
return &Agents{ds: ds, agents: res}
}
func (a *Agents) AgentName() string {
return "agents"
}
func (a *Agents) GetMBID(id string, name string) (string, error) {
func (a *Agents) GetMBID(ctx context.Context, id string, name string) (string, error) {
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(a.ctx) {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistMBIDRetriever)
if !ok {
continue
}
mbid, err := agent.GetMBID(id, name)
mbid, err := agent.GetMBID(ctx, id, name)
if mbid != "" && err == nil {
log.Debug(a.ctx, "Got MBID", "agent", ag.AgentName(), "artist", name, "mbid", mbid, "elapsed", time.Since(start))
log.Debug(ctx, "Got MBID", "agent", ag.AgentName(), "artist", name, "mbid", mbid, "elapsed", time.Since(start))
return mbid, err
}
}
return "", ErrNotFound
}
func (a *Agents) GetURL(id, name, mbid string) (string, error) {
func (a *Agents) GetURL(ctx context.Context, id, name, mbid string) (string, error) {
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(a.ctx) {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistURLRetriever)
if !ok {
continue
}
url, err := agent.GetURL(id, name, mbid)
url, err := agent.GetURL(ctx, id, name, mbid)
if url != "" && err == nil {
log.Debug(a.ctx, "Got External Url", "agent", ag.AgentName(), "artist", name, "url", url, "elapsed", time.Since(start))
log.Debug(ctx, "Got External Url", "agent", ag.AgentName(), "artist", name, "url", url, "elapsed", time.Since(start))
return url, err
}
}
return "", ErrNotFound
}
func (a *Agents) GetBiography(id, name, mbid string) (string, error) {
func (a *Agents) GetBiography(ctx context.Context, id, name, mbid string) (string, error) {
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(a.ctx) {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistBiographyRetriever)
if !ok {
continue
}
bio, err := agent.GetBiography(id, name, mbid)
bio, err := agent.GetBiography(ctx, id, name, mbid)
if bio != "" && err == nil {
log.Debug(a.ctx, "Got Biography", "agent", ag.AgentName(), "artist", name, "len", len(bio), "elapsed", time.Since(start))
log.Debug(ctx, "Got Biography", "agent", ag.AgentName(), "artist", name, "len", len(bio), "elapsed", time.Since(start))
return bio, err
}
}
return "", ErrNotFound
}
func (a *Agents) GetSimilar(id, name, mbid string, limit int) ([]Artist, error) {
func (a *Agents) GetSimilar(ctx context.Context, id, name, mbid string, limit int) ([]Artist, error) {
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(a.ctx) {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistSimilarRetriever)
if !ok {
continue
}
similar, err := agent.GetSimilar(id, name, mbid, limit)
similar, err := agent.GetSimilar(ctx, id, name, mbid, limit)
if len(similar) >= 0 && err == nil {
log.Debug(a.ctx, "Got Similar Artists", "agent", ag.AgentName(), "artist", name, "similar", similar, "elapsed", time.Since(start))
log.Debug(ctx, "Got Similar Artists", "agent", ag.AgentName(), "artist", name, "similar", similar, "elapsed", time.Since(start))
return similar, err
}
}
return nil, ErrNotFound
}
func (a *Agents) GetImages(id, name, mbid string) ([]ArtistImage, error) {
func (a *Agents) GetImages(ctx context.Context, id, name, mbid string) ([]ArtistImage, error) {
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(a.ctx) {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistImageRetriever)
if !ok {
continue
}
images, err := agent.GetImages(id, name, mbid)
images, err := agent.GetImages(ctx, id, name, mbid)
if len(images) > 0 && err == nil {
log.Debug(a.ctx, "Got Images", "agent", ag.AgentName(), "artist", name, "images", images, "elapsed", time.Since(start))
log.Debug(ctx, "Got Images", "agent", ag.AgentName(), "artist", name, "images", images, "elapsed", time.Since(start))
return images, err
}
}
return nil, ErrNotFound
}
func (a *Agents) GetTopSongs(id, artistName, mbid string, count int) ([]Song, error) {
func (a *Agents) GetTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]Song, error) {
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(a.ctx) {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistTopSongsRetriever)
if !ok {
continue
}
songs, err := agent.GetTopSongs(id, artistName, mbid, count)
songs, err := agent.GetTopSongs(ctx, id, artistName, mbid, count)
if len(songs) > 0 && err == nil {
log.Debug(a.ctx, "Got Top Songs", "agent", ag.AgentName(), "artist", artistName, "songs", songs, "elapsed", time.Since(start))
log.Debug(ctx, "Got Top Songs", "agent", ag.AgentName(), "artist", artistName, "songs", songs, "elapsed", time.Since(start))
return songs, err
}
}
+28 -28
View File
@@ -20,14 +20,14 @@ var _ = Describe("Agents", func() {
var ag *Agents
BeforeEach(func() {
conf.Server.Agents = ""
ag = NewAgents(ctx)
ag = New(ctx)
})
It("calls the placeholder GetBiography", func() {
Expect(ag.GetBiography("123", "John Doe", "mb123")).To(Equal(placeholderBiography))
Expect(ag.GetBiography(ctx, "123", "John Doe", "mb123")).To(Equal(placeholderBiography))
})
It("calls the placeholder GetImages", func() {
images, err := ag.GetImages("123", "John Doe", "mb123")
images, err := ag.GetImages(ctx, "123", "John Doe", "mb123")
Expect(err).ToNot(HaveOccurred())
Expect(images).To(HaveLen(3))
for _, i := range images {
@@ -50,24 +50,24 @@ var _ = Describe("Agents", func() {
}{}
})
conf.Server.Agents = "empty,fake"
ag = NewAgents(ctx)
ag = New(ctx)
Expect(ag.AgentName()).To(Equal("agents"))
})
Describe("GetMBID", func() {
It("returns on first match", func() {
Expect(ag.GetMBID("123", "test")).To(Equal("mbid"))
Expect(ag.GetMBID(ctx, "123", "test")).To(Equal("mbid"))
Expect(mock.Args).To(ConsistOf("123", "test"))
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetMBID("123", "test")
_, err := ag.GetMBID(ctx, "123", "test")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetMBID("123", "test")
_, err := ag.GetMBID(ctx, "123", "test")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
@@ -75,18 +75,18 @@ var _ = Describe("Agents", func() {
Describe("GetURL", func() {
It("returns on first match", func() {
Expect(ag.GetURL("123", "test", "mb123")).To(Equal("url"))
Expect(ag.GetURL(ctx, "123", "test", "mb123")).To(Equal("url"))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetURL("123", "test", "mb123")
_, err := ag.GetURL(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetURL("123", "test", "mb123")
_, err := ag.GetURL(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
@@ -94,17 +94,17 @@ var _ = Describe("Agents", func() {
Describe("GetBiography", func() {
It("returns on first match", func() {
Expect(ag.GetBiography("123", "test", "mb123")).To(Equal("bio"))
Expect(ag.GetBiography(ctx, "123", "test", "mb123")).To(Equal("bio"))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
Expect(ag.GetBiography("123", "test", "mb123")).To(Equal(placeholderBiography))
Expect(ag.GetBiography(ctx, "123", "test", "mb123")).To(Equal(placeholderBiography))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetBiography("123", "test", "mb123")
_, err := ag.GetBiography(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
@@ -112,7 +112,7 @@ var _ = Describe("Agents", func() {
Describe("GetImages", func() {
It("returns on first match", func() {
Expect(ag.GetImages("123", "test", "mb123")).To(Equal([]ArtistImage{{
Expect(ag.GetImages(ctx, "123", "test", "mb123")).To(Equal([]ArtistImage{{
URL: "imageUrl",
Size: 100,
}}))
@@ -120,12 +120,12 @@ var _ = Describe("Agents", func() {
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
Expect(ag.GetImages("123", "test", "mb123")).To(HaveLen(3))
Expect(ag.GetImages(ctx, "123", "test", "mb123")).To(HaveLen(3))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetImages("123", "test", "mb123")
_, err := ag.GetImages(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
@@ -133,7 +133,7 @@ var _ = Describe("Agents", func() {
Describe("GetSimilar", func() {
It("returns on first match", func() {
Expect(ag.GetSimilar("123", "test", "mb123", 1)).To(Equal([]Artist{{
Expect(ag.GetSimilar(ctx, "123", "test", "mb123", 1)).To(Equal([]Artist{{
Name: "Joe Dohn",
MBID: "mbid321",
}}))
@@ -141,13 +141,13 @@ var _ = Describe("Agents", func() {
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetSimilar("123", "test", "mb123", 1)
_, err := ag.GetSimilar(ctx, "123", "test", "mb123", 1)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123", 1))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetSimilar("123", "test", "mb123", 1)
_, err := ag.GetSimilar(ctx, "123", "test", "mb123", 1)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
@@ -155,7 +155,7 @@ var _ = Describe("Agents", func() {
Describe("GetTopSongs", func() {
It("returns on first match", func() {
Expect(ag.GetTopSongs("123", "test", "mb123", 2)).To(Equal([]Song{{
Expect(ag.GetTopSongs(ctx, "123", "test", "mb123", 2)).To(Equal([]Song{{
Name: "A Song",
MBID: "mbid444",
}}))
@@ -163,13 +163,13 @@ var _ = Describe("Agents", func() {
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetTopSongs("123", "test", "mb123", 2)
_, err := ag.GetTopSongs(ctx, "123", "test", "mb123", 2)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123", 2))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetTopSongs("123", "test", "mb123", 2)
_, err := ag.GetTopSongs(ctx, "123", "test", "mb123", 2)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
@@ -186,7 +186,7 @@ func (a *mockAgent) AgentName() string {
return "fake"
}
func (a *mockAgent) GetMBID(id string, name string) (string, error) {
func (a *mockAgent) GetMBID(ctx context.Context, id string, name string) (string, error) {
a.Args = []interface{}{id, name}
if a.Err != nil {
return "", a.Err
@@ -194,7 +194,7 @@ func (a *mockAgent) GetMBID(id string, name string) (string, error) {
return "mbid", nil
}
func (a *mockAgent) GetURL(id, name, mbid string) (string, error) {
func (a *mockAgent) GetURL(ctx context.Context, id, name, mbid string) (string, error) {
a.Args = []interface{}{id, name, mbid}
if a.Err != nil {
return "", a.Err
@@ -202,7 +202,7 @@ func (a *mockAgent) GetURL(id, name, mbid string) (string, error) {
return "url", nil
}
func (a *mockAgent) GetBiography(id, name, mbid string) (string, error) {
func (a *mockAgent) GetBiography(ctx context.Context, id, name, mbid string) (string, error) {
a.Args = []interface{}{id, name, mbid}
if a.Err != nil {
return "", a.Err
@@ -210,7 +210,7 @@ func (a *mockAgent) GetBiography(id, name, mbid string) (string, error) {
return "bio", nil
}
func (a *mockAgent) GetImages(id, name, mbid string) ([]ArtistImage, error) {
func (a *mockAgent) GetImages(ctx context.Context, id, name, mbid string) ([]ArtistImage, error) {
a.Args = []interface{}{id, name, mbid}
if a.Err != nil {
return nil, a.Err
@@ -221,7 +221,7 @@ func (a *mockAgent) GetImages(id, name, mbid string) ([]ArtistImage, error) {
}}, nil
}
func (a *mockAgent) GetSimilar(id, name, mbid string, limit int) ([]Artist, error) {
func (a *mockAgent) GetSimilar(ctx context.Context, id, name, mbid string, limit int) ([]Artist, error) {
a.Args = []interface{}{id, name, mbid, limit}
if a.Err != nil {
return nil, a.Err
@@ -232,7 +232,7 @@ func (a *mockAgent) GetSimilar(id, name, mbid string, limit int) ([]Artist, erro
}}, nil
}
func (a *mockAgent) GetTopSongs(id, artistName, mbid string, count int) ([]Song, error) {
func (a *mockAgent) GetTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]Song, error) {
a.Args = []interface{}{id, artistName, mbid, count}
if a.Err != nil {
return nil, a.Err
+9 -7
View File
@@ -3,9 +3,11 @@ package agents
import (
"context"
"errors"
"github.com/navidrome/navidrome/model"
)
type Constructor func(ctx context.Context) Interface
type Constructor func(ds model.DataStore) Interface
type Interface interface {
AgentName() string
@@ -31,27 +33,27 @@ var (
)
type ArtistMBIDRetriever interface {
GetMBID(id string, name string) (string, error)
GetMBID(ctx context.Context, id string, name string) (string, error)
}
type ArtistURLRetriever interface {
GetURL(id, name, mbid string) (string, error)
GetURL(ctx context.Context, id, name, mbid string) (string, error)
}
type ArtistBiographyRetriever interface {
GetBiography(id, name, mbid string) (string, error)
GetBiography(ctx context.Context, id, name, mbid string) (string, error)
}
type ArtistSimilarRetriever interface {
GetSimilar(id, name, mbid string, limit int) ([]Artist, error)
GetSimilar(ctx context.Context, id, name, mbid string, limit int) ([]Artist, error)
}
type ArtistImageRetriever interface {
GetImages(id, name, mbid string) ([]ArtistImage, error)
GetImages(ctx context.Context, id, name, mbid string) ([]ArtistImage, error)
}
type ArtistTopSongsRetriever interface {
GetTopSongs(id, artistName, mbid string, count int) ([]Song, error)
GetTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]Song, error)
}
var Map map[string]Constructor
+24 -8
View File
@@ -7,7 +7,9 @@ import (
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
@@ -17,15 +19,16 @@ const (
type lastfmAgent struct {
ctx context.Context
ds model.DataStore
apiKey string
secret string
lang string
client *Client
}
func lastFMConstructor(ctx context.Context) agents.Interface {
func lastFMConstructor(ds model.DataStore) *lastfmAgent {
l := &lastfmAgent{
ctx: ctx,
ds: ds,
lang: conf.Server.LastFM.Language,
apiKey: conf.Server.LastFM.ApiKey,
secret: conf.Server.LastFM.Secret,
@@ -39,7 +42,7 @@ func (l *lastfmAgent) AgentName() string {
return lastFMAgentName
}
func (l *lastfmAgent) GetMBID(id string, name string) (string, error) {
func (l *lastfmAgent) GetMBID(ctx context.Context, id string, name string) (string, error) {
a, err := l.callArtistGetInfo(name, "")
if err != nil {
return "", err
@@ -50,7 +53,7 @@ func (l *lastfmAgent) GetMBID(id string, name string) (string, error) {
return a.MBID, nil
}
func (l *lastfmAgent) GetURL(id, name, mbid string) (string, error) {
func (l *lastfmAgent) GetURL(ctx context.Context, id, name, mbid string) (string, error) {
a, err := l.callArtistGetInfo(name, mbid)
if err != nil {
return "", err
@@ -61,7 +64,7 @@ func (l *lastfmAgent) GetURL(id, name, mbid string) (string, error) {
return a.URL, nil
}
func (l *lastfmAgent) GetBiography(id, name, mbid string) (string, error) {
func (l *lastfmAgent) GetBiography(ctx context.Context, id, name, mbid string) (string, error) {
a, err := l.callArtistGetInfo(name, mbid)
if err != nil {
return "", err
@@ -72,7 +75,7 @@ func (l *lastfmAgent) GetBiography(id, name, mbid string) (string, error) {
return a.Bio.Summary, nil
}
func (l *lastfmAgent) GetSimilar(id, name, mbid string, limit int) ([]agents.Artist, error) {
func (l *lastfmAgent) GetSimilar(ctx context.Context, id, name, mbid string, limit int) ([]agents.Artist, error) {
resp, err := l.callArtistGetSimilar(name, mbid, limit)
if err != nil {
return nil, err
@@ -90,7 +93,7 @@ func (l *lastfmAgent) GetSimilar(id, name, mbid string, limit int) ([]agents.Art
return res, nil
}
func (l *lastfmAgent) GetTopSongs(id, artistName, mbid string, count int) ([]agents.Song, error) {
func (l *lastfmAgent) GetTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]agents.Song, error) {
resp, err := l.callArtistGetTopTracks(artistName, mbid, count)
if err != nil {
return nil, err
@@ -151,10 +154,23 @@ func (l *lastfmAgent) callArtistGetTopTracks(artistName, mbid string, count int)
return t.Track, nil
}
func (l *lastfmAgent) NowPlaying(c context.Context, track *model.MediaFile) error {
return nil
}
func (l *lastfmAgent) Scrobble(ctx context.Context, scrobbles []scrobbler.Scrobble) error {
return nil
}
func init() {
conf.AddHook(func() {
if conf.Server.LastFM.Enabled {
agents.Register(lastFMAgentName, lastFMConstructor)
agents.Register(lastFMAgentName, func(ds model.DataStore) agents.Interface {
return lastFMConstructor(ds)
})
scrobbler.Register(lastFMAgentName, func(ds model.DataStore) scrobbler.Scrobbler {
return lastFMConstructor(ds)
})
}
})
}
+18 -18
View File
@@ -46,14 +46,14 @@ var _ = Describe("lastfmAgent", func() {
It("returns the biography", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetBiography("123", "U2", "mbid-1234")).To(Equal("U2 é uma das mais importantes bandas de rock de todos os tempos. Formada em 1976 em Dublin, composta por Bono (vocalista e guitarrista), The Edge (guitarrista, pianista e backing vocal), Adam Clayton (baixista), Larry Mullen, Jr. (baterista e percussionista).\n\nDesde a década de 80, U2 é uma das bandas mais populares no mundo. Seus shows são únicos e um verdadeiro festival de efeitos especiais, além de serem um dos que mais arrecadam anualmente. <a href=\"https://www.last.fm/music/U2\">Read more on Last.fm</a>"))
Expect(agent.GetBiography(ctx, "123", "U2", "mbid-1234")).To(Equal("U2 é uma das mais importantes bandas de rock de todos os tempos. Formada em 1976 em Dublin, composta por Bono (vocalista e guitarrista), The Edge (guitarrista, pianista e backing vocal), Adam Clayton (baixista), Larry Mullen, Jr. (baterista e percussionista).\n\nDesde a década de 80, U2 é uma das bandas mais populares no mundo. Seus shows são únicos e um verdadeiro festival de efeitos especiais, além de serem um dos que mais arrecadam anualmente. <a href=\"https://www.last.fm/music/U2\">Read more on Last.fm</a>"))
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.FM call fails", func() {
httpClient.Err = errors.New("error")
_, err := agent.GetBiography("123", "U2", "mbid-1234")
_, err := agent.GetBiography(ctx, "123", "U2", "mbid-1234")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
@@ -61,7 +61,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call returns an error", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
_, err := agent.GetBiography("123", "U2", "mbid-1234")
_, err := agent.GetBiography(ctx, "123", "U2", "mbid-1234")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
@@ -69,7 +69,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call returns an error 6 and mbid is empty", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, err := agent.GetBiography("123", "U2", "")
_, err := agent.GetBiography(ctx, "123", "U2", "")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
})
@@ -78,13 +78,13 @@ var _ = Describe("lastfmAgent", func() {
It("calls again when the response is artist == [unknown]", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.unknown.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
_, _ = agent.GetBiography("123", "U2", "mbid-1234")
_, _ = agent.GetBiography(ctx, "123", "U2", "mbid-1234")
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
It("calls again when last.fm returns an error 6", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, _ = agent.GetBiography("123", "U2", "mbid-1234")
_, _ = agent.GetBiography(ctx, "123", "U2", "mbid-1234")
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
@@ -104,7 +104,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns similar artists", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetSimilar("123", "U2", "mbid-1234", 2)).To(Equal([]agents.Artist{
Expect(agent.GetSimilar(ctx, "123", "U2", "mbid-1234", 2)).To(Equal([]agents.Artist{
{Name: "Passengers", MBID: "e110c11f-1c94-4471-a350-c38f46b29389"},
{Name: "INXS", MBID: "481bf5f9-2e7c-4c44-b08a-05b32bc7c00d"},
}))
@@ -114,7 +114,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call fails", func() {
httpClient.Err = errors.New("error")
_, err := agent.GetSimilar("123", "U2", "mbid-1234", 2)
_, err := agent.GetSimilar(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
@@ -122,7 +122,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call returns an error", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
_, err := agent.GetSimilar("123", "U2", "mbid-1234", 2)
_, err := agent.GetSimilar(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
@@ -130,7 +130,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call returns an error 6 and mbid is empty", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, err := agent.GetSimilar("123", "U2", "", 2)
_, err := agent.GetSimilar(ctx, "123", "U2", "", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
})
@@ -139,13 +139,13 @@ var _ = Describe("lastfmAgent", func() {
It("calls again when the response is artist == [unknown]", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.unknown.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
_, _ = agent.GetSimilar("123", "U2", "mbid-1234", 2)
_, _ = agent.GetSimilar(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
It("calls again when last.fm returns an error 6", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, _ = agent.GetSimilar("123", "U2", "mbid-1234", 2)
_, _ = agent.GetSimilar(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
@@ -165,7 +165,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns top songs", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetTopSongs("123", "U2", "mbid-1234", 2)).To(Equal([]agents.Song{
Expect(agent.GetTopSongs(ctx, "123", "U2", "mbid-1234", 2)).To(Equal([]agents.Song{
{Name: "Beautiful Day", MBID: "f7f264d0-a89b-4682-9cd7-a4e7c37637af"},
{Name: "With or Without You", MBID: "6b9a509f-6907-4a6e-9345-2f12da09ba4b"},
}))
@@ -175,7 +175,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call fails", func() {
httpClient.Err = errors.New("error")
_, err := agent.GetTopSongs("123", "U2", "mbid-1234", 2)
_, err := agent.GetTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
@@ -183,7 +183,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call returns an error", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
_, err := agent.GetTopSongs("123", "U2", "mbid-1234", 2)
_, err := agent.GetTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
@@ -191,7 +191,7 @@ var _ = Describe("lastfmAgent", func() {
It("returns an error if Last.FM call returns an error 6 and mbid is empty", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, err := agent.GetTopSongs("123", "U2", "", 2)
_, err := agent.GetTopSongs(ctx, "123", "U2", "", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
})
@@ -200,13 +200,13 @@ var _ = Describe("lastfmAgent", func() {
It("calls again when the response is artist == [unknown]", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.unknown.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
_, _ = agent.GetTopSongs("123", "U2", "mbid-1234", 2)
_, _ = agent.GetTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
It("calls again when last.fm returns an error 6", func() {
httpClient.Res = http.Response{Body: ioutil.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, _ = agent.GetTopSongs("123", "U2", "mbid-1234", 2)
_, _ = agent.GetTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
+5 -3
View File
@@ -2,6 +2,8 @@ package agents
import (
"context"
"github.com/navidrome/navidrome/model"
)
const PlaceholderAgentName = "placeholder"
@@ -15,7 +17,7 @@ const (
type placeholderAgent struct{}
func placeholdersConstructor(ctx context.Context) Interface {
func placeholdersConstructor(ds model.DataStore) Interface {
return &placeholderAgent{}
}
@@ -23,11 +25,11 @@ func (p *placeholderAgent) AgentName() string {
return PlaceholderAgentName
}
func (p *placeholderAgent) GetBiography(id, name, mbid string) (string, error) {
func (p *placeholderAgent) GetBiography(ctx context.Context, id, name, mbid string) (string, error) {
return placeholderBiography, nil
}
func (p *placeholderAgent) GetImages(id, name, mbid string) ([]ArtistImage, error) {
func (p *placeholderAgent) GetImages(ctx context.Context, id, name, mbid string) ([]ArtistImage, error) {
return []ArtistImage{
{placeholderArtistImageLargeUrl, 300},
{placeholderArtistImageMediumUrl, 174},
+9 -9
View File
@@ -19,15 +19,15 @@ import (
const spotifyAgentName = "spotify"
type spotifyAgent struct {
ctx context.Context
ds model.DataStore
id string
secret string
client *Client
}
func spotifyConstructor(ctx context.Context) agents.Interface {
func spotifyConstructor(ds model.DataStore) agents.Interface {
l := &spotifyAgent{
ctx: ctx,
ds: ds,
id: conf.Server.Spotify.ID,
secret: conf.Server.Spotify.Secret,
}
@@ -40,13 +40,13 @@ func (s *spotifyAgent) AgentName() string {
return spotifyAgentName
}
func (s *spotifyAgent) GetImages(id, name, mbid string) ([]agents.ArtistImage, error) {
a, err := s.searchArtist(name)
func (s *spotifyAgent) GetImages(ctx context.Context, id, name, mbid string) ([]agents.ArtistImage, error) {
a, err := s.searchArtist(ctx, name)
if err != nil {
if err == model.ErrNotFound {
log.Warn(s.ctx, "Artist not found in Spotify", "artist", name)
log.Warn(ctx, "Artist not found in Spotify", "artist", name)
} else {
log.Error(s.ctx, "Error calling Spotify", "artist", name, err)
log.Error(ctx, "Error calling Spotify", "artist", name, err)
}
return nil, err
}
@@ -61,8 +61,8 @@ func (s *spotifyAgent) GetImages(id, name, mbid string) ([]agents.ArtistImage, e
return res, nil
}
func (s *spotifyAgent) searchArtist(name string) (*Artist, error) {
artists, err := s.client.SearchArtists(s.ctx, name, 40)
func (s *spotifyAgent) searchArtist(ctx context.Context, name string) (*Artist, error) {
artists, err := s.client.SearchArtists(ctx, name, 40)
if err != nil || len(artists) == 0 {
return nil, model.ErrNotFound
}