Skip to content

Commit

Permalink
Add "real" TopSongs
Browse files Browse the repository at this point in the history
  • Loading branch information
deluan committed Oct 21, 2020
1 parent b5e20c1 commit 049ac70
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 3 deletions.
27 changes: 27 additions & 0 deletions core/external_info.go
Expand Up @@ -25,6 +25,7 @@ type ExternalInfo interface {
ArtistInfo(ctx context.Context, id string) (*model.ArtistInfo, error)
SimilarArtists(ctx context.Context, id string, includeNotPresent bool, count int) (model.Artists, error)
SimilarSongs(ctx context.Context, id string, count int) (model.MediaFiles, error)
TopSongs(ctx context.Context, artist string, count int) (model.MediaFiles, error)
}

func NewExternalInfo(ds model.DataStore, lfm *lastfm.Client, spf *spotify.Client) ExternalInfo {
Expand Down Expand Up @@ -136,6 +137,32 @@ func (e *externalInfo) similarArtists(ctx context.Context, artist *model.Artist,
return result, nil
}

func (e *externalInfo) TopSongs(ctx context.Context, artist string, count int) (model.MediaFiles, error) {
if e.lfm == nil {
log.Warn(ctx, "Last.FM client not configured")
return nil, model.ErrNotAvailable
}
log.Debug(ctx, "Calling Last.FM ArtistGetTopTracks", "artist", artist)
tracks, err := e.lfm.ArtistGetTopTracks(ctx, artist, count)
if err != nil {
return nil, err
}
var songs model.MediaFiles
for _, t := range tracks {
mfs, err := e.ds.MediaFile(ctx).GetAll(model.QueryOptions{
Filters: squirrel.And{
squirrel.Like{"artist": artist},
squirrel.Like{"title": t.Name},
},
})
if err != nil || len(mfs) == 0 {
continue
}
songs = append(songs, mfs[0])
}
return songs, nil
}

func (e *externalInfo) ArtistInfo(ctx context.Context, id string) (*model.ArtistInfo, error) {
artist, err := e.getArtist(ctx, id)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions core/lastfm/client.go
Expand Up @@ -80,6 +80,18 @@ func (c *Client) ArtistGetSimilar(ctx context.Context, name string, limit int) (
return response.SimilarArtists.Artists, nil
}

func (c *Client) ArtistGetTopTracks(ctx context.Context, name string, limit int) ([]Track, error) {
params := url.Values{}
params.Add("method", "artist.getTopTracks")
params.Add("artist", name)
params.Add("limit", strconv.Itoa(limit))
response, err := c.makeRequest(params)
if err != nil {
return nil, err
}
return response.TopTracks.Track, nil
}

func (c *Client) parseError(data []byte) error {
var e Error
err := json.Unmarshal(data, &e)
Expand Down
38 changes: 38 additions & 0 deletions core/lastfm/client_test.go
Expand Up @@ -98,7 +98,45 @@ var _ = Describe("Client", func() {
_, err := client.ArtistGetSimilar(context.TODO(), "U2", 2)
Expect(err).To(MatchError("invalid character '<' looking for beginning of value"))
})
})

Describe("ArtistGetTopTracks", func() {
It("returns top tracks for a successful response", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.json")
httpClient.res = http.Response{Body: f, StatusCode: 200}

tracks, err := client.ArtistGetTopTracks(context.TODO(), "U2", 2)
Expect(err).To(BeNil())
Expect(len(tracks)).To(Equal(2))
Expect(httpClient.savedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&limit=2&method=artist.getTopTracks"))
})

It("fails if Last.FM returns an error", func() {
httpClient.res = http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":3,"message":"Invalid Method - No method with that name in this package"}`)),
StatusCode: 400,
}

_, err := client.ArtistGetTopTracks(context.TODO(), "U2", 2)
Expect(err).To(MatchError("last.fm error(3): Invalid Method - No method with that name in this package"))
})

It("fails if HttpClient.Do() returns error", func() {
httpClient.err = errors.New("generic error")

_, err := client.ArtistGetTopTracks(context.TODO(), "U2", 2)
Expect(err).To(MatchError("generic error"))
})

It("fails if returned body is not a valid JSON", func() {
httpClient.res = http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(`<xml>NOT_VALID_JSON</xml>`)),
StatusCode: 200,
}

_, err := client.ArtistGetTopTracks(context.TODO(), "U2", 2)
Expect(err).To(MatchError("invalid character '<' looking for beginning of value"))
})
})
})

