mirror of
https://github.com/navidrome/navidrome.git
synced 2026-02-15 01:11:20 -05:00
* feat(lastfm): support multiple languages for album and artist info retrieval Signed-off-by: Deluan <deluan@navidrome.org> * fix(lastfm): improve content validation for album and artist descriptions Signed-off-by: Deluan <deluan@navidrome.org> * refactor(lastfm): remove single language test and clarify languages field in configuration Signed-off-by: Deluan <deluan@navidrome.org> * feat(deezer): support multiple languages for artist bio retrieval Signed-off-by: Deluan <deluan@navidrome.org> * refactor(lastfm): rename ignoredBiographies to ignoredContent for clarity Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
172 lines
5.4 KiB
Go
172 lines
5.4 KiB
Go
package deezer
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/navidrome/navidrome/conf"
|
|
"github.com/navidrome/navidrome/conf/configtest"
|
|
"github.com/navidrome/navidrome/core/agents"
|
|
"github.com/navidrome/navidrome/tests"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("deezerAgent", func() {
|
|
var ctx context.Context
|
|
|
|
BeforeEach(func() {
|
|
ctx = context.Background()
|
|
DeferCleanup(configtest.SetupConfig())
|
|
conf.Server.Deezer.Enabled = true
|
|
})
|
|
|
|
Describe("deezerConstructor", func() {
|
|
It("uses configured languages", func() {
|
|
conf.Server.Deezer.Languages = []string{"pt", "en"}
|
|
agent := deezerConstructor(&tests.MockDataStore{}).(*deezerAgent)
|
|
Expect(agent.languages).To(Equal([]string{"pt", "en"}))
|
|
})
|
|
})
|
|
|
|
Describe("GetArtistBiography - Language Fallback", func() {
|
|
var agent *deezerAgent
|
|
var httpClient *langAwareHttpClient
|
|
|
|
BeforeEach(func() {
|
|
httpClient = newLangAwareHttpClient()
|
|
|
|
// Mock search artist (returns Michael Jackson)
|
|
fSearch, _ := os.Open("tests/fixtures/deezer.search.artist.json")
|
|
httpClient.searchResponse = &http.Response{Body: fSearch, StatusCode: 200}
|
|
|
|
// Mock JWT token
|
|
testJWT := createTestJWT(5 * time.Minute)
|
|
httpClient.jwtResponse = &http.Response{
|
|
StatusCode: 200,
|
|
Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s","refresh_token":""}`, testJWT))),
|
|
}
|
|
})
|
|
|
|
setupAgent := func(languages []string) {
|
|
conf.Server.Deezer.Languages = languages
|
|
agent = &deezerAgent{
|
|
dataStore: &tests.MockDataStore{},
|
|
client: newClient(httpClient),
|
|
languages: languages,
|
|
}
|
|
}
|
|
|
|
It("returns content in first language when available (1 bio API call)", func() {
|
|
setupAgent([]string{"fr", "en"})
|
|
|
|
// French biography available
|
|
fFr, _ := os.Open("tests/fixtures/deezer.artist.bio.fr.json")
|
|
httpClient.bioResponses["fr"] = &http.Response{Body: fFr, StatusCode: 200}
|
|
|
|
bio, err := agent.GetArtistBiography(ctx, "", "Michael Jackson", "")
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(bio).To(ContainSubstring("Guy-Manuel de Homem Christo et Thomas Bangalter"))
|
|
Expect(httpClient.bioRequestCount).To(Equal(1))
|
|
Expect(httpClient.bioRequests[0].Header.Get("Accept-Language")).To(Equal("fr"))
|
|
})
|
|
|
|
It("falls back to second language when first returns empty (2 bio API calls)", func() {
|
|
setupAgent([]string{"ja", "en"})
|
|
|
|
// Japanese returns empty biography
|
|
fJa, _ := os.Open("tests/fixtures/deezer.artist.bio.empty.json")
|
|
httpClient.bioResponses["ja"] = &http.Response{Body: fJa, StatusCode: 200}
|
|
// English returns full biography
|
|
fEn, _ := os.Open("tests/fixtures/deezer.artist.bio.en.json")
|
|
httpClient.bioResponses["en"] = &http.Response{Body: fEn, StatusCode: 200}
|
|
|
|
bio, err := agent.GetArtistBiography(ctx, "", "Michael Jackson", "")
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(bio).To(ContainSubstring("Schoolmates Thomas and Guy-Manuel"))
|
|
Expect(httpClient.bioRequestCount).To(Equal(2))
|
|
Expect(httpClient.bioRequests[0].Header.Get("Accept-Language")).To(Equal("ja"))
|
|
Expect(httpClient.bioRequests[1].Header.Get("Accept-Language")).To(Equal("en"))
|
|
})
|
|
|
|
It("returns ErrNotFound when all languages return empty", func() {
|
|
setupAgent([]string{"ja", "xx"})
|
|
|
|
// Both languages return empty biography
|
|
fJa, _ := os.Open("tests/fixtures/deezer.artist.bio.empty.json")
|
|
httpClient.bioResponses["ja"] = &http.Response{Body: fJa, StatusCode: 200}
|
|
fXx, _ := os.Open("tests/fixtures/deezer.artist.bio.empty.json")
|
|
httpClient.bioResponses["xx"] = &http.Response{Body: fXx, StatusCode: 200}
|
|
|
|
_, err := agent.GetArtistBiography(ctx, "", "Michael Jackson", "")
|
|
|
|
Expect(err).To(MatchError(agents.ErrNotFound))
|
|
Expect(httpClient.bioRequestCount).To(Equal(2))
|
|
})
|
|
})
|
|
})
|
|
|
|
// langAwareHttpClient is a mock HTTP client that returns different responses based on the Accept-Language header
|
|
type langAwareHttpClient struct {
|
|
searchResponse *http.Response
|
|
jwtResponse *http.Response
|
|
bioResponses map[string]*http.Response
|
|
bioRequests []*http.Request
|
|
bioRequestCount int
|
|
}
|
|
|
|
func newLangAwareHttpClient() *langAwareHttpClient {
|
|
return &langAwareHttpClient{
|
|
bioResponses: make(map[string]*http.Response),
|
|
bioRequests: make([]*http.Request, 0),
|
|
}
|
|
}
|
|
|
|
func (c *langAwareHttpClient) Do(req *http.Request) (*http.Response, error) {
|
|
// Handle search artist request
|
|
if req.URL.Host == "api.deezer.com" && req.URL.Path == "/search/artist" {
|
|
if c.searchResponse != nil {
|
|
return c.searchResponse, nil
|
|
}
|
|
return &http.Response{
|
|
StatusCode: 200,
|
|
Body: io.NopCloser(bytes.NewBufferString(`{"data":[],"total":0}`)),
|
|
}, nil
|
|
}
|
|
|
|
// Handle JWT token request
|
|
if req.URL.Host == "auth.deezer.com" && req.URL.Path == "/login/anonymous" {
|
|
if c.jwtResponse != nil {
|
|
return c.jwtResponse, nil
|
|
}
|
|
return &http.Response{
|
|
StatusCode: 500,
|
|
Body: io.NopCloser(bytes.NewBufferString(`{"error":"no mock"}`)),
|
|
}, nil
|
|
}
|
|
|
|
// Handle bio request (GraphQL API)
|
|
if req.URL.Host == "pipe.deezer.com" && req.URL.Path == "/api" {
|
|
c.bioRequestCount++
|
|
c.bioRequests = append(c.bioRequests, req)
|
|
lang := req.Header.Get("Accept-Language")
|
|
if resp, ok := c.bioResponses[lang]; ok {
|
|
return resp, nil
|
|
}
|
|
// Return empty bio by default
|
|
return &http.Response{
|
|
StatusCode: 200,
|
|
Body: io.NopCloser(bytes.NewBufferString(`{"data":{"artist":{"bio":{"full":""}}}}`)),
|
|
}, nil
|
|
}
|
|
|
|
panic("URL not mocked: " + req.URL.String())
|
|
}
|