From e2ecdb2f3016f8f44cbb67df0052443a422123d2 Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:15:50 -0800 Subject: [PATCH] add similar artists and track radio --- adapters/listenbrainz/agent.go | 44 ++++-- adapters/listenbrainz/agent_test.go | 103 ++++++++++++- adapters/listenbrainz/client.go | 69 ++++++++- adapters/listenbrainz/client_test.go | 138 +++++++++++++++++- .../listenbrainz.labs.similar-artists.json | 1 + ....similar-recordings-real-out-of-order.json | 1 + .../listenbrainz.labs.similar-recordings.json | 1 + 7 files changed, 330 insertions(+), 27 deletions(-) create mode 100644 tests/fixtures/listenbrainz.labs.similar-artists.json create mode 100644 tests/fixtures/listenbrainz.labs.similar-recordings-real-out-of-order.json create mode 100644 tests/fixtures/listenbrainz.labs.similar-recordings.json diff --git a/adapters/listenbrainz/agent.go b/adapters/listenbrainz/agent.go index ef879654a..ccf6a19b7 100644 --- a/adapters/listenbrainz/agent.go +++ b/adapters/listenbrainz/agent.go @@ -164,7 +164,7 @@ func (l *listenBrainzAgent) GetSimilarArtists(ctx context.Context, id string, na return nil, agents.ErrNotFound } - resp, err := l.client.getSimilarArtists(ctx, mbid) + resp, err := l.client.getSimilarArtists(ctx, mbid, limit) if err != nil { return nil, err } @@ -173,17 +173,42 @@ func (l *listenBrainzAgent) GetSimilarArtists(ctx context.Context, id string, na return nil, agents.ErrNotFound } - artistCount := min(len(resp), limit) - artists := make([]agents.Artist, artistCount) + artists := make([]agents.Artist, len(resp)) - for i := 0; i < artistCount; i++ { - artists[i].MBID = resp[i].MBID - artists[i].Name = resp[i].Name + for i, artist := range resp { + artists[i].MBID = artist.MBID + artists[i].Name = artist.Name } return artists, nil } +func (l *listenBrainzAgent) GetSimilarSongsByTrack(ctx context.Context, id string, name string, artist string, mbid string, limit int) ([]agents.Song, error) { + if mbid == "" { + return nil, agents.ErrNotFound + } + + resp, err := l.client.getSimilarRecordings(ctx, mbid, limit) + if err != nil { + return nil, err + } + + if len(resp) == 0 { + return nil, agents.ErrNotFound + } + + songs := make([]agents.Song, len(resp)) + for i, song := range resp { + songs[i].Album = song.ReleaseName + songs[i].AlbumMBID = song.ReleaseMBID + songs[i].Artist = song.Artist + songs[i].MBID = song.MBID + songs[i].Name = song.Name + } + + return songs, nil +} + func init() { conf.AddHook(func() { if conf.Server.ListenBrainz.Enabled { @@ -211,7 +236,8 @@ func init() { } var ( - _ agents.ArtistTopSongsRetriever = (*listenBrainzAgent)(nil) - _ agents.ArtistURLRetriever = (*listenBrainzAgent)(nil) - _ agents.ArtistSimilarRetriever = (*listenBrainzAgent)(nil) + _ agents.ArtistTopSongsRetriever = (*listenBrainzAgent)(nil) + _ agents.ArtistURLRetriever = (*listenBrainzAgent)(nil) + _ agents.ArtistSimilarRetriever = (*listenBrainzAgent)(nil) + _ agents.SimilarSongsByTrackRetriever = (*listenBrainzAgent)(nil) ) diff --git a/adapters/listenbrainz/agent_test.go b/adapters/listenbrainz/agent_test.go index cc61e10e9..699670b2f 100644 --- a/adapters/listenbrainz/agent_test.go +++ b/adapters/listenbrainz/agent_test.go @@ -294,7 +294,8 @@ var _ = Describe("listenBrainzAgent", func() { Describe("GetSimilarArtists", func() { var agent *listenBrainzAgent var httpClient *tests.FakeHttpClient - baseUrl := "https://labs.api.listenbrainz.org/similar-artists/json" + baseUrl := "https://labs.api.listenbrainz.org/similar-artists/json?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=" + mbid := "db92a151-1ac2-438b-bc43-b82e149ddd50" BeforeEach(func() { httpClient = &tests.FakeHttpClient{} @@ -305,10 +306,10 @@ var _ = Describe("listenBrainzAgent", func() { It("returns error when fetch calls", func() { httpClient.Err = errors.New("error") - _, err := agent.GetSimilarArtists(ctx, "", "", "db92a151-1ac2-438b-bc43-b82e149ddd50", 1) + _, err := agent.GetSimilarArtists(ctx, "", "", mbid, 1) Expect(err).To(HaveOccurred()) Expect(httpClient.RequestCount).To(Equal(1)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=db92a151-1ac2-438b-bc43-b82e149ddd50")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) }) It("returns an error on listenbrainz error", func() { @@ -319,7 +320,7 @@ var _ = Describe("listenBrainzAgent", func() { _, err := agent.GetSimilarArtists(ctx, "", "", "1", 1) Expect(err).To(HaveOccurred()) Expect(httpClient.RequestCount).To(Equal(1)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=1")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "1")) }) It("returns all data on call", func() { @@ -329,7 +330,7 @@ var _ = Describe("listenBrainzAgent", func() { resp, err := agent.GetSimilarArtists(ctx, "", "", "db92a151-1ac2-438b-bc43-b82e149ddd50", 2) Expect(err).ToNot(HaveOccurred()) Expect(httpClient.RequestCount).To(Equal(1)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=db92a151-1ac2-438b-bc43-b82e149ddd50")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) Expect(resp).To(Equal([]agents.Artist{ {MBID: "f27ec8db-af05-4f36-916e-3d57f91ecf5e", Name: "Michael Jackson"}, {MBID: "7364dea6-ca9a-48e3-be01-b44ad0d19897", Name: "a-ha"}, @@ -343,10 +344,100 @@ var _ = Describe("listenBrainzAgent", func() { resp, err := agent.GetSimilarArtists(ctx, "", "", "db92a151-1ac2-438b-bc43-b82e149ddd50", 1) Expect(err).ToNot(HaveOccurred()) Expect(httpClient.RequestCount).To(Equal(1)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=db92a151-1ac2-438b-bc43-b82e149ddd50")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) Expect(resp).To(Equal([]agents.Artist{ {MBID: "f27ec8db-af05-4f36-916e-3d57f91ecf5e", Name: "Michael Jackson"}, })) }) }) + + Describe("GetSimilarTracks", func() { + var agent *listenBrainzAgent + var httpClient *tests.FakeHttpClient + mbid := "8f3471b5-7e6a-48da-86a9-c1c07a0f47ae" + baseUrl := "https://labs.api.listenbrainz.org/similar-recordings/json?algorithm=session_based_days_180_session_300_contribution_5_threshold_15_limit_50_skip_30&recording_mbids=" + + BeforeEach(func() { + httpClient = &tests.FakeHttpClient{} + client := newClient("BASE_URL", httpClient) + agent = listenBrainzConstructor(ds) + agent.client = client + }) + + It("returns error when fetch calls", func() { + httpClient.Err = errors.New("error") + _, err := agent.GetSimilarSongsByTrack(ctx, "", "", "", mbid, 1) + Expect(err).To(HaveOccurred()) + Expect(httpClient.RequestCount).To(Equal(1)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) + }) + + It("returns an error on listenbrainz error", func() { + httpClient.Res = http.Response{ + Body: io.NopCloser(bytes.NewBufferString(`Bad request`)), + StatusCode: 400, + } + _, err := agent.GetSimilarSongsByTrack(ctx, "", "", "", "1", 1) + Expect(err).To(HaveOccurred()) + Expect(httpClient.RequestCount).To(Equal(1)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "1")) + }) + + It("returns all data on call", func() { + f, _ := os.Open("tests/fixtures/listenbrainz.labs.similar-recordings.json") + httpClient.Res = http.Response{Body: f, StatusCode: 200} + + resp, err := agent.GetSimilarSongsByTrack(ctx, "", "", "", mbid, 2) + Expect(err).ToNot(HaveOccurred()) + Expect(httpClient.RequestCount).To(Equal(1)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) + Expect(resp).To(Equal([]agents.Song{ + { + ID: "", + Name: "Take On Me", + MBID: "12f65dca-de8f-43fe-a65d-f12a02aaadf3", + ISRC: "", + Artist: "a‐ha", + ArtistMBID: "", + Album: "Hunting High and Low", + AlbumMBID: "4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc", + Duration: 0, + }, + { + ID: "", + Name: "Wake Me Up Before You Go‐Go", + MBID: "80033c72-aa19-4ba8-9227-afb075fec46e", + ISRC: "", + Artist: "Wham!", + ArtistMBID: "", + Album: "Make It Big", + AlbumMBID: "c143d542-48dc-446b-b523-1762da721638", + Duration: 0, + }, + })) + }) + + It("returns subset of data on call", func() { + f, _ := os.Open("tests/fixtures/listenbrainz.labs.similar-recordings.json") + httpClient.Res = http.Response{Body: f, StatusCode: 200} + + resp, err := agent.GetSimilarSongsByTrack(ctx, "", "", "", mbid, 1) + Expect(err).ToNot(HaveOccurred()) + Expect(httpClient.RequestCount).To(Equal(1)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) + Expect(resp).To(Equal([]agents.Song{ + { + ID: "", + Name: "Take On Me", + MBID: "12f65dca-de8f-43fe-a65d-f12a02aaadf3", + ISRC: "", + Artist: "a‐ha", + ArtistMBID: "", + Album: "Hunting High and Low", + AlbumMBID: "4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc", + Duration: 0, + }, + })) + }) + }) }) diff --git a/adapters/listenbrainz/client.go b/adapters/listenbrainz/client.go index e38c16907..76aa92bd2 100644 --- a/adapters/listenbrainz/client.go +++ b/adapters/listenbrainz/client.go @@ -9,6 +9,8 @@ import ( "net/http" "net/url" "path" + "slices" + "sort" "github.com/navidrome/navidrome/log" ) @@ -17,7 +19,9 @@ const ( lbzApiUrl = "https://api.listenbrainz.org/1/" labsBase = "https://labs.api.listenbrainz.org/" // There are a couple of algorithms from https://labs.api.listenbrainz.org/similar-artists - algorithm = "session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30" + artistAlgorithm = "session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30" + // From https://labs.api.listenbrainz.org/similar-recordings + trackALgorithm = "session_based_days_180_session_300_contribution_5_threshold_15_limit_50_skip_30" ) var ( @@ -288,15 +292,16 @@ func (c *client) getArtistTopSongs(ctx context.Context, mbid string, count int) } type artist struct { - MBID string `json:"artist_mbid"` - Name string `json:"name"` + MBID string `json:"artist_mbid"` + Name string `json:"name"` + Score int `json:"score"` } -func (c *client) getSimilarArtists(ctx context.Context, mbid string) ([]artist, error) { +func (c *client) getSimilarArtists(ctx context.Context, mbid string, limit int) ([]artist, error) { req, _ := http.NewRequestWithContext(ctx, http.MethodGet, labsBase+"similar-artists/json", nil) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.URL.RawQuery = url.Values{ - "artist_mbids": []string{mbid}, "algorithm": []string{algorithm}, + "artist_mbids": []string{mbid}, "algorithm": []string{artistAlgorithm}, }.Encode() log.Trace(ctx, fmt.Sprintf("Sending ListenBrainz Labs %s request", req.Method), "url", req.URL) @@ -315,5 +320,59 @@ func (c *client) getSimilarArtists(ctx context.Context, mbid string) ([]artist, return nil, fmt.Errorf("ListenBrainz: HTTP Error, Status: (%d)", resp.StatusCode) } + if len(artists) > limit { + return artists[:limit], nil + } + return artists, nil } + +type recording struct { + MBID string `json:"recording_mbid"` + Name string `json:"recording_name"` + Artist string `json:"artist_credit_name"` + ReleaseName string `json:"release_name"` + ReleaseMBID string `json:"release_mbid"` + Score int `json:"score"` +} + +func (c *client) getSimilarRecordings(ctx context.Context, mbid string, limit int) ([]recording, error) { + req, _ := http.NewRequestWithContext(ctx, http.MethodGet, labsBase+"similar-recordings/json", nil) + req.Header.Add("Content-Type", "application/json; charset=UTF-8") + req.URL.RawQuery = url.Values{ + "recording_mbids": []string{mbid}, "algorithm": []string{trackALgorithm}, + }.Encode() + + log.Trace(ctx, fmt.Sprintf("Sending ListenBrainz Labs %s request", req.Method), "url", req.URL) + resp, err := c.hc.Do(req) + + if err != nil { + return nil, err + } + + defer resp.Body.Close() + decoder := json.NewDecoder(resp.Body) + + var recordings []recording + jsonErr := decoder.Decode(&recordings) + if jsonErr != nil { + return nil, fmt.Errorf("ListenBrainz: HTTP Error, Status: (%d)", resp.StatusCode) + } + + // For whatever reason, labs API isn't guaranteed to give results in the proper order + // and may also provide duplicates. See listenbrainz.labs.similar-recordings-real-out-of-order.json + // generated from https://labs.api.listenbrainz.org/similar-recordings/json?recording_mbids=8f3471b5-7e6a-48da-86a9-c1c07a0f47ae&algorithm=session_based_days_180_session_300_contribution_5_threshold_15_limit_50_skip_30 + sort.Slice(recordings, func(i, j int) bool { + return recordings[i].Score > recordings[j].Score + }) + + recordings = slices.CompactFunc(recordings, func(a, b recording) bool { + return a.MBID == b.MBID + }) + + if len(recordings) > limit { + return recordings[:limit], nil + } + + return recordings, nil +} diff --git a/adapters/listenbrainz/client_test.go b/adapters/listenbrainz/client_test.go index dc31e61a2..e8e4d9810 100644 --- a/adapters/listenbrainz/client_test.go +++ b/adapters/listenbrainz/client_test.go @@ -246,31 +246,155 @@ var _ = Describe("client", func() { }) Context("getSimilarArtists", func() { - baseUrl := "https://labs.api.listenbrainz.org/similar-artists/json" + baseUrl := "https://labs.api.listenbrainz.org/similar-artists/json?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=" + mbid := "db92a151-1ac2-438b-bc43-b82e149ddd50" It("handles a malformed request with status code", func() { httpClient.Res = http.Response{ Body: io.NopCloser(bytes.NewBufferString(`Bad request`)), StatusCode: 400, } - _, err := client.getSimilarArtists(context.Background(), "1") + _, err := client.getSimilarArtists(context.Background(), "1", 2) Expect(err.Error()).To(Equal("ListenBrainz: HTTP Error, Status: (400)")) Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=1")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "1")) Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8")) }) It("handles real data properly", func() { f, _ := os.Open("tests/fixtures/listenbrainz.labs.similar-artists.json") httpClient.Res = http.Response{Body: f, StatusCode: 200} - resp, err := client.getSimilarArtists(context.Background(), "db92a151-1ac2-438b-bc43-b82e149ddd50") + resp, err := client.getSimilarArtists(context.Background(), mbid, 2) Expect(err).ToNot(HaveOccurred()) Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) - Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "?algorithm=session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30&artist_mbids=db92a151-1ac2-438b-bc43-b82e149ddd50")) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8")) Expect(resp).To(Equal([]artist{ - {MBID: "f27ec8db-af05-4f36-916e-3d57f91ecf5e", Name: "Michael Jackson"}, - {MBID: "7364dea6-ca9a-48e3-be01-b44ad0d19897", Name: "a-ha"}, + {MBID: "f27ec8db-af05-4f36-916e-3d57f91ecf5e", Name: "Michael Jackson", Score: 800}, + {MBID: "7364dea6-ca9a-48e3-be01-b44ad0d19897", Name: "a-ha", Score: 792}, + })) + }) + + It("truncates data when requested", func() { + f, _ := os.Open("tests/fixtures/listenbrainz.labs.similar-artists.json") + httpClient.Res = http.Response{Body: f, StatusCode: 200} + resp, err := client.getSimilarArtists(context.Background(), "db92a151-1ac2-438b-bc43-b82e149ddd50", 1) + Expect(err).ToNot(HaveOccurred()) + Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) + Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8")) + Expect(resp).To(Equal([]artist{ + {MBID: "f27ec8db-af05-4f36-916e-3d57f91ecf5e", Name: "Michael Jackson", Score: 800}, + })) + }) + }) + + Context("getSimilarRecordings", func() { + mbid := "8f3471b5-7e6a-48da-86a9-c1c07a0f47ae" + baseUrl := "https://labs.api.listenbrainz.org/similar-recordings/json?algorithm=session_based_days_180_session_300_contribution_5_threshold_15_limit_50_skip_30&recording_mbids=" + + It("handles a malformed request with status code", func() { + httpClient.Res = http.Response{ + Body: io.NopCloser(bytes.NewBufferString(`Bad request`)), + StatusCode: 400, + } + _, err := client.getSimilarRecordings(context.Background(), "1", 2) + Expect(err.Error()).To(Equal("ListenBrainz: HTTP Error, Status: (400)")) + Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + "1")) + Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8")) + }) + + It("handles real data properly", func() { + f, _ := os.Open("tests/fixtures/listenbrainz.labs.similar-recordings.json") + httpClient.Res = http.Response{Body: f, StatusCode: 200} + resp, err := client.getSimilarRecordings(context.Background(), mbid, 2) + Expect(err).ToNot(HaveOccurred()) + Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) + Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8")) + Expect(resp).To(Equal([]recording{ + { + MBID: "12f65dca-de8f-43fe-a65d-f12a02aaadf3", + Name: "Take On Me", + Artist: "a‐ha", + ReleaseName: "Hunting High and Low", + ReleaseMBID: "4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc", + Score: 124, + }, + { + MBID: "80033c72-aa19-4ba8-9227-afb075fec46e", + Name: "Wake Me Up Before You Go‐Go", + Artist: "Wham!", + ReleaseName: "Make It Big", + ReleaseMBID: "c143d542-48dc-446b-b523-1762da721638", + Score: 65, + }, + })) + }) + + It("truncates data when requested", func() { + f, _ := os.Open("tests/fixtures/listenbrainz.labs.similar-recordings.json") + httpClient.Res = http.Response{Body: f, StatusCode: 200} + resp, err := client.getSimilarRecordings(context.Background(), mbid, 1) + Expect(err).ToNot(HaveOccurred()) + Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) + Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8")) + Expect(resp).To(Equal([]recording{ + { + MBID: "12f65dca-de8f-43fe-a65d-f12a02aaadf3", + Name: "Take On Me", + Artist: "a‐ha", + ReleaseName: "Hunting High and Low", + ReleaseMBID: "4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc", + Score: 124, + }, + })) + }) + + It("properly sorts by score and truncates duplicates", func() { + f, _ := os.Open("tests/fixtures/listenbrainz.labs.similar-recordings-real-out-of-order.json") + httpClient.Res = http.Response{Body: f, StatusCode: 200} + // There are actually 5 items. The dedup should happen FIRST + resp, err := client.getSimilarRecordings(context.Background(), mbid, 4) + Expect(err).ToNot(HaveOccurred()) + Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet)) + Expect(httpClient.SavedRequest.URL.String()).To(Equal(baseUrl + mbid)) + Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8")) + Expect(resp).To(Equal([]recording{ + { + MBID: "12f65dca-de8f-43fe-a65d-f12a02aaadf3", + Name: "Take On Me", + Artist: "a‐ha", + ReleaseName: "Hunting High and Low", + ReleaseMBID: "4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc", + Score: 124, + }, + { + MBID: "e4b347be-ecb2-44ff-aaa8-3d4c517d7ea5", + Name: "Everybody Wants to Rule the World", + Artist: "Tears for Fears", + ReleaseName: "Songs From the Big Chair", + ReleaseMBID: "21f19b06-81f1-347a-add5-5d0c77696597", + Score: 68, + }, + { + MBID: "80033c72-aa19-4ba8-9227-afb075fec46e", + Name: "Wake Me Up Before You Go‐Go", + Artist: "Wham!", + ReleaseName: "Make It Big", + ReleaseMBID: "c143d542-48dc-446b-b523-1762da721638", + Score: 65, + }, + { + MBID: "ef4c6855-949e-4e22-b41e-8e0a2d372d5f", + Name: "Tainted Love", + Artist: "Soft Cell", + ReleaseName: "Non-Stop Erotic Cabaret", + ReleaseMBID: "1acaa870-6e0c-4b6e-9e91-fdec4e5ea4b1", + Score: 61, + }, })) }) }) diff --git a/tests/fixtures/listenbrainz.labs.similar-artists.json b/tests/fixtures/listenbrainz.labs.similar-artists.json new file mode 100644 index 000000000..1cd2c85b5 --- /dev/null +++ b/tests/fixtures/listenbrainz.labs.similar-artists.json @@ -0,0 +1 @@ +[{"artist_mbid": "f27ec8db-af05-4f36-916e-3d57f91ecf5e", "name": "Michael Jackson", "comment": "\u201cKing of Pop\u201d", "type": "Person", "gender": "Male", "score": 800, "reference_mbid": "db92a151-1ac2-438b-bc43-b82e149ddd50"}, {"artist_mbid": "7364dea6-ca9a-48e3-be01-b44ad0d19897", "name": "a-ha", "comment": "Norwegian synth\u2010pop band", "type": "Group", "gender": null, "score": 792, "reference_mbid": "db92a151-1ac2-438b-bc43-b82e149ddd50"}] \ No newline at end of file diff --git a/tests/fixtures/listenbrainz.labs.similar-recordings-real-out-of-order.json b/tests/fixtures/listenbrainz.labs.similar-recordings-real-out-of-order.json new file mode 100644 index 000000000..61913bcf5 --- /dev/null +++ b/tests/fixtures/listenbrainz.labs.similar-recordings-real-out-of-order.json @@ -0,0 +1 @@ +[{"recording_mbid":"12f65dca-de8f-43fe-a65d-f12a02aaadf3","recording_name":"Take On Me","artist_credit_name":"a‐ha","artist_credit_mbids":null,"release_name":"Hunting High and Low","release_mbid":"4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc","caa_id":13015069966,"caa_release_mbid":"181b9a01-0446-4601-99be-b011ab615631","score":124,"reference_mbid":"8f3471b5-7e6a-48da-86a9-c1c07a0f47ae"},{"recording_mbid":"12f65dca-de8f-43fe-a65d-f12a02aaadf3","recording_name":"Take On Me","artist_credit_name":"a‐ha","artist_credit_mbids":null,"release_name":"Hunting High and Low","release_mbid":"4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc","caa_id":13015069966,"caa_release_mbid":"181b9a01-0446-4601-99be-b011ab615631","score":124,"reference_mbid":"8f3471b5-7e6a-48da-86a9-c1c07a0f47ae"},{"recording_mbid":"80033c72-aa19-4ba8-9227-afb075fec46e","recording_name":"Wake Me Up Before You Go‐Go","artist_credit_name":"Wham!","artist_credit_mbids":null,"release_name":"Make It Big","release_mbid":"c143d542-48dc-446b-b523-1762da721638","caa_id":2622532701,"caa_release_mbid":"ec01ad0c-a28f-4d45-bed7-d73014161c38","score":65,"reference_mbid":"8f3471b5-7e6a-48da-86a9-c1c07a0f47ae"},{"recording_mbid":"ef4c6855-949e-4e22-b41e-8e0a2d372d5f","recording_name":"Tainted Love","artist_credit_name":"Soft Cell","artist_credit_mbids":null,"release_name":"Non-Stop Erotic Cabaret","release_mbid":"1acaa870-6e0c-4b6e-9e91-fdec4e5ea4b1","caa_id":1031647403,"caa_release_mbid":"c3367d3a-2f6c-48d1-95c5-c1ee7a49c479","score":61,"reference_mbid":"8f3471b5-7e6a-48da-86a9-c1c07a0f47ae"},{"recording_mbid":"e4b347be-ecb2-44ff-aaa8-3d4c517d7ea5","recording_name":"Everybody Wants to Rule the World","artist_credit_name":"Tears for Fears","artist_credit_mbids":null,"release_name":"Songs From the Big Chair","release_mbid":"21f19b06-81f1-347a-add5-5d0c77696597","caa_id":19682986993,"caa_release_mbid":"9aefc6dd-216a-4271-ada1-d9cf67956f39","score":68,"reference_mbid":"8f3471b5-7e6a-48da-86a9-c1c07a0f47ae"}] \ No newline at end of file diff --git a/tests/fixtures/listenbrainz.labs.similar-recordings.json b/tests/fixtures/listenbrainz.labs.similar-recordings.json new file mode 100644 index 000000000..87323f426 --- /dev/null +++ b/tests/fixtures/listenbrainz.labs.similar-recordings.json @@ -0,0 +1 @@ +[{"recording_mbid":"12f65dca-de8f-43fe-a65d-f12a02aaadf3","recording_name":"Take On Me","artist_credit_name":"a‐ha","artist_credit_mbids":null,"release_name":"Hunting High and Low","release_mbid":"4ec07fe8-e7c6-3106-a0aa-fdf92f13f7fc","caa_id":13015069966,"caa_release_mbid":"181b9a01-0446-4601-99be-b011ab615631","score":124,"reference_mbid":"8f3471b5-7e6a-48da-86a9-c1c07a0f47ae"},{"recording_mbid":"80033c72-aa19-4ba8-9227-afb075fec46e","recording_name":"Wake Me Up Before You Go‐Go","artist_credit_name":"Wham!","artist_credit_mbids":null,"release_name":"Make It Big","release_mbid":"c143d542-48dc-446b-b523-1762da721638","caa_id":2622532701,"caa_release_mbid":"ec01ad0c-a28f-4d45-bed7-d73014161c38","score":65,"reference_mbid":"8f3471b5-7e6a-48da-86a9-c1c07a0f47ae"}] \ No newline at end of file