Expand Down
10 changes: 10 additions & 0 deletions core/lastfm/responses.go
Expand Up @@ -3,6 +3,7 @@ package lastfm
type Response struct {
Artist Artist `json:"artist"`
SimilarArtists SimilarArtists `json:"similarartists"`
TopTracks TopTracks `json:"toptracks"`
}

type Artist struct {
Expand Down Expand Up @@ -42,6 +43,15 @@ type ArtistBio struct {
Content string `json:"content"`
}

type Track struct {
Name string `json:"name"`
MBID string `json:"mbid"`
}

type TopTracks struct {
Track []Track `json:"track"`
}

type Error struct {
Code int `json:"error"`
Message string `json:"message"`
Expand Down
15 changes: 15 additions & 0 deletions core/lastfm/responses_test.go
Expand Up @@ -41,6 +41,21 @@ var _ = Describe("LastFM responses", func() {
})
})

Describe("TopTracks", func() {
It("parses the response correctly", func() {
var resp Response
body, _ := ioutil.ReadFile("tests/fixtures/lastfm.artist.gettoptracks.json")
err := json.Unmarshal(body, &resp)
Expect(err).To(BeNil())

Expect(resp.TopTracks.Track).To(HaveLen(2))
Expect(resp.TopTracks.Track[0].Name).To(Equal("Beautiful Day"))
Expect(resp.TopTracks.Track[0].MBID).To(Equal("f7f264d0-a89b-4682-9cd7-a4e7c37637af"))
Expect(resp.TopTracks.Track[1].Name).To(Equal("With or Without You"))
Expect(resp.TopTracks.Track[1].MBID).To(Equal("6b9a509f-6907-4a6e-9345-2f12da09ba4b"))
})
})

Describe("Error", func() {
It("parses the error response correctly", func() {
var error Error
Expand Down
19 changes: 16 additions & 3 deletions server/subsonic/browsing.go
Expand Up @@ -298,7 +298,7 @@ func (c *BrowsingController) GetSimilarSongs(w http.ResponseWriter, r *http.Requ
if err != nil {
return nil, err
}
count := utils.ParamInt(r, "count", 20)
count := utils.ParamInt(r, "count", 50)

songs, err := c.ei.SimilarSongs(ctx, id, count)
if err != nil {
Expand All @@ -325,10 +325,23 @@ func (c *BrowsingController) GetSimilarSongs2(w http.ResponseWriter, r *http.Req
return response, nil
}

// TODO Integrate with Last.FM
func (c *BrowsingController) GetTopSongs(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
ctx := r.Context()
artist, err := requiredParamString(r, "artist", "artist parameter required")
if err != nil {
return nil, err
}
count := utils.ParamInt(r, "count", 50)

songs, err := c.ei.TopSongs(ctx, artist, count)
if err != nil {
return nil, err
}

response := newResponse()
response.TopSongs = &responses.TopSongs{}
response.TopSongs = &responses.TopSongs{
Song: childrenFromMediaFiles(ctx, songs),
}
return response, nil
}

Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/lastfm.artist.gettoptracks.json
@@ -0,0 +1 @@
{"toptracks":{"track":[{"name":"Beautiful Day","playcount":"6309776","listeners":"1037970","mbid":"f7f264d0-a89b-4682-9cd7-a4e7c37637af","url":"https://www.last.fm/music/U2/_/Beautiful+Day","streamable":"0","artist":{"name":"U2","mbid":"a3cb23fc-acd3-4ce0-8f36-1e5aa6a18432","url":"https://www.last.fm/music/U2"},"image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"}],"@attr":{"rank":"1"}},{"name":"With or Without You","playcount":"6779665","listeners":"1022929","mbid":"6b9a509f-6907-4a6e-9345-2f12da09ba4b","url":"https://www.last.fm/music/U2/_/With+or+Without+You","streamable":"0","artist":{"name":"U2","mbid":"a3cb23fc-acd3-4ce0-8f36-1e5aa6a18432","url":"https://www.last.fm/music/U2"},"image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"}],"@attr":{"rank":"2"}}],"@attr":{"artist":"U2","page":"1","perPage":"2","totalPages":"166117","total":"332234"}}}

0 comments on commit 049ac70

Please sign in to comment.