mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
Merge 5c5cae09705842e4edbebca8f0518e2d3e378346 into 13c48b38a0737236b79af02b4a7bd42cb6ee1b27
This commit is contained in:
commit
0d3820ad92
@ -64,7 +64,7 @@ func lastFMConstructor(ds model.DataStore) *lastfmAgent {
|
|||||||
}
|
}
|
||||||
chc := cache.NewHTTPClient(hc, consts.DefaultHttpClientTimeOut)
|
chc := cache.NewHTTPClient(hc, consts.DefaultHttpClientTimeOut)
|
||||||
l.httpClient = chc
|
l.httpClient = chc
|
||||||
l.client = newClient(l.apiKey, l.secret, chc)
|
l.client = newClient(l.apiKey, l.secret, conf.Server.LastFM.BaseURL, chc)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
var httpClient *tests.FakeHttpClient
|
var httpClient *tests.FakeHttpClient
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client := newClient("API_KEY", "SECRET", httpClient)
|
client := newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = client
|
agent.client = client
|
||||||
})
|
})
|
||||||
@ -114,7 +114,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
It("returns content in first language when available (1 API call)", func() {
|
It("returns content in first language when available (1 API call)", func() {
|
||||||
conf.Server.LastFM.Languages = []string{"pt", "en"}
|
conf.Server.LastFM.Languages = []string{"pt", "en"}
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = newClient("API_KEY", "SECRET", httpClient)
|
agent.client = newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
|
|
||||||
// Portuguese biography available
|
// Portuguese biography available
|
||||||
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
|
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
|
||||||
@ -131,7 +131,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
It("falls back to second language when first returns empty (2 API calls)", func() {
|
It("falls back to second language when first returns empty (2 API calls)", func() {
|
||||||
conf.Server.LastFM.Languages = []string{"ja", "en"}
|
conf.Server.LastFM.Languages = []string{"ja", "en"}
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = newClient("API_KEY", "SECRET", httpClient)
|
agent.client = newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
|
|
||||||
// Japanese returns empty/ignored biography (actual Last.fm response with just "Read more" link)
|
// Japanese returns empty/ignored biography (actual Last.fm response with just "Read more" link)
|
||||||
fJa, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.empty.json")
|
fJa, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.empty.json")
|
||||||
@ -152,7 +152,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
It("returns ErrNotFound when all languages return empty", func() {
|
It("returns ErrNotFound when all languages return empty", func() {
|
||||||
conf.Server.LastFM.Languages = []string{"ja", "xx"}
|
conf.Server.LastFM.Languages = []string{"ja", "xx"}
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = newClient("API_KEY", "SECRET", httpClient)
|
agent.client = newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
|
|
||||||
// Both languages return empty/ignored biography (using actual Last.fm response format)
|
// Both languages return empty/ignored biography (using actual Last.fm response format)
|
||||||
fJa, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.empty.json")
|
fJa, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.empty.json")
|
||||||
@ -179,7 +179,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
It("falls back to second language when first returns empty description (2 API calls)", func() {
|
It("falls back to second language when first returns empty description (2 API calls)", func() {
|
||||||
conf.Server.LastFM.Languages = []string{"ja", "en"}
|
conf.Server.LastFM.Languages = []string{"ja", "en"}
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = newClient("API_KEY", "SECRET", httpClient)
|
agent.client = newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
|
|
||||||
// Japanese returns album without wiki/description (actual Last.fm response)
|
// Japanese returns album without wiki/description (actual Last.fm response)
|
||||||
fJa, _ := os.Open("tests/fixtures/lastfm.album.getinfo.empty.json")
|
fJa, _ := os.Open("tests/fixtures/lastfm.album.getinfo.empty.json")
|
||||||
@ -201,7 +201,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
It("returns album without description when all languages return empty", func() {
|
It("returns album without description when all languages return empty", func() {
|
||||||
conf.Server.LastFM.Languages = []string{"ja", "xx"}
|
conf.Server.LastFM.Languages = []string{"ja", "xx"}
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = newClient("API_KEY", "SECRET", httpClient)
|
agent.client = newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
|
|
||||||
// Both languages return album without description
|
// Both languages return album without description
|
||||||
fJa, _ := os.Open("tests/fixtures/lastfm.album.getinfo.empty.json")
|
fJa, _ := os.Open("tests/fixtures/lastfm.album.getinfo.empty.json")
|
||||||
@ -224,7 +224,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
var httpClient *tests.FakeHttpClient
|
var httpClient *tests.FakeHttpClient
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client := newClient("API_KEY", "SECRET", httpClient)
|
client := newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = client
|
agent.client = client
|
||||||
})
|
})
|
||||||
@ -262,7 +262,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
var httpClient *tests.FakeHttpClient
|
var httpClient *tests.FakeHttpClient
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client := newClient("API_KEY", "SECRET", httpClient)
|
client := newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = client
|
agent.client = client
|
||||||
})
|
})
|
||||||
@ -300,7 +300,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
var httpClient *tests.FakeHttpClient
|
var httpClient *tests.FakeHttpClient
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client := newClient("API_KEY", "SECRET", httpClient)
|
client := newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = client
|
agent.client = client
|
||||||
})
|
})
|
||||||
@ -350,7 +350,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
_ = ds.UserProps(ctx).Put("user-1", sessionKeyProperty, "SK-1")
|
_ = ds.UserProps(ctx).Put("user-1", sessionKeyProperty, "SK-1")
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client := newClient("API_KEY", "SECRET", httpClient)
|
client := newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = client
|
agent.client = client
|
||||||
track = &model.MediaFile{
|
track = &model.MediaFile{
|
||||||
@ -524,7 +524,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
var httpClient *tests.FakeHttpClient
|
var httpClient *tests.FakeHttpClient
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client := newClient("API_KEY", "SECRET", httpClient)
|
client := newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = client
|
agent.client = client
|
||||||
})
|
})
|
||||||
@ -594,7 +594,7 @@ var _ = Describe("lastfmAgent", func() {
|
|||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
apiClient = &tests.FakeHttpClient{}
|
apiClient = &tests.FakeHttpClient{}
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client := newClient("API_KEY", "SECRET", apiClient)
|
client := newClient("API_KEY", "SECRET", "", apiClient)
|
||||||
agent = lastFMConstructor(ds)
|
agent = lastFMConstructor(ds)
|
||||||
agent.client = client
|
agent.client = client
|
||||||
agent.httpClient = httpClient
|
agent.httpClient = httpClient
|
||||||
|
|||||||
@ -31,6 +31,7 @@ type Router struct {
|
|||||||
client *client
|
client *client
|
||||||
apiKey string
|
apiKey string
|
||||||
secret string
|
secret string
|
||||||
|
authURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(ds model.DataStore) *Router {
|
func NewRouter(ds model.DataStore) *Router {
|
||||||
@ -38,13 +39,14 @@ func NewRouter(ds model.DataStore) *Router {
|
|||||||
ds: ds,
|
ds: ds,
|
||||||
apiKey: conf.Server.LastFM.ApiKey,
|
apiKey: conf.Server.LastFM.ApiKey,
|
||||||
secret: conf.Server.LastFM.Secret,
|
secret: conf.Server.LastFM.Secret,
|
||||||
|
authURL: conf.Server.LastFM.AuthURL,
|
||||||
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
|
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
|
||||||
}
|
}
|
||||||
r.Handler = r.routes()
|
r.Handler = r.routes()
|
||||||
hc := &http.Client{
|
hc := &http.Client{
|
||||||
Timeout: consts.DefaultHttpClientTimeOut,
|
Timeout: consts.DefaultHttpClientTimeOut,
|
||||||
}
|
}
|
||||||
r.client = newClient(r.apiKey, r.secret, hc)
|
r.client = newClient(r.apiKey, r.secret, conf.Server.LastFM.BaseURL, hc)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +68,8 @@ func (s *Router) routes() http.Handler {
|
|||||||
|
|
||||||
func (s *Router) getLinkStatus(w http.ResponseWriter, r *http.Request) {
|
func (s *Router) getLinkStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
resp := map[string]any{
|
resp := map[string]any{
|
||||||
"apiKey": s.apiKey,
|
"apiKey": s.apiKey,
|
||||||
|
"authUrl": s.authURL,
|
||||||
}
|
}
|
||||||
u, _ := request.UserFrom(r.Context())
|
u, _ := request.UserFrom(r.Context())
|
||||||
key, err := s.sessionKeys.Get(r.Context(), u.ID)
|
key, err := s.sessionKeys.Get(r.Context(), u.ID)
|
||||||
|
|||||||
@ -14,13 +14,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/navidrome/navidrome/consts"
|
||||||
"github.com/navidrome/navidrome/log"
|
"github.com/navidrome/navidrome/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
apiBaseUrl = "https://ws.audioscrobbler.com/2.0/"
|
|
||||||
)
|
|
||||||
|
|
||||||
type lastFMError struct {
|
type lastFMError struct {
|
||||||
Code int
|
Code int
|
||||||
Message string
|
Message string
|
||||||
@ -34,14 +31,15 @@ type httpDoer interface {
|
|||||||
Do(req *http.Request) (*http.Response, error)
|
Do(req *http.Request) (*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClient(apiKey string, secret string, hc httpDoer) *client {
|
func newClient(apiKey string, secret string, baseURL string, hc httpDoer) *client {
|
||||||
return &client{apiKey, secret, hc}
|
return &client{apiKey, secret, normalizeLastFMBaseURL(baseURL), hc}
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
secret string
|
secret string
|
||||||
hc httpDoer
|
baseURL string
|
||||||
|
hc httpDoer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) albumGetInfo(ctx context.Context, name string, artist string, mbid string, lang string) (*Album, error) {
|
func (c *client) albumGetInfo(ctx context.Context, name string, artist string, mbid string, lang string) (*Album, error) {
|
||||||
@ -200,10 +198,10 @@ func (c *client) makeRequest(ctx context.Context, method string, params url.Valu
|
|||||||
var req *http.Request
|
var req *http.Request
|
||||||
if method == http.MethodPost {
|
if method == http.MethodPost {
|
||||||
body := strings.NewReader(params.Encode())
|
body := strings.NewReader(params.Encode())
|
||||||
req, _ = http.NewRequestWithContext(ctx, method, apiBaseUrl, body)
|
req, _ = http.NewRequestWithContext(ctx, method, c.baseURL, body)
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
} else {
|
} else {
|
||||||
req, _ = http.NewRequestWithContext(ctx, method, apiBaseUrl, nil)
|
req, _ = http.NewRequestWithContext(ctx, method, c.baseURL, nil)
|
||||||
req.URL.RawQuery = params.Encode()
|
req.URL.RawQuery = params.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,6 +229,17 @@ func (c *client) makeRequest(ctx context.Context, method string, params url.Valu
|
|||||||
return &response, nil
|
return &response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeLastFMBaseURL(baseURL string) string {
|
||||||
|
baseURL = strings.TrimSpace(baseURL)
|
||||||
|
if baseURL == "" {
|
||||||
|
return consts.DefaultLastFMBaseURL
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(baseURL, "/") {
|
||||||
|
return baseURL + "/"
|
||||||
|
}
|
||||||
|
return baseURL
|
||||||
|
}
|
||||||
|
|
||||||
func (c *client) sign(params url.Values) {
|
func (c *client) sign(params url.Values) {
|
||||||
// the parameters must be in order before hashing
|
// the parameters must be in order before hashing
|
||||||
keys := make([]string, 0, len(params))
|
keys := make([]string, 0, len(params))
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/navidrome/navidrome/consts"
|
||||||
"github.com/navidrome/navidrome/tests"
|
"github.com/navidrome/navidrome/tests"
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
@ -22,7 +23,7 @@ var _ = Describe("client", func() {
|
|||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
httpClient = &tests.FakeHttpClient{}
|
httpClient = &tests.FakeHttpClient{}
|
||||||
client = newClient("API_KEY", "SECRET", httpClient)
|
client = newClient("API_KEY", "SECRET", "", httpClient)
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("albumGetInfo", func() {
|
Describe("albumGetInfo", func() {
|
||||||
@ -33,7 +34,7 @@ var _ = Describe("client", func() {
|
|||||||
album, err := client.albumGetInfo(context.Background(), "Believe", "U2", "mbid-1234", "pt")
|
album, err := client.albumGetInfo(context.Background(), "Believe", "U2", "mbid-1234", "pt")
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(album.Name).To(Equal("Believe"))
|
Expect(album.Name).To(Equal("Believe"))
|
||||||
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?album=Believe&api_key=API_KEY&artist=U2&format=json&lang=pt&mbid=mbid-1234&method=album.getInfo"))
|
Expect(httpClient.SavedRequest.URL.String()).To(Equal(consts.DefaultLastFMBaseURL + "?album=Believe&api_key=API_KEY&artist=U2&format=json&lang=pt&mbid=mbid-1234&method=album.getInfo"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ var _ = Describe("client", func() {
|
|||||||
artist, err := client.artistGetInfo(context.Background(), "U2", "pt")
|
artist, err := client.artistGetInfo(context.Background(), "U2", "pt")
|
||||||
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(consts.DefaultLastFMBaseURL + "?api_key=API_KEY&artist=U2&format=json&lang=pt&method=artist.getInfo"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("fails if Last.fm returns an http status != 200", func() {
|
It("fails if Last.fm returns an http status != 200", func() {
|
||||||
@ -105,7 +106,7 @@ var _ = Describe("client", func() {
|
|||||||
similar, err := client.artistGetSimilar(context.Background(), "U2", 2)
|
similar, err := client.artistGetSimilar(context.Background(), "U2", 2)
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(len(similar.Artists)).To(Equal(2))
|
Expect(len(similar.Artists)).To(Equal(2))
|
||||||
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&limit=2&method=artist.getSimilar"))
|
Expect(httpClient.SavedRequest.URL.String()).To(Equal(consts.DefaultLastFMBaseURL + "?api_key=API_KEY&artist=U2&format=json&limit=2&method=artist.getSimilar"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -117,7 +118,7 @@ var _ = Describe("client", func() {
|
|||||||
top, err := client.artistGetTopTracks(context.Background(), "U2", 2)
|
top, err := client.artistGetTopTracks(context.Background(), "U2", 2)
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(len(top.Track)).To(Equal(2))
|
Expect(len(top.Track)).To(Equal(2))
|
||||||
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&limit=2&method=artist.getTopTracks"))
|
Expect(httpClient.SavedRequest.URL.String()).To(Equal(consts.DefaultLastFMBaseURL + "?api_key=API_KEY&artist=U2&format=json&limit=2&method=artist.getTopTracks"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -132,7 +133,7 @@ var _ = Describe("client", func() {
|
|||||||
Expect(similar.Track[0].Name).To(Equal("Dreaming of Me"))
|
Expect(similar.Track[0].Name).To(Equal("Dreaming of Me"))
|
||||||
Expect(similar.Track[0].Artist.Name).To(Equal("Depeche Mode"))
|
Expect(similar.Track[0].Artist.Name).To(Equal("Depeche Mode"))
|
||||||
Expect(similar.Track[0].Match).To(Equal(1.0))
|
Expect(similar.Track[0].Match).To(Equal(1.0))
|
||||||
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=Depeche+Mode&format=json&limit=5&method=track.getSimilar&track=Just+Can%27t+Get+Enough"))
|
Expect(httpClient.SavedRequest.URL.String()).To(Equal(consts.DefaultLastFMBaseURL + "?api_key=API_KEY&artist=Depeche+Mode&format=json&limit=5&method=track.getSimilar&track=Just+Can%27t+Get+Enough"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns empty list when no similar tracks found", func() {
|
It("returns empty list when no similar tracks found", func() {
|
||||||
|
|||||||
@ -185,6 +185,8 @@ type lastfmOptions struct {
|
|||||||
Enabled bool
|
Enabled bool
|
||||||
ApiKey string //nolint:gosec
|
ApiKey string //nolint:gosec
|
||||||
Secret string //nolint:gosec
|
Secret string //nolint:gosec
|
||||||
|
BaseURL string
|
||||||
|
AuthURL string
|
||||||
Language string
|
Language string
|
||||||
ScrobbleFirstArtistOnly bool
|
ScrobbleFirstArtistOnly bool
|
||||||
|
|
||||||
@ -822,6 +824,8 @@ func setViperDefaults() {
|
|||||||
viper.SetDefault("lastfm.language", consts.DefaultInfoLanguage)
|
viper.SetDefault("lastfm.language", consts.DefaultInfoLanguage)
|
||||||
viper.SetDefault("lastfm.apikey", "")
|
viper.SetDefault("lastfm.apikey", "")
|
||||||
viper.SetDefault("lastfm.secret", "")
|
viper.SetDefault("lastfm.secret", "")
|
||||||
|
viper.SetDefault("lastfm.baseurl", consts.DefaultLastFMBaseURL)
|
||||||
|
viper.SetDefault("lastfm.authurl", consts.DefaultLastFMAuthURL)
|
||||||
viper.SetDefault("lastfm.scrobblefirstartistonly", false)
|
viper.SetDefault("lastfm.scrobblefirstartistonly", false)
|
||||||
viper.SetDefault("deezer.enabled", true)
|
viper.SetDefault("deezer.enabled", true)
|
||||||
viper.SetDefault("deezer.language", consts.DefaultInfoLanguage)
|
viper.SetDefault("deezer.language", consts.DefaultInfoLanguage)
|
||||||
|
|||||||
@ -75,6 +75,8 @@ const (
|
|||||||
DefaultUIPlaybackReportInterval = time.Minute
|
DefaultUIPlaybackReportInterval = time.Minute
|
||||||
|
|
||||||
DefaultHttpClientTimeOut = 10 * time.Second
|
DefaultHttpClientTimeOut = 10 * time.Second
|
||||||
|
DefaultLastFMBaseURL = "https://ws.audioscrobbler.com/2.0/"
|
||||||
|
DefaultLastFMAuthURL = "https://www.last.fm/api/auth/"
|
||||||
|
|
||||||
DefaultListenBrainzBaseURL = "https://api.listenbrainz.org/1/"
|
DefaultListenBrainzBaseURL = "https://api.listenbrainz.org/1/"
|
||||||
DefaultListenBrainzArtistAlgorithm = "session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30"
|
DefaultListenBrainzArtistAlgorithm = "session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30"
|
||||||
|
|||||||
@ -12,8 +12,15 @@ import { useInterval } from '../common'
|
|||||||
import { baseUrl, openInNewTab } from '../utils'
|
import { baseUrl, openInNewTab } from '../utils'
|
||||||
import { httpClient } from '../dataProvider'
|
import { httpClient } from '../dataProvider'
|
||||||
|
|
||||||
|
const buildAuthUrl = (authUrl, apiKey, callbackUrl) => {
|
||||||
|
const url = new URL(authUrl)
|
||||||
|
url.searchParams.set('api_key', apiKey)
|
||||||
|
url.searchParams.set('cb', callbackUrl)
|
||||||
|
return url.toString()
|
||||||
|
}
|
||||||
|
|
||||||
const Progress = (props) => {
|
const Progress = (props) => {
|
||||||
const { setLinked, setCheckingLink, apiKey } = props
|
const { setLinked, setCheckingLink, apiKey, authUrl } = props
|
||||||
const notify = useNotify()
|
const notify = useNotify()
|
||||||
let linkCheckDelay = 2000
|
let linkCheckDelay = 2000
|
||||||
let linkChecks = 30
|
let linkChecks = 30
|
||||||
@ -24,10 +31,16 @@ const Progress = (props) => {
|
|||||||
`/api/lastfm/link/callback?uid=${localStorage.getItem('userId')}`,
|
`/api/lastfm/link/callback?uid=${localStorage.getItem('userId')}`,
|
||||||
)
|
)
|
||||||
const callbackUrl = `${window.location.origin}${callbackEndpoint}`
|
const callbackUrl = `${window.location.origin}${callbackEndpoint}`
|
||||||
openedTab.current = openInNewTab(
|
try {
|
||||||
`https://www.last.fm/api/auth/?api_key=${apiKey}&cb=${callbackUrl}`,
|
openedTab.current = openInNewTab(
|
||||||
)
|
buildAuthUrl(authUrl, apiKey, callbackUrl),
|
||||||
}, [apiKey])
|
)
|
||||||
|
} catch {
|
||||||
|
setCheckingLink(false)
|
||||||
|
setLinked(false)
|
||||||
|
notify('message.lastfmLinkFailure', 'warning')
|
||||||
|
}
|
||||||
|
}, [apiKey, authUrl, notify, setCheckingLink, setLinked])
|
||||||
|
|
||||||
const endChecking = (success) => {
|
const endChecking = (success) => {
|
||||||
linkCheckDelay = null
|
linkCheckDelay = null
|
||||||
@ -76,12 +89,14 @@ export const LastfmScrobbleToggle = (props) => {
|
|||||||
const [linked, setLinked] = useState(null)
|
const [linked, setLinked] = useState(null)
|
||||||
const [checkingLink, setCheckingLink] = useState(false)
|
const [checkingLink, setCheckingLink] = useState(false)
|
||||||
const [apiKey, setApiKey] = useState(false)
|
const [apiKey, setApiKey] = useState(false)
|
||||||
|
const [authUrl, setAuthUrl] = useState(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
httpClient('/api/lastfm/link')
|
httpClient('/api/lastfm/link')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setLinked(response.json.status === true)
|
setLinked(response.json.status === true)
|
||||||
setApiKey(response.json.apiKey)
|
setApiKey(response.json.apiKey)
|
||||||
|
setAuthUrl(response.json.authUrl || null)
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setLinked(false)
|
setLinked(false)
|
||||||
@ -90,6 +105,10 @@ export const LastfmScrobbleToggle = (props) => {
|
|||||||
|
|
||||||
const toggleScrobble = () => {
|
const toggleScrobble = () => {
|
||||||
if (!linked) {
|
if (!linked) {
|
||||||
|
if (!authUrl) {
|
||||||
|
notify('message.lastfmLinkFailure', 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
setCheckingLink(true)
|
setCheckingLink(true)
|
||||||
} else {
|
} else {
|
||||||
httpClient('/api/lastfm/link', { method: 'DELETE' })
|
httpClient('/api/lastfm/link', { method: 'DELETE' })
|
||||||
@ -109,7 +128,7 @@ export const LastfmScrobbleToggle = (props) => {
|
|||||||
id={'lastfm'}
|
id={'lastfm'}
|
||||||
color="primary"
|
color="primary"
|
||||||
checked={linked || checkingLink}
|
checked={linked || checkingLink}
|
||||||
disabled={!apiKey || linked === null || checkingLink}
|
disabled={!apiKey || !authUrl || linked === null || checkingLink}
|
||||||
onChange={toggleScrobble}
|
onChange={toggleScrobble}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@ -122,6 +141,7 @@ export const LastfmScrobbleToggle = (props) => {
|
|||||||
setLinked={setLinked}
|
setLinked={setLinked}
|
||||||
setCheckingLink={setCheckingLink}
|
setCheckingLink={setCheckingLink}
|
||||||
apiKey={apiKey}
|
apiKey={apiKey}
|
||||||
|
authUrl={authUrl}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!apiKey && (
|
{!apiKey && (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user