feat: add MusicBrainz artist-ID resolver

Add ResolveArtistMBID to query the MB artist search endpoint and return the
first matching artist ID, with httptest-backed tests for match, no-match,
HTTP error, and invalid-JSON paths.
This commit is contained in:
2026-07-19 22:16:43 +03:00
parent 85c42ec858
commit 40c4240693
3 changed files with 156 additions and 4 deletions

View File

@@ -0,0 +1,47 @@
package musicbrainz
import (
"context"
"encoding/json"
"fmt"
"net/url"
)
// mbArtistSearchResult models the JSON response of the MusicBrainz artist
// search endpoint (/ws/2/artist?query=artist:<name>&fmt=json). Only the
// fields we need for MBID resolution are decoded.
type mbArtistSearchResult struct {
Artists []struct {
ID string `json:"id"`
Name string `json:"name"`
Score int `json:"score"`
} `json:"artists"`
}
// ResolveArtistMBID resolves a MusicBrainz artist ID (MBID) for the given
// artist name by querying the MusicBrainz artist search endpoint. It returns
// the ID of the first (best-scoring) matching artist. An error is returned if
// the search yields no matches, the response cannot be parsed, or the
// underlying request fails.
func (c *MusicBrainzClient) ResolveArtistMBID(ctx context.Context, name string) (string, error) {
params := url.Values{}
params.Set("query", fmt.Sprintf("artist:%s", name))
params.Set("fmt", "json")
path := "/artist?" + params.Encode()
body, err := c.doGet(ctx, path)
if err != nil {
return "", fmt.Errorf("resolve MBID for artist %q: %w", name, err)
}
var result mbArtistSearchResult
if err := json.Unmarshal(body, &result); err != nil {
return "", fmt.Errorf("parse artist search response for %q: %w", name, err)
}
if len(result.Artists) == 0 {
return "", fmt.Errorf("no MusicBrainz artist found for %q", name)
}
return result.Artists[0].ID, nil
}