diff --git a/coverart.go b/coverart.go index 46592a0..81b8ceb 100644 --- a/coverart.go +++ b/coverart.go @@ -84,17 +84,21 @@ func uploadToUguu(imageData []byte, contentType string) (string, error) { body = append(body, imageData...) body = append(body, []byte(fmt.Sprintf("\r\n--%s--\r\n", boundary))...) - req := pdk.NewHTTPRequest(pdk.MethodPost, "https://uguu.se/upload") - req.SetHeader("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", boundary)) - req.SetBody(body) - - resp := req.Send() - if resp.Status() >= 400 { - return "", fmt.Errorf("uguu.se upload failed: HTTP %d", resp.Status()) + resp, err := host.HTTPSend(host.HTTPRequest{ + Method: "POST", + URL: "https://uguu.se/upload", + Headers: map[string]string{"Content-Type": fmt.Sprintf("multipart/form-data; boundary=%s", boundary)}, + Body: body, + }) + if err != nil { + return "", fmt.Errorf("uguu.se upload failed: %w", err) + } + if resp.StatusCode >= 400 { + return "", fmt.Errorf("uguu.se upload failed: HTTP %d", resp.StatusCode) } var result uguuResponse - if err := json.Unmarshal(resp.Body(), &result); err != nil { + if err := json.Unmarshal(resp.Body, &result); err != nil { return "", fmt.Errorf("failed to parse uguu.se response: %w", err) } diff --git a/coverart_test.go b/coverart_test.go index e3131e8..8d9eeeb 100644 --- a/coverart_test.go +++ b/coverart_test.go @@ -20,6 +20,8 @@ var _ = Describe("getImageURL", func() { host.ArtworkMock.Calls = nil host.SubsonicAPIMock.ExpectedCalls = nil host.SubsonicAPIMock.Calls = nil + host.HTTPMock.ExpectedCalls = nil + host.HTTPMock.Calls = nil pdk.PDKMock.On("Log", mock.Anything, mock.Anything).Maybe() }) @@ -71,10 +73,9 @@ var _ = Describe("getImageURL", func() { Return("image/jpeg", imageData, nil) // Mock uguu.se HTTP upload - uguuReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://uguu.se/upload").Return(uguuReq) - pdk.PDKMock.On("Send", uguuReq).Return(pdk.NewStubHTTPResponse(200, nil, - []byte(`{"success":true,"files":[{"url":"https://a.uguu.se/uploaded.jpg"}]}`))) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.URL == "https://uguu.se/upload" + })).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`{"success":true,"files":[{"url":"https://a.uguu.se/uploaded.jpg"}]}`)}, nil) // Mock cache set host.CacheMock.On("SetString", "uguu.artwork.track1", "https://a.uguu.se/uploaded.jpg", int64(9000)).Return(nil) @@ -98,9 +99,9 @@ var _ = Describe("getImageURL", func() { host.SubsonicAPIMock.On("CallRaw", "/getCoverArt?u=testuser&id=track1&size=300"). Return("image/jpeg", []byte("fake-image-data"), nil) - uguuReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://uguu.se/upload").Return(uguuReq) - pdk.PDKMock.On("Send", uguuReq).Return(pdk.NewStubHTTPResponse(500, nil, []byte(`{"success":false}`))) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.URL == "https://uguu.se/upload" + })).Return(&host.HTTPResponse{StatusCode: 500, Body: []byte(`{"success":false}`)}, nil) url := getImageURL("testuser", "track1") Expect(url).To(BeEmpty()) diff --git a/go.mod b/go.mod index dbaa9df..1bd00d1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module discord-rich-presence go 1.25 require ( - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0-20260207182358-29f98b889b11 + github.com/navidrome/navidrome/plugins/pdk/go v0.0.0-20260224182233-40719d928320 github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 github.com/stretchr/testify v1.11.1 diff --git a/go.sum b/go.sum index 702fa22..1fcf1d8 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/maruel/natural v1.3.0 h1:VsmCsBmEyrR46RomtgHs5hbKADGRVtliHTyCOLFBpsg= github.com/maruel/natural v1.3.0/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= -github.com/navidrome/navidrome/plugins/pdk/go v0.0.0-20260207182358-29f98b889b11 h1:VE4bqzkS6apWDtco9hAGdThFttjbYoLR0DEILAGDyyc= -github.com/navidrome/navidrome/plugins/pdk/go v0.0.0-20260207182358-29f98b889b11/go.mod h1:HijQ0Z0OeEa6LIwUJh6H9WqAptye096jHazmKXf+YV4= +github.com/navidrome/navidrome/plugins/pdk/go v0.0.0-20260224182233-40719d928320 h1:TVn0Jv9Xd4aoyTbBoSMAv38Mfh8lWX/kMP2au2KX1cQ= +github.com/navidrome/navidrome/plugins/pdk/go v0.0.0-20260224182233-40719d928320/go.mod h1:HijQ0Z0OeEa6LIwUJh6H9WqAptye096jHazmKXf+YV4= github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= diff --git a/main_test.go b/main_test.go index a1b6ed0..ce399fb 100644 --- a/main_test.go +++ b/main_test.go @@ -33,6 +33,8 @@ var _ = Describe("discordPlugin", func() { host.ArtworkMock.Calls = nil host.SubsonicAPIMock.ExpectedCalls = nil host.SubsonicAPIMock.Calls = nil + host.HTTPMock.ExpectedCalls = nil + host.HTTPMock.Calls = nil }) Describe("getConfig", func() { @@ -128,9 +130,9 @@ var _ = Describe("discordPlugin", func() { // Mock HTTP GET request for gateway discovery gatewayResp := []byte(`{"url":"wss://gateway.discord.gg"}`) - gatewayReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodGet, "https://discord.com/api/gateway").Return(gatewayReq).Once() - pdk.PDKMock.On("Send", gatewayReq).Return(pdk.NewStubHTTPResponse(200, nil, gatewayResp)).Once() + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.Method == "GET" && req.URL == "https://discord.com/api/gateway" + })).Return(&host.HTTPResponse{StatusCode: 200, Body: gatewayResp}, nil) // Mock WebSocket connection host.WebSocketMock.On("Connect", mock.MatchedBy(func(url string) bool { @@ -148,9 +150,7 @@ var _ = Describe("discordPlugin", func() { host.ArtworkMock.On("GetTrackUrl", "track1", int32(300)).Return("https://example.com/art.jpg", nil) // Mock HTTP POST requests (Discord external assets API) - postReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(postReq) - pdk.PDKMock.On("Send", postReq).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`{}`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`{}`)}, nil) // Schedule clear activity callback host.SchedulerMock.On("ScheduleOneTime", mock.Anything, payloadClearActivity, "testuser-clear").Return("testuser-clear", nil) @@ -180,9 +180,9 @@ var _ = Describe("discordPlugin", func() { // Connect mocks host.CacheMock.On("GetInt", "discord.seq.testuser").Return(int64(0), false, errors.New("not found")) gatewayResp := []byte(`{"url":"wss://gateway.discord.gg"}`) - gatewayReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodGet, "https://discord.com/api/gateway").Return(gatewayReq).Once() - pdk.PDKMock.On("Send", gatewayReq).Return(pdk.NewStubHTTPResponse(200, nil, gatewayResp)).Once() + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.Method == "GET" && req.URL == "https://discord.com/api/gateway" + })).Return(&host.HTTPResponse{StatusCode: 200, Body: gatewayResp}, nil) host.WebSocketMock.On("Connect", mock.MatchedBy(func(url string) bool { return strings.Contains(url, "gateway.discord.gg") }), mock.Anything, "testuser").Return("testuser", nil) @@ -199,9 +199,7 @@ var _ = Describe("discordPlugin", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) host.CacheMock.On("SetString", discordImageKey, mock.Anything, mock.Anything).Return(nil) host.ArtworkMock.On("GetTrackUrl", "track1", int32(300)).Return("https://example.com/art.jpg", nil) - postReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(postReq) - pdk.PDKMock.On("Send", postReq).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`{}`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`{}`)}, nil) host.SchedulerMock.On("ScheduleOneTime", mock.Anything, payloadClearActivity, "testuser-clear").Return("testuser-clear", nil) err := plugin.NowPlaying(scrobbler.NowPlayingRequest{ diff --git a/plugin_suite_test.go b/plugin_suite_test.go index 8f69496..9e183c7 100644 --- a/plugin_suite_test.go +++ b/plugin_suite_test.go @@ -4,6 +4,7 @@ import ( "strings" "testing" + "github.com/navidrome/navidrome/plugins/pdk/go/host" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stretchr/testify/mock" @@ -17,6 +18,6 @@ func TestDiscordPlugin(t *testing.T) { // Shared matchers for tighter mock expectations across all test files. var ( discordImageKey = mock.MatchedBy(func(key string) bool { return strings.HasPrefix(key, "discord.image.") }) - externalAssetsURL = mock.MatchedBy(func(url string) bool { return strings.Contains(url, "external-assets") }) + externalAssetsReq = mock.MatchedBy(func(req host.HTTPRequest) bool { return strings.Contains(req.URL, "external-assets") }) spotifyURLKey = mock.MatchedBy(func(key string) bool { return strings.HasPrefix(key, "spotify.url.") }) ) diff --git a/rpc.go b/rpc.go index d3a2682..0b15bb3 100644 --- a/rpc.go +++ b/rpc.go @@ -147,18 +147,22 @@ func (r *discordRPC) processImage(imageURL, clientID, token string, ttl int64) ( // Process via Discord API body := fmt.Sprintf(`{"urls":[%q]}`, imageURL) - req := pdk.NewHTTPRequest(pdk.MethodPost, fmt.Sprintf("https://discord.com/api/v9/applications/%s/external-assets", clientID)) - req.SetHeader("Authorization", token) - req.SetHeader("Content-Type", "application/json") - req.SetBody([]byte(body)) - - resp := req.Send() - if resp.Status() >= 400 { - return "", fmt.Errorf("failed to process image: HTTP %d", resp.Status()) + resp, err := host.HTTPSend(host.HTTPRequest{ + Method: "POST", + URL: fmt.Sprintf("https://discord.com/api/v9/applications/%s/external-assets", clientID), + Headers: map[string]string{"Authorization": token, "Content-Type": "application/json"}, + Body: []byte(body), + }) + if err != nil { + pdk.Log(pdk.LogWarn, fmt.Sprintf("HTTP request failed for image processing: %v", err)) + return "", fmt.Errorf("failed to process image: %w", err) + } + if resp.StatusCode >= 400 { + return "", fmt.Errorf("failed to process image: HTTP %d", resp.StatusCode) } var data []map[string]string - if err := json.Unmarshal(resp.Body(), &data); err != nil { + if err := json.Unmarshal(resp.Body, &data); err != nil { return "", fmt.Errorf("failed to unmarshal image response: %w", err) } @@ -257,14 +261,20 @@ func (r *discordRPC) sendMessage(username string, opCode int, payload any) error // getDiscordGateway retrieves the Discord gateway URL. func (r *discordRPC) getDiscordGateway() (string, error) { - req := pdk.NewHTTPRequest(pdk.MethodGet, "https://discord.com/api/gateway") - resp := req.Send() - if resp.Status() != 200 { - return "", fmt.Errorf("failed to get Discord gateway: HTTP %d", resp.Status()) + resp, err := host.HTTPSend(host.HTTPRequest{ + Method: "GET", + URL: "https://discord.com/api/gateway", + }) + if err != nil { + pdk.Log(pdk.LogWarn, fmt.Sprintf("HTTP request failed for Discord gateway: %v", err)) + return "", fmt.Errorf("failed to get Discord gateway: %w", err) + } + if resp.StatusCode != 200 { + return "", fmt.Errorf("failed to get Discord gateway: HTTP %d", resp.StatusCode) } var result map[string]string - if err := json.Unmarshal(resp.Body(), &result); err != nil { + if err := json.Unmarshal(resp.Body, &result); err != nil { return "", fmt.Errorf("failed to parse Discord gateway response: %w", err) } return result["url"], nil diff --git a/rpc_test.go b/rpc_test.go index fbee7a3..be5dca5 100644 --- a/rpc_test.go +++ b/rpc_test.go @@ -25,6 +25,8 @@ var _ = Describe("discordRPC", func() { host.WebSocketMock.Calls = nil host.SchedulerMock.ExpectedCalls = nil host.SchedulerMock.Calls = nil + host.HTTPMock.ExpectedCalls = nil + host.HTTPMock.Calls = nil }) Describe("sendMessage", func() { @@ -81,9 +83,9 @@ var _ = Describe("discordRPC", func() { // Mock HTTP GET request for gateway discovery gatewayResp := []byte(`{"url":"wss://gateway.discord.gg"}`) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodGet, "https://discord.com/api/gateway").Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(200, nil, gatewayResp)) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.Method == "GET" && req.URL == "https://discord.com/api/gateway" + })).Return(&host.HTTPResponse{StatusCode: 200, Body: gatewayResp}, nil) // Mock WebSocket connection host.WebSocketMock.On("Connect", mock.MatchedBy(func(url string) bool { @@ -265,9 +267,7 @@ var _ = Describe("discordRPC", func() { return val == "mp:external/new-asset" }), int64(imageCacheTTL)).Return(nil) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`[{"external_asset_path":"external/new-asset"}]`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[{"external_asset_path":"external/new-asset"}]`)}, nil) result, err := r.processImage("https://example.com/art.jpg", "client123", "token123", imageCacheTTL) Expect(err).ToNot(HaveOccurred()) @@ -277,9 +277,7 @@ var _ = Describe("discordRPC", func() { It("returns error on HTTP failure", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(500, nil, []byte(`error`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 500, Body: []byte(`error`)}, nil) _, err := r.processImage("https://example.com/art.jpg", "client123", "token123", imageCacheTTL) Expect(err).To(HaveOccurred()) @@ -289,9 +287,7 @@ var _ = Describe("discordRPC", func() { It("returns error on unmarshal failure", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`{"not":"an-array"}`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`{"not":"an-array"}`)}, nil) _, err := r.processImage("https://example.com/art.jpg", "client123", "token123", imageCacheTTL) Expect(err).To(HaveOccurred()) @@ -301,9 +297,7 @@ var _ = Describe("discordRPC", func() { It("returns error on empty response array", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`[]`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[]`)}, nil) _, err := r.processImage("https://example.com/art.jpg", "client123", "token123", imageCacheTTL) Expect(err).To(HaveOccurred()) @@ -313,9 +307,7 @@ var _ = Describe("discordRPC", func() { It("returns error on empty external_asset_path", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`[{"external_asset_path":""}]`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[{"external_asset_path":""}]`)}, nil) _, err := r.processImage("https://example.com/art.jpg", "client123", "token123", imageCacheTTL) Expect(err).To(HaveOccurred()) @@ -332,9 +324,7 @@ var _ = Describe("discordRPC", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) host.CacheMock.On("SetString", discordImageKey, mock.Anything, mock.Anything).Return(nil) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`[{"external_asset_path":"external/art"}]`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[{"external_asset_path":"external/art"}]`)}, nil) host.WebSocketMock.On("SendText", "testuser", mock.MatchedBy(func(msg string) bool { return strings.Contains(msg, `"op":3`) && @@ -364,15 +354,9 @@ var _ = Describe("discordRPC", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) host.CacheMock.On("SetString", discordImageKey, mock.Anything, mock.Anything).Return(nil) - trackReq := &pdk.HTTPRequest{} - defaultReq := &pdk.HTTPRequest{} - // First call (track art) returns 500, second call (default) succeeds - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(trackReq).Once() - pdk.PDKMock.On("Send", trackReq).Return(pdk.NewStubHTTPResponse(500, nil, []byte(`error`))).Once() - - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(defaultReq).Once() - pdk.PDKMock.On("Send", defaultReq).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`[{"external_asset_path":"external/logo"}]`))).Once() + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 500, Body: []byte(`error`)}, nil).Once() + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[{"external_asset_path":"external/logo"}]`)}, nil).Once() host.WebSocketMock.On("SendText", "testuser", mock.MatchedBy(func(msg string) bool { return strings.Contains(msg, `"op":3`) && @@ -400,9 +384,7 @@ var _ = Describe("discordRPC", func() { It("clears all images when both track art and default fail", func() { host.CacheMock.On("GetString", discordImageKey).Return("", false, nil) - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`{"not":"array"}`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`{"not":"array"}`)}, nil) host.WebSocketMock.On("SendText", "testuser", mock.MatchedBy(func(msg string) bool { return strings.Contains(msg, `"op":3`) && @@ -431,9 +413,7 @@ var _ = Describe("discordRPC", func() { host.CacheMock.On("GetString", discordImageKey).Return("mp:cached/large", true, nil).Once() host.CacheMock.On("GetString", discordImageKey).Return("", false, nil).Once() - httpReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, externalAssetsURL).Return(httpReq) - pdk.PDKMock.On("Send", mock.Anything).Return(pdk.NewStubHTTPResponse(500, nil, []byte(`error`))) + host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 500, Body: []byte(`error`)}, nil) host.WebSocketMock.On("SendText", "testuser", mock.MatchedBy(func(msg string) bool { return strings.Contains(msg, `"large_image":"mp:cached/large"`) && diff --git a/spotify.go b/spotify.go index ef9b862..11ef8a8 100644 --- a/spotify.go +++ b/spotify.go @@ -49,19 +49,23 @@ func spotifyCacheKey(artist, title, album string) string { // trySpotifyFromMBID calls the ListenBrainz spotify-id-from-mbid endpoint. func trySpotifyFromMBID(mbid string) string { body := fmt.Sprintf(`[{"recording_mbid":%q}]`, mbid) - req := pdk.NewHTTPRequest(pdk.MethodPost, "https://labs.api.listenbrainz.org/spotify-id-from-mbid/json") - req.SetHeader("Content-Type", "application/json") - req.SetBody([]byte(body)) - - resp := req.Send() - status := resp.Status() - if status < 200 || status >= 300 { - pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz MBID lookup failed: HTTP %d, body=%s", status, string(resp.Body()))) + resp, err := host.HTTPSend(host.HTTPRequest{ + Method: "POST", + URL: "https://labs.api.listenbrainz.org/spotify-id-from-mbid/json", + Headers: map[string]string{"Content-Type": "application/json"}, + Body: []byte(body), + }) + if err != nil { + pdk.Log(pdk.LogInfo, fmt.Sprintf("ListenBrainz MBID lookup request failed: %v", err)) return "" } - id := parseSpotifyID(resp.Body()) + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz MBID lookup failed: HTTP %d, body=%s", resp.StatusCode, string(resp.Body))) + return "" + } + id := parseSpotifyID(resp.Body) if id == "" { - pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz MBID lookup returned no spotify_track_id for mbid=%s, body=%s", mbid, string(resp.Body()))) + pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz MBID lookup returned no spotify_track_id for mbid=%s, body=%s", mbid, string(resp.Body))) } return id } @@ -69,20 +73,25 @@ func trySpotifyFromMBID(mbid string) string { // trySpotifyFromMetadata calls the ListenBrainz spotify-id-from-metadata endpoint. func trySpotifyFromMetadata(artist, title, album string) string { payload := fmt.Sprintf(`[{"artist_name":%q,"track_name":%q,"release_name":%q}]`, artist, title, album) - req := pdk.NewHTTPRequest(pdk.MethodPost, "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json") - req.SetHeader("Content-Type", "application/json") - req.SetBody([]byte(payload)) pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz metadata request: %s", payload)) - resp := req.Send() - status := resp.Status() - if status < 200 || status >= 300 { - pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz metadata lookup failed: HTTP %d, body=%s", status, string(resp.Body()))) + resp, err := host.HTTPSend(host.HTTPRequest{ + Method: "POST", + URL: "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json", + Headers: map[string]string{"Content-Type": "application/json"}, + Body: []byte(payload), + }) + if err != nil { + pdk.Log(pdk.LogInfo, fmt.Sprintf("ListenBrainz metadata lookup request failed: %v", err)) return "" } - pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz metadata response: HTTP %d, body=%s", status, string(resp.Body()))) - id := parseSpotifyID(resp.Body()) + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz metadata lookup failed: HTTP %d, body=%s", resp.StatusCode, string(resp.Body))) + return "" + } + pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz metadata response: HTTP %d, body=%s", resp.StatusCode, string(resp.Body))) + id := parseSpotifyID(resp.Body) if id == "" { pdk.Log(pdk.LogDebug, fmt.Sprintf("ListenBrainz metadata returned no spotify_track_id for %q - %q", artist, title)) } diff --git a/spotify_test.go b/spotify_test.go index 73e9dc3..0926dd8 100644 --- a/spotify_test.go +++ b/spotify_test.go @@ -118,6 +118,8 @@ var _ = Describe("Spotify", func() { pdk.ResetMock() host.CacheMock.ExpectedCalls = nil host.CacheMock.Calls = nil + host.HTTPMock.ExpectedCalls = nil + host.HTTPMock.Calls = nil pdk.PDKMock.On("Log", mock.Anything, mock.Anything).Maybe() }) @@ -138,10 +140,9 @@ var _ = Describe("Spotify", func() { host.CacheMock.On("SetString", spotifyURLKey, mock.Anything, mock.Anything).Return(nil) // Mock the MBID HTTP request - mbidReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://labs.api.listenbrainz.org/spotify-id-from-mbid/json").Return(mbidReq) - pdk.PDKMock.On("Send", mbidReq).Return(pdk.NewStubHTTPResponse(200, nil, - []byte(`[{"spotify_track_ids":["63OQupATfueTdZMWIV7nzz"]}]`))) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.URL == "https://labs.api.listenbrainz.org/spotify-id-from-mbid/json" + })).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[{"spotify_track_ids":["63OQupATfueTdZMWIV7nzz"]}]`)}, nil) url := resolveSpotifyURL(scrobbler.TrackInfo{ Title: "Karma Police", @@ -159,15 +160,14 @@ var _ = Describe("Spotify", func() { host.CacheMock.On("SetString", spotifyURLKey, mock.Anything, mock.Anything).Return(nil) // MBID request fails - mbidReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://labs.api.listenbrainz.org/spotify-id-from-mbid/json").Return(mbidReq) - pdk.PDKMock.On("Send", mbidReq).Return(pdk.NewStubHTTPResponse(404, nil, []byte(`[]`))) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.URL == "https://labs.api.listenbrainz.org/spotify-id-from-mbid/json" + })).Return(&host.HTTPResponse{StatusCode: 404, Body: []byte(`[]`)}, nil) // Metadata request succeeds - metaReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json").Return(metaReq) - pdk.PDKMock.On("Send", metaReq).Return(pdk.NewStubHTTPResponse(200, nil, - []byte(`[{"spotify_track_ids":["4wlLbLeDWbA6TzwZFp1UaK"]}]`))) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.URL == "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json" + })).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[{"spotify_track_ids":["4wlLbLeDWbA6TzwZFp1UaK"]}]`)}, nil) url := resolveSpotifyURL(scrobbler.TrackInfo{ Title: "Karma Police", @@ -184,9 +184,9 @@ var _ = Describe("Spotify", func() { host.CacheMock.On("SetString", spotifyURLKey, mock.Anything, mock.Anything).Return(nil) // No MBID, metadata request fails - metaReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json").Return(metaReq) - pdk.PDKMock.On("Send", metaReq).Return(pdk.NewStubHTTPResponse(500, nil, []byte(`error`))) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.URL == "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json" + })).Return(&host.HTTPResponse{StatusCode: 500, Body: []byte(`error`)}, nil) url := resolveSpotifyURL(scrobbler.TrackInfo{ Title: "Karma Police", @@ -203,10 +203,9 @@ var _ = Describe("Spotify", func() { host.CacheMock.On("GetString", spotifyURLKey).Return("", false, nil) host.CacheMock.On("SetString", spotifyURLKey, mock.Anything, mock.Anything).Return(nil) - metaReq := &pdk.HTTPRequest{} - pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json").Return(metaReq) - pdk.PDKMock.On("Send", metaReq).Return(pdk.NewStubHTTPResponse(200, nil, - []byte(`[{"spotify_track_ids":["4tIGK5G9hNDA50ZdGioZRG"]}]`))) + host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool { + return req.URL == "https://labs.api.listenbrainz.org/spotify-id-from-metadata/json" + })).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`[{"spotify_track_ids":["4tIGK5G9hNDA50ZdGioZRG"]}]`)}, nil) url := resolveSpotifyURL(scrobbler.TrackInfo{ Title: "Some Song",