diff --git a/web/src/lib/musicbrainz.test.ts b/web/src/lib/musicbrainz.test.ts index a322c23..bd3fb3f 100644 --- a/web/src/lib/musicbrainz.test.ts +++ b/web/src/lib/musicbrainz.test.ts @@ -47,4 +47,13 @@ describe("musicbrainz module", () => { vi.stubGlobal("fetch", vi.fn(() => jsonResponse({ "release-groups": [] }))); expect(await searchReleaseGroup("Nobody", "Nothing")).toBeNull(); }); + + it("escapes quotes in the release-group query", async () => { + const fetchMock = vi.fn((_url: string, _init?: RequestInit) => jsonResponse({ "release-groups": [] })); + vi.stubGlobal("fetch", fetchMock); + await searchReleaseGroup("AC/DC", 'Back in "Black"'); + const [url] = fetchMock.mock.calls[0]; + // the decoded query must contain the escaped quote, not a bare one that closes the phrase + expect(decodeURIComponent(url)).toContain('Back in \\"Black\\"'); + }); }); diff --git a/web/src/lib/musicbrainz.ts b/web/src/lib/musicbrainz.ts index 896b47c..55f55e5 100644 --- a/web/src/lib/musicbrainz.ts +++ b/web/src/lib/musicbrainz.ts @@ -54,8 +54,12 @@ export async function browseReleaseGroups(artistMbid: string): Promise "\\" + c); +} + export async function searchReleaseGroup(artist: string, album: string): Promise { - const query = `releasegroup:"${album}" AND artist:"${artist}"`; + const query = `releasegroup:"${escapeLucenePhrase(album)}" AND artist:"${escapeLucenePhrase(artist)}"`; const data = await mbGet(`/release-group?query=${encodeURIComponent(query)}&fmt=json&limit=5`); const groups: any[] = data["release-groups"] ?? []; if (groups.length === 0) return null;