diff --git a/conf/configuration.go b/conf/configuration.go index cbbc3fab..672d046c 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -98,6 +98,7 @@ type spotifyOptions struct { type listenBrainzOptions struct { Enabled bool + BaseURL string } var ( @@ -254,6 +255,7 @@ func init() { viper.SetDefault("spotify.id", "") viper.SetDefault("spotify.secret", "") viper.SetDefault("listenbrainz.enabled", true) + viper.SetDefault("listenbrainz.baseurl", "https://api.listenbrainz.org/1/") // DevFlags. These are used to enable/disable debugging and incomplete features viper.SetDefault("devlogsourceline", false) diff --git a/core/agents/listenbrainz/agent.go b/core/agents/listenbrainz/agent.go index 05b80be3..e6a83aaa 100644 --- a/core/agents/listenbrainz/agent.go +++ b/core/agents/listenbrainz/agent.go @@ -21,6 +21,7 @@ const ( type listenBrainzAgent struct { ds model.DataStore sessionKeys *agents.SessionKeys + baseURL string client *Client } @@ -28,12 +29,13 @@ func listenBrainzConstructor(ds model.DataStore) *listenBrainzAgent { l := &listenBrainzAgent{ ds: ds, sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty}, + baseURL: conf.Server.ListenBrainz.BaseURL, } hc := &http.Client{ Timeout: consts.DefaultHttpClientTimeOut, } chc := utils.NewCachedHTTPClient(hc, consts.DefaultHttpClientTimeOut) - l.client = NewClient(chc) + l.client = NewClient(l.baseURL, chc) return l } diff --git a/core/agents/listenbrainz/agent_test.go b/core/agents/listenbrainz/agent_test.go index 657d0778..a05f4d64 100644 --- a/core/agents/listenbrainz/agent_test.go +++ b/core/agents/listenbrainz/agent_test.go @@ -29,7 +29,7 @@ var _ = Describe("listenBrainzAgent", func() { _ = ds.UserProps(ctx).Put("user-1", sessionKeyProperty, "SK-1") httpClient = &tests.FakeHttpClient{} agent = listenBrainzConstructor(ds) - agent.client = NewClient(httpClient) + agent.client = NewClient("http://localhost:8080", httpClient) track = &model.MediaFile{ ID: "123", Title: "Track Title", diff --git a/core/agents/listenbrainz/auth_router.go b/core/agents/listenbrainz/auth_router.go index 96c5599f..d34dfaa7 100644 --- a/core/agents/listenbrainz/auth_router.go +++ b/core/agents/listenbrainz/auth_router.go @@ -8,6 +8,7 @@ import ( "github.com/deluan/rest" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" + "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/core/agents" "github.com/navidrome/navidrome/log" @@ -38,7 +39,7 @@ func NewRouter(ds model.DataStore) *Router { hc := &http.Client{ Timeout: consts.DefaultHttpClientTimeOut, } - r.client = NewClient(hc) + r.client = NewClient(conf.Server.ListenBrainz.BaseURL, hc) return r } diff --git a/core/agents/listenbrainz/auth_router_test.go b/core/agents/listenbrainz/auth_router_test.go index 522369b4..0e068336 100644 --- a/core/agents/listenbrainz/auth_router_test.go +++ b/core/agents/listenbrainz/auth_router_test.go @@ -24,7 +24,7 @@ var _ = Describe("ListenBrainz Auth Router", func() { BeforeEach(func() { sk = &fakeSessionKeys{KeyName: sessionKeyProperty} httpClient = &tests.FakeHttpClient{} - cl := NewClient(httpClient) + cl := NewClient("http://localhost/", httpClient) r = Router{ sessionKeys: sk, client: cl, diff --git a/core/agents/listenbrainz/client.go b/core/agents/listenbrainz/client.go index dbb0941c..674a9587 100644 --- a/core/agents/listenbrainz/client.go +++ b/core/agents/listenbrainz/client.go @@ -6,14 +6,12 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" + "path" "github.com/navidrome/navidrome/log" ) -const ( - apiBaseUrl = "https://api.listenbrainz.org/1/" -) - type listenBrainzError struct { Code int Message string @@ -27,12 +25,13 @@ type httpDoer interface { Do(req *http.Request) (*http.Response, error) } -func NewClient(hc httpDoer) *Client { - return &Client{hc} +func NewClient(baseURL string, hc httpDoer) *Client { + return &Client{baseURL, hc} } type Client struct { - hc httpDoer + baseURL string + hc httpDoer } type listenBrainzResponse struct { @@ -128,9 +127,22 @@ func (c *Client) Scrobble(ctx context.Context, apiKey string, li listenInfo) err return nil } +func (c *Client) path(endpoint string) (string, error) { + u, err := url.Parse(c.baseURL) + if err != nil { + return "", err + } + u.Path = path.Join(u.Path, endpoint) + return u.String(), nil +} + func (c *Client) makeRequest(method string, endpoint string, r *listenBrainzRequest) (*listenBrainzResponse, error) { b, _ := json.Marshal(r.Body) - req, _ := http.NewRequest(method, apiBaseUrl+endpoint, bytes.NewBuffer(b)) + uri, err := c.path(endpoint) + if err != nil { + return nil, err + } + req, _ := http.NewRequest(method, uri, bytes.NewBuffer(b)) if r.ApiKey != "" { req.Header.Add("Authorization", fmt.Sprintf("Token %s", r.ApiKey)) diff --git a/core/agents/listenbrainz/client_test.go b/core/agents/listenbrainz/client_test.go index a7970431..d32896e4 100644 --- a/core/agents/listenbrainz/client_test.go +++ b/core/agents/listenbrainz/client_test.go @@ -18,7 +18,7 @@ var _ = Describe("Client", func() { var client *Client BeforeEach(func() { httpClient = &tests.FakeHttpClient{} - client = NewClient(httpClient) + client = NewClient("BASE_URL/", httpClient) }) Describe("listenBrainzResponse", func() { @@ -48,7 +48,7 @@ var _ = Describe("Client", func() { _, err := client.ValidateToken(context.Background(), "LB-TOKEN") Expect(err).ToNot(HaveOccurred()) Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "validate-token")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal("BASE_URL/validate-token")) Expect(httpClient.SavedRequest.Header.Get("Authorization")).To(Equal("Token LB-TOKEN")) }) @@ -86,7 +86,7 @@ var _ = Describe("Client", func() { It("formats the request properly", func() { Expect(client.UpdateNowPlaying(context.Background(), "LB-TOKEN", li)).To(Succeed()) Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodPost)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "submit-listens")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal("BASE_URL/submit-listens")) Expect(httpClient.SavedRequest.Header.Get("Authorization")).To(Equal("Token LB-TOKEN")) body, _ := io.ReadAll(httpClient.SavedRequest.Body) @@ -103,7 +103,7 @@ var _ = Describe("Client", func() { It("formats the request properly", func() { Expect(client.Scrobble(context.Background(), "LB-TOKEN", li)).To(Succeed()) Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodPost)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "submit-listens")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal("BASE_URL/submit-listens")) Expect(httpClient.SavedRequest.Header.Get("Authorization")).To(Equal("Token LB-TOKEN")) body, _ := io.ReadAll(httpClient.SavedRequest.Body) diff --git a/server/initial_setup.go b/server/initial_setup.go index 3718c255..0044a0a7 100644 --- a/server/initial_setup.go +++ b/server/initial_setup.go @@ -100,7 +100,7 @@ func checkExternalCredentials() { if !conf.Server.ListenBrainz.Enabled { log.Info("ListenBrainz integration is DISABLED") } else { - log.Debug("ListenBrainz integration is ENABLED") + log.Debug("ListenBrainz integration is ENABLED", "ListenBrainz.BaseURL", conf.Server.ListenBrainz.BaseURL) } if conf.Server.Spotify.ID == "" || conf.Server.Spotify.Secret == "" {