feat(discovery): web ListenBrainz similar-artists client
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { similarArtists } from "./listenbrainz";
|
||||
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
describe("similarArtists", () => {
|
||||
it("parses rows and drops the seed itself", async () => {
|
||||
vi.spyOn(global, "fetch").mockResolvedValue(
|
||||
new Response(JSON.stringify([
|
||||
{ artist_mbid: "seed", name: "Seed", score: 100 },
|
||||
{ artist_mbid: "c1", name: "Cand", score: 42 },
|
||||
]), { status: 200 }),
|
||||
);
|
||||
const out = await similarArtists("seed");
|
||||
expect(out).toEqual([{ mbid: "c1", name: "Cand", score: 42 }]);
|
||||
});
|
||||
|
||||
it("throws on a non-2xx response", async () => {
|
||||
vi.spyOn(global, "fetch").mockResolvedValue(new Response("nope", { status: 500 }));
|
||||
await expect(similarArtists("seed")).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
const LB_BASE = process.env.LISTENBRAINZ_URL ?? "https://labs.api.listenbrainz.org";
|
||||
// Keep in sync with the worker's _listenbrainz.py default algorithm.
|
||||
const ALGORITHM =
|
||||
"session_based_days_7500_session_300_contribution_5_threshold_15_limit_50_filter_True_skip_30";
|
||||
|
||||
export type SimilarArtist = { mbid: string; name: string; score: number };
|
||||
|
||||
export async function similarArtists(artistMbid: string): Promise<SimilarArtist[]> {
|
||||
const url =
|
||||
`${LB_BASE}/similar-artists/json?artist_mbids=${encodeURIComponent(artistMbid)}` +
|
||||
`&algorithm=${encodeURIComponent(ALGORITHM)}`;
|
||||
const res = await fetch(url, { headers: { Accept: "application/json" } });
|
||||
if (!res.ok) throw new Error(`ListenBrainz request failed: ${res.status}`);
|
||||
const rows: any[] = (await res.json()) ?? [];
|
||||
return rows
|
||||
.filter((r) => r.artist_mbid && r.artist_mbid !== artistMbid)
|
||||
.map((r) => ({ mbid: r.artist_mbid, name: r.name ?? r.comment ?? "", score: Number(r.score ?? 0) }));
|
||||
}
|
||||
Reference in New Issue
Block a user