fix(lyrics): use normalized Genius response (#517)

This commit is contained in:
zarzet
2026-08-11 17:54:15 +07:00
parent 7e429f8373
commit 8333069b95
2 changed files with 32 additions and 2 deletions
+12 -2
View File
@@ -151,7 +151,7 @@ func parsePaxsenixLyricsPayload(raw, provider string, multiPersonWordByWord bool
if err := json.Unmarshal([]byte(raw), &lrcPayload); err == nil {
lrcPayload = strings.TrimSpace(lrcPayload)
if lrcPayload == "" {
return nil, fmt.Errorf("%s returned empty lyrics", provider)
return nil, lyricsServiceUnavailableErrorf("%s returned empty lyrics", provider)
}
return lyricsResponseFromText(lrcPayload, provider), nil
}
@@ -191,7 +191,13 @@ func parsePaxsenixLyricsPayload(raw, provider string, multiPersonWordByWord bool
if trimmed != "" && !strings.HasPrefix(trimmed, "{") && !strings.HasPrefix(trimmed, "[") {
return lyricsResponseFromText(trimmed, provider), nil
}
return nil, fmt.Errorf("failed to decode %s lyrics response", provider)
if json.Valid([]byte(trimmed)) {
return nil, lyricsServiceUnavailableErrorf(
"%s returned a response without usable lyrics",
provider,
)
}
return nil, lyricsServiceUnavailableErrorf("failed to decode %s lyrics response", provider)
}
// lyricsResponseFromLRCText parses LRC-or-plain text into a response, or nil
@@ -541,6 +547,10 @@ func (c *GeniusLyricsClient) FetchLyrics(trackName, artistName string, durationS
params := url.Values{}
params.Set("url", geniusURL)
// The legacy v1 contract can report success with an empty lyrics string.
// v2 keeps the same string payload shape while using the maintained
// normalized Genius extractor.
params.Set("v", "2")
raw, err := fetchPaxsenixBody(c.httpClient, "https://lyrics.paxsenix.org/genius/lyrics", params)
if err != nil {
return nil, fmt.Errorf("genius lyrics fetch failed: %w", err)
+20
View File
@@ -165,6 +165,23 @@ func TestLyricsCacheParsingAndLRCLibClient(t *testing.T) {
}
}
func TestPaxsenixEmptyLyricsPayloadMarksProviderUnavailable(t *testing.T) {
lyrics, err := parsePaxsenixLyricsPayload(
`{"error":false,"lyrics":""}`,
"Genius",
false,
)
if lyrics != nil || err == nil {
t.Fatalf("empty PAX Senix payload = %#v/%v", lyrics, err)
}
if !isLyricsProviderUnavailableError(err) {
t.Fatalf("empty PAX Senix payload was not marked unavailable: %v", err)
}
if strings.Contains(strings.ToLower(err.Error()), "decode") {
t.Fatalf("empty JSON payload was misreported as a decode failure: %v", err)
}
}
func TestLyricsProviderHealthSkipsUnavailableProvider(t *testing.T) {
SetLyricsProviderOrder([]string{LyricsProviderLRCLIB})
defer SetLyricsProviderOrder(nil)
@@ -493,6 +510,9 @@ func TestExternalLyricsProvidersWithFakeHTTP(t *testing.T) {
}
return &http.Response{StatusCode: 200, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(`{"response":{"sections":[{"hits":[{"type":"song","result":{"title":"Song","primary_artist_names":"Artist","url":"https://genius.com/artist-song-lyrics"}}]}]}}`)), Request: req}, nil
case strings.Contains(req.URL.Path, "/genius/lyrics"):
if got := req.URL.Query().Get("v"); got != "2" {
t.Fatalf("genius API version = %q", got)
}
return &http.Response{StatusCode: 200, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(`{"error":false,"lyrics":"Genius line"}`)), Request: req}, nil
default:
return &http.Response{StatusCode: 404, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(`{}`)), Request: req}, nil