Add ExternalInformation core service (not a great name, I know)

This commit is contained in:
Deluan
2020-10-18 19:10:11 -04:00
committed by Deluan Quintão
parent 19ead8f7e8
commit 07535e1518
14 changed files with 313 additions and 38 deletions
+4 -1
View File
@@ -50,7 +50,10 @@ func CreateSubsonicAPIRouter() (*subsonic.Router, error) {
mediaStreamer := core.NewMediaStreamer(dataStore, transcoderTranscoder, transcodingCache) mediaStreamer := core.NewMediaStreamer(dataStore, transcoderTranscoder, transcodingCache)
archiver := core.NewArchiver(dataStore) archiver := core.NewArchiver(dataStore)
players := engine.NewPlayers(dataStore) players := engine.NewPlayers(dataStore)
router := subsonic.New(artwork, listGenerator, playlists, mediaStreamer, archiver, players, dataStore) lastFMClient := core.LastFMNewClient()
spotifyClient := core.SpotifyNewClient()
externalInfo := core.NewExternalInfo(dataStore, lastFMClient, spotifyClient)
router := subsonic.New(artwork, listGenerator, playlists, mediaStreamer, archiver, players, externalInfo, dataStore)
return router, nil return router, nil
} }
+169
View File
@@ -0,0 +1,169 @@
package core
import (
"context"
"sort"
"strings"
"sync"
"time"
"github.com/deluan/navidrome/core/lastfm"
"github.com/deluan/navidrome/core/spotify"
"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model"
"github.com/microcosm-cc/bluemonday"
)
const placeholderArtistImageSmallUrl = "https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png"
const placeholderArtistImageMediumUrl = "https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png"
const placeholderArtistImageLargeUrl = "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png"
type ExternalInfo interface {
ArtistInfo(ctx context.Context, artistId string, includeNotPresent bool, count int) (*model.ArtistInfo, error)
}
type LastFMClient interface {
ArtistGetInfo(ctx context.Context, name string) (*lastfm.Artist, error)
}
type SpotifyClient interface {
ArtistImages(ctx context.Context, name string) ([]spotify.Image, error)
}
func NewExternalInfo(ds model.DataStore, lfm LastFMClient, spf SpotifyClient) ExternalInfo {
return &externalInfo{ds: ds, lfm: lfm, spf: spf}
}
type externalInfo struct {
ds model.DataStore
lfm LastFMClient
spf SpotifyClient
}
func (e *externalInfo) ArtistInfo(ctx context.Context, artistId string,
includeNotPresent bool, count int) (*model.ArtistInfo, error) {
info := model.ArtistInfo{ID: artistId}
artist, err := e.ds.Artist(ctx).Get(artistId)
if err != nil {
return nil, err
}
info.Name = artist.Name
// TODO Load from local: artist.jpg/png/webp, artist.json (with the remaining info)
var wg sync.WaitGroup
e.callArtistInfo(ctx, artist, includeNotPresent, &wg, &info)
e.callArtistImages(ctx, artist, &wg, &info)
wg.Wait()
// Use placeholders if could not get from external sources
e.setBio(&info, "Biography not available")
e.setSmallImageUrl(&info, placeholderArtistImageSmallUrl)
e.setMediumImageUrl(&info, placeholderArtistImageMediumUrl)
e.setLargeImageUrl(&info, placeholderArtistImageLargeUrl)
log.Trace(ctx, "ArtistInfo collected", "artist", artist.Name, "info", info)
return &info, nil
}
func (e *externalInfo) callArtistInfo(ctx context.Context, artist *model.Artist, includeNotPresent bool,
wg *sync.WaitGroup, info *model.ArtistInfo) {
if e.lfm != nil {
log.Debug(ctx, "Calling Last.FM ArtistGetInfo", "artist", artist.Name)
wg.Add(1)
go func() {
start := time.Now()
defer wg.Done()
lfmArtist, err := e.lfm.ArtistGetInfo(nil, artist.Name)
if err != nil {
log.Error(ctx, "Error calling Last.FM", "artist", artist.Name, err)
} else {
log.Debug(ctx, "Got info from Last.FM", "artist", artist.Name, "info", lfmArtist.Bio.Summary, "elapsed", time.Since(start))
}
e.setBio(info, lfmArtist.Bio.Summary)
e.setSimilar(ctx, info, lfmArtist.Similar.Artists, includeNotPresent)
}()
}
}
func (e *externalInfo) callArtistImages(ctx context.Context, artist *model.Artist, wg *sync.WaitGroup, info *model.ArtistInfo) {
if e.spf != nil {
log.Debug(ctx, "Calling Spotify ArtistImages", "artist", artist.Name)
wg.Add(1)
go func() {
start := time.Now()
defer wg.Done()
spfImages, err := e.spf.ArtistImages(nil, artist.Name)
if err != nil {
log.Error(ctx, "Error calling Spotify", "artist", artist.Name, err)
} else {
log.Debug(ctx, "Got images from Spotify", "artist", artist.Name, "images", spfImages, "elapsed", time.Since(start))
}
sort.Slice(spfImages, func(i, j int) bool { return spfImages[i].Width > spfImages[j].Width })
if len(spfImages) >= 1 {
e.setLargeImageUrl(info, spfImages[0].URL)
}
if len(spfImages) >= 2 {
e.setMediumImageUrl(info, spfImages[1].URL)
}
if len(spfImages) >= 3 {
e.setSmallImageUrl(info, spfImages[2].URL)
}
}()
}
}
func (e *externalInfo) setBio(info *model.ArtistInfo, bio string) {
policy := bluemonday.UGCPolicy()
if info.Bio == "" {
bio = policy.Sanitize(bio)
bio = strings.ReplaceAll(bio, "\n", " ")
info.Bio = strings.ReplaceAll(bio, "<a ", "<a target='_blank' ")
}
}
func (e *externalInfo) setSmallImageUrl(info *model.ArtistInfo, url string) {
if info.SmallImageUrl == "" {
info.SmallImageUrl = url
}
}
func (e *externalInfo) setMediumImageUrl(info *model.ArtistInfo, url string) {
if info.MediumImageUrl == "" {
info.MediumImageUrl = url
}
}
func (e *externalInfo) setLargeImageUrl(info *model.ArtistInfo, url string) {
if info.LargeImageUrl == "" {
info.LargeImageUrl = url
}
}
func (e *externalInfo) setSimilar(ctx context.Context, info *model.ArtistInfo, artists []lastfm.Artist, includeNotPresent bool) {
if len(info.Similar) == 0 {
var notPresent []string
// First select artists that are present.
for _, s := range artists {
sa, err := e.ds.Artist(ctx).FindByName(s.Name)
if err != nil {
notPresent = append(notPresent, s.Name)
continue
}
info.Similar = append(info.Similar, *sa)
}
// Then fill up with non-present artists
if includeNotPresent {
for _, s := range notPresent {
sa := model.Artist{ID: "-1", Name: s}
info.Similar = append(info.Similar, sa)
}
}
}
}
+2 -1
View File
@@ -1,6 +1,7 @@
package lastfm package lastfm
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@@ -27,7 +28,7 @@ type Client struct {
} }
// TODO SimilarArtists() // TODO SimilarArtists()
func (c *Client) ArtistGetInfo(name string) (*Artist, error) { func (c *Client) ArtistGetInfo(ctx context.Context, name string) (*Artist, error) {
params := url.Values{} params := url.Values{}
params.Add("method", "artist.getInfo") params.Add("method", "artist.getInfo")
params.Add("format", "json") params.Add("format", "json")
+5 -4
View File
@@ -2,6 +2,7 @@ package lastfm
import ( import (
"bytes" "bytes"
"context"
"errors" "errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@@ -25,7 +26,7 @@ var _ = Describe("Client", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json") f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
httpClient.res = http.Response{Body: f, StatusCode: 200} httpClient.res = http.Response{Body: f, StatusCode: 200}
artist, err := client.ArtistGetInfo("U2") artist, err := client.ArtistGetInfo(context.TODO(), "U2")
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(artist.Name).To(Equal("U2")) Expect(artist.Name).To(Equal("U2"))
Expect(httpClient.savedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&lang=pt&method=artist.getInfo")) Expect(httpClient.savedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&lang=pt&method=artist.getInfo"))
@@ -37,14 +38,14 @@ var _ = Describe("Client", func() {
StatusCode: 400, StatusCode: 400,
} }
_, err := client.ArtistGetInfo("U2") _, err := client.ArtistGetInfo(context.TODO(), "U2")
Expect(err).To(MatchError("last.fm error(3): Invalid Method - No method with that name in this package")) 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() { It("fails if HttpClient.Do() returns error", func() {
httpClient.err = errors.New("generic error") httpClient.err = errors.New("generic error")
_, err := client.ArtistGetInfo("U2") _, err := client.ArtistGetInfo(context.TODO(), "U2")
Expect(err).To(MatchError("generic error")) Expect(err).To(MatchError("generic error"))
}) })
@@ -54,7 +55,7 @@ var _ = Describe("Client", func() {
StatusCode: 200, StatusCode: 200,
} }
_, err := client.ArtistGetInfo("U2") _, err := client.ArtistGetInfo(context.TODO(), "U2")
Expect(err).To(MatchError("invalid character '<' looking for beginning of value")) Expect(err).To(MatchError("invalid character '<' looking for beginning of value"))
}) })
+7 -5
View File
@@ -1,6 +1,7 @@
package spotify package spotify
import ( import (
"context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
@@ -34,8 +35,8 @@ type Client struct {
hc HttpClient hc HttpClient
} }
func (c *Client) ArtistImages(name string) ([]Image, error) { func (c *Client) ArtistImages(ctx context.Context, name string) ([]Image, error) {
token, err := c.authorize() token, err := c.authorize(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -58,12 +59,13 @@ func (c *Client) ArtistImages(name string) ([]Image, error) {
if len(results.Artists.Items) == 0 { if len(results.Artists.Items) == 0 {
return nil, ErrNotFound return nil, ErrNotFound
} }
log.Debug(ctx, "Found artist in Spotify", "artist", results.Artists.Items[0].Name)
return results.Artists.Items[0].Images, err return results.Artists.Items[0].Images, err
} }
func (c *Client) authorize() (string, error) { func (c *Client) authorize(ctx context.Context) (string, error) {
payload := url.Values{} payload := url.Values{}
payload.Add("grant_type", "client_credentials.getInfo") payload.Add("grant_type", "client_credentials")
req, _ := http.NewRequest("POST", "https://accounts.spotify.com/api/token", strings.NewReader(payload.Encode())) req, _ := http.NewRequest("POST", "https://accounts.spotify.com/api/token", strings.NewReader(payload.Encode()))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
@@ -80,7 +82,7 @@ func (c *Client) authorize() (string, error) {
if v, ok := response["access_token"]; ok { if v, ok := response["access_token"]; ok {
return v.(string), nil return v.(string), nil
} }
log.Error("Invalid spotify response", "resp", response) log.Error(ctx, "Invalid spotify response", "resp", response)
return "", errors.New("invalid response") return "", errors.New("invalid response")
} }
+7 -6
View File
@@ -2,6 +2,7 @@ package spotify
import ( import (
"bytes" "bytes"
"context"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
@@ -28,7 +29,7 @@ var _ = Describe("Client", func() {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)), Body: ioutil.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)),
}) })
images, err := client.ArtistImages("U2") images, err := client.ArtistImages(context.TODO(), "U2")
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(images).To(HaveLen(3)) Expect(images).To(HaveLen(3))
Expect(images[0].Width).To(Equal(640)) Expect(images[0].Width).To(Equal(640))
@@ -50,7 +51,7 @@ var _ = Describe("Client", func() {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)), Body: ioutil.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)),
}) })
_, err := client.ArtistImages("U2") _, err := client.ArtistImages(context.TODO(), "U2")
Expect(err).To(MatchError(ErrNotFound)) Expect(err).To(MatchError(ErrNotFound))
}) })
@@ -62,7 +63,7 @@ var _ = Describe("Client", func() {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":"invalid_client","error_description":"Invalid client"}`)), Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":"invalid_client","error_description":"Invalid client"}`)),
}) })
_, err := client.ArtistImages("U2") _, err := client.ArtistImages(context.TODO(), "U2")
Expect(err).To(MatchError("spotify error(invalid_client): Invalid client")) Expect(err).To(MatchError("spotify error(invalid_client): Invalid client"))
}) })
}) })
@@ -74,7 +75,7 @@ var _ = Describe("Client", func() {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)), Body: ioutil.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)),
}) })
token, err := client.authorize() token, err := client.authorize(nil)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(token).To(Equal("NEW_ACCESS_TOKEN")) Expect(token).To(Equal("NEW_ACCESS_TOKEN"))
auth := httpClient.lastRequest.Header.Get("Authorization") auth := httpClient.lastRequest.Header.Get("Authorization")
@@ -87,7 +88,7 @@ var _ = Describe("Client", func() {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":"invalid_client","error_description":"Invalid client"}`)), Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":"invalid_client","error_description":"Invalid client"}`)),
}) })
_, err := client.authorize() _, err := client.authorize(nil)
Expect(err).To(MatchError("spotify error(invalid_client): Invalid client")) Expect(err).To(MatchError("spotify error(invalid_client): Invalid client"))
}) })
@@ -97,7 +98,7 @@ var _ = Describe("Client", func() {
Body: ioutil.NopCloser(bytes.NewBufferString(`{NOT_VALID}`)), Body: ioutil.NopCloser(bytes.NewBufferString(`{NOT_VALID}`)),
}) })
_, err := client.authorize() _, err := client.authorize(nil)
Expect(err).To(MatchError("invalid character 'N' looking for beginning of object key string")) Expect(err).To(MatchError("invalid character 'N' looking for beginning of object key string"))
}) })
}) })
+24
View File
@@ -1,6 +1,11 @@
package core package core
import ( import (
"net/http"
"github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/core/lastfm"
"github.com/deluan/navidrome/core/spotify"
"github.com/deluan/navidrome/core/transcoder" "github.com/deluan/navidrome/core/transcoder"
"github.com/google/wire" "github.com/google/wire"
) )
@@ -11,5 +16,24 @@ var Set = wire.NewSet(
NewTranscodingCache, NewTranscodingCache,
NewImageCache, NewImageCache,
NewArchiver, NewArchiver,
NewExternalInfo,
LastFMNewClient,
SpotifyNewClient,
transcoder.New, transcoder.New,
) )
func LastFMNewClient() LastFMClient {
if conf.Server.LastFM.ApiKey == "" {
return nil
}
return lastfm.NewClient(conf.Server.LastFM.ApiKey, conf.Server.LastFM.Language, http.DefaultClient)
}
func SpotifyNewClient() SpotifyClient {
if conf.Server.Spotify.ID == "" || conf.Server.Spotify.Secret == "" {
return nil
}
return spotify.NewClient(conf.Server.Spotify.ID, conf.Server.Spotify.Secret, http.DefaultClient)
}
+1
View File
@@ -26,6 +26,7 @@ type ArtistRepository interface {
Exists(id string) (bool, error) Exists(id string) (bool, error)
Put(m *Artist) error Put(m *Artist) error
Get(id string) (*Artist, error) Get(id string) (*Artist, error)
FindByName(name string) (*Artist, error)
GetStarred(options ...QueryOptions) (Artists, error) GetStarred(options ...QueryOptions) (Artists, error)
Search(q string, offset int, size int) (Artists, error) Search(q string, offset int, size int) (Artists, error)
Refresh(ids ...string) error Refresh(ids ...string) error
+11
View File
@@ -0,0 +1,11 @@
package model
type ArtistInfo struct {
ID string
Name string
Bio string
Similar []Artist
SmallImageUrl string
MediumImageUrl string
LargeImageUrl string
}
+12
View File
@@ -66,6 +66,18 @@ func (r *artistRepository) Get(id string) (*model.Artist, error) {
return &res[0], nil return &res[0], nil
} }
func (r *artistRepository) FindByName(name string) (*model.Artist, error) {
sel := r.selectArtist().Where(Eq{"name": name})
var res model.Artists
if err := r.queryAll(sel, &res); err != nil {
return nil, err
}
if len(res) == 0 {
return nil, model.ErrNotFound
}
return &res[0], nil
}
func (r *artistRepository) GetAll(options ...model.QueryOptions) (model.Artists, error) { func (r *artistRepository) GetAll(options ...model.QueryOptions) (model.Artists, error) {
sel := r.selectArtist(options...) sel := r.selectArtist(options...)
res := model.Artists{} res := model.Artists{}
+3 -2
View File
@@ -29,6 +29,7 @@ type Router struct {
Streamer core.MediaStreamer Streamer core.MediaStreamer
Archiver core.Archiver Archiver core.Archiver
Players engine.Players Players engine.Players
ExternalInfo core.ExternalInfo
DataStore model.DataStore DataStore model.DataStore
mux http.Handler mux http.Handler
@@ -36,9 +37,9 @@ type Router struct {
func New(artwork core.Artwork, listGenerator engine.ListGenerator, func New(artwork core.Artwork, listGenerator engine.ListGenerator,
playlists engine.Playlists, streamer core.MediaStreamer, playlists engine.Playlists, streamer core.MediaStreamer,
archiver core.Archiver, players engine.Players, ds model.DataStore) *Router { archiver core.Archiver, players engine.Players, externalInfo core.ExternalInfo, ds model.DataStore) *Router {
r := &Router{Artwork: artwork, ListGenerator: listGenerator, Playlists: playlists, r := &Router{Artwork: artwork, ListGenerator: listGenerator, Playlists: playlists,
Streamer: streamer, Archiver: archiver, Players: players, DataStore: ds} Streamer: streamer, Archiver: archiver, Players: players, ExternalInfo: externalInfo, DataStore: ds}
r.mux = r.routes() r.mux = r.routes()
return r return r
} }
+64 -16
View File
@@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/deluan/navidrome/conf" "github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/core"
"github.com/deluan/navidrome/log" "github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/server/subsonic/responses" "github.com/deluan/navidrome/server/subsonic/responses"
@@ -17,10 +18,11 @@ import (
type BrowsingController struct { type BrowsingController struct {
ds model.DataStore ds model.DataStore
ei core.ExternalInfo
} }
func NewBrowsingController(ds model.DataStore) *BrowsingController { func NewBrowsingController(ds model.DataStore, ei core.ExternalInfo) *BrowsingController {
return &BrowsingController{ds: ds} return &BrowsingController{ds: ds, ei: ei}
} }
func (c *BrowsingController) GetMusicFolders(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { func (c *BrowsingController) GetMusicFolders(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
@@ -230,29 +232,75 @@ func (c *BrowsingController) GetGenres(w http.ResponseWriter, r *http.Request) (
return response, nil return response, nil
} }
const placeholderArtistImageSmallUrl = "https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png"
const placeholderArtistImageMediumUrl = "https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png"
const placeholderArtistImageLargeUrl = "https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png"
// TODO Integrate with Last.FM
func (c *BrowsingController) GetArtistInfo(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { func (c *BrowsingController) GetArtistInfo(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
ctx := r.Context()
id, err := requiredParamString(r, "id", "id parameter required")
if err != nil {
return nil, err
}
count := utils.ParamInt(r, "count", 20)
includeNotPresent := utils.ParamBool(r, "includeNotPresent", false)
entity, err := getEntityByID(ctx, c.ds, id)
if err != nil {
return nil, err
}
switch v := entity.(type) {
case *model.MediaFile:
id = v.ArtistID
case *model.Album:
id = v.AlbumArtistID
case *model.Artist:
id = v.ID
default:
err = model.ErrNotFound
}
if err != nil {
return nil, err
}
info, err := c.ei.ArtistInfo(ctx, id, includeNotPresent, count)
if err != nil {
return nil, err
}
response := newResponse() response := newResponse()
response.ArtistInfo = &responses.ArtistInfo{} response.ArtistInfo = &responses.ArtistInfo{}
response.ArtistInfo.Biography = "Biography not available" response.ArtistInfo.Biography = info.Bio
response.ArtistInfo.SmallImageUrl = placeholderArtistImageSmallUrl response.ArtistInfo.SmallImageUrl = info.SmallImageUrl
response.ArtistInfo.MediumImageUrl = placeholderArtistImageMediumUrl response.ArtistInfo.MediumImageUrl = info.MediumImageUrl
response.ArtistInfo.LargeImageUrl = placeholderArtistImageLargeUrl response.ArtistInfo.LargeImageUrl = info.LargeImageUrl
for _, s := range info.Similar {
similar := responses.Artist{}
similar.Id = s.ID
similar.Name = s.Name
similar.AlbumCount = s.AlbumCount
if s.Starred {
similar.Starred = &s.StarredAt
}
response.ArtistInfo.SimilarArtist = append(response.ArtistInfo.SimilarArtist, similar)
}
return response, nil return response, nil
} }
// TODO Integrate with Last.FM
func (c *BrowsingController) GetArtistInfo2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) { func (c *BrowsingController) GetArtistInfo2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
info, err := c.GetArtistInfo(w, r)
if err != nil {
return nil, err
}
response := newResponse() response := newResponse()
response.ArtistInfo2 = &responses.ArtistInfo2{} response.ArtistInfo2 = &responses.ArtistInfo2{}
response.ArtistInfo2.Biography = "Biography not available" response.ArtistInfo2.ArtistInfoBase = info.ArtistInfo.ArtistInfoBase
response.ArtistInfo2.SmallImageUrl = placeholderArtistImageSmallUrl for _, s := range info.ArtistInfo.SimilarArtist {
response.ArtistInfo2.MediumImageUrl = placeholderArtistImageSmallUrl similar := responses.ArtistID3{}
response.ArtistInfo2.LargeImageUrl = placeholderArtistImageSmallUrl similar.Id = s.Id
similar.Name = s.Name
similar.AlbumCount = s.AlbumCount
similar.Starred = s.Starred
response.ArtistInfo2.SimilarArtist = append(response.ArtistInfo2.SimilarArtist, similar)
}
return response, nil return response, nil
} }
+3 -2
View File
@@ -19,7 +19,8 @@ func initSystemController(router *Router) *SystemController {
func initBrowsingController(router *Router) *BrowsingController { func initBrowsingController(router *Router) *BrowsingController {
dataStore := router.DataStore dataStore := router.DataStore
browsingController := NewBrowsingController(dataStore) externalInfo := router.ExternalInfo
browsingController := NewBrowsingController(dataStore, externalInfo)
return browsingController return browsingController
} }
@@ -85,5 +86,5 @@ var allProviders = wire.NewSet(
NewUsersController, NewUsersController,
NewMediaRetrievalController, NewMediaRetrievalController,
NewStreamController, NewStreamController,
NewBookmarksController, engine.NewNowPlayingRepository, wire.FieldsOf(new(*Router), "Artwork", "ListGenerator", "Playlists", "Streamer", "Archiver", "DataStore"), NewBookmarksController, engine.NewNowPlayingRepository, wire.FieldsOf(new(*Router), "Artwork", "ListGenerator", "Playlists", "Streamer", "Archiver", "DataStore", "ExternalInfo"),
) )
+1 -1
View File
@@ -19,7 +19,7 @@ var allProviders = wire.NewSet(
NewStreamController, NewStreamController,
NewBookmarksController, NewBookmarksController,
engine.NewNowPlayingRepository, engine.NewNowPlayingRepository,
wire.FieldsOf(new(*Router), "Artwork", "ListGenerator", "Playlists", "Streamer", "Archiver", "DataStore"), wire.FieldsOf(new(*Router), "Artwork", "ListGenerator", "Playlists", "Streamer", "Archiver", "DataStore", "ExternalInfo"),
) )
func initSystemController(router *Router) *SystemController { func initSystemController(router *Router) *SystemController {