From 7b523d6b61a2fdffd1585af90a1faafbe7682b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deluan=20Quint=C3=A3o?= Date: Thu, 29 Jan 2026 13:05:51 -0500 Subject: [PATCH] feat(agents): support multiple languages for Last.fm and Deezer metadata (#4952) * feat(lastfm): support multiple languages for album and artist info retrieval Signed-off-by: Deluan * fix(lastfm): improve content validation for album and artist descriptions Signed-off-by: Deluan * refactor(lastfm): remove single language test and clarify languages field in configuration Signed-off-by: Deluan * feat(deezer): support multiple languages for artist bio retrieval Signed-off-by: Deluan * refactor(lastfm): rename ignoredBiographies to ignoredContent for clarity Signed-off-by: Deluan --------- Signed-off-by: Deluan --- adapters/deezer/client.go | 10 +- adapters/deezer/client_auth_test.go | 2 +- adapters/deezer/client_test.go | 31 ++-- adapters/deezer/deezer.go | 17 +- adapters/deezer/deezer_test.go | 171 ++++++++++++++++++ adapters/lastfm/agent.go | 94 ++++++---- adapters/lastfm/agent_test.go | 165 ++++++++++++++++- adapters/lastfm/auth_router.go | 2 +- adapters/lastfm/client.go | 13 +- adapters/lastfm/client_test.go | 16 +- conf/configuration.go | 32 +++- conf/configuration_test.go | 26 +++ conf/export_test.go | 2 + consts/consts.go | 2 + tests/fixtures/deezer.artist.bio.empty.json | 10 + tests/fixtures/deezer.artist.bio.en.json | 12 ++ tests/fixtures/deezer.artist.bio.fr.json | 12 ++ tests/fixtures/deezer.artist.bio.json | 9 - .../fixtures/lastfm.album.getinfo.empty.json | 1 + tests/fixtures/lastfm.album.getinfo.en.json | 1 + .../fixtures/lastfm.artist.getinfo.empty.json | 1 + tests/fixtures/lastfm.artist.getinfo.en.json | 1 + 22 files changed, 528 insertions(+), 102 deletions(-) create mode 100644 adapters/deezer/deezer_test.go create mode 100644 tests/fixtures/deezer.artist.bio.empty.json create mode 100644 tests/fixtures/deezer.artist.bio.en.json create mode 100644 tests/fixtures/deezer.artist.bio.fr.json delete mode 100644 tests/fixtures/deezer.artist.bio.json create mode 100644 tests/fixtures/lastfm.album.getinfo.empty.json create mode 100644 tests/fixtures/lastfm.album.getinfo.en.json create mode 100644 tests/fixtures/lastfm.artist.getinfo.empty.json create mode 100644 tests/fixtures/lastfm.artist.getinfo.en.json diff --git a/adapters/deezer/client.go b/adapters/deezer/client.go index 6c97745a3..31150c673 100644 --- a/adapters/deezer/client.go +++ b/adapters/deezer/client.go @@ -29,14 +29,12 @@ type httpDoer interface { type client struct { httpDoer httpDoer - language string jwt jwtToken } -func newClient(hc httpDoer, language string) *client { +func newClient(hc httpDoer) *client { return &client{ httpDoer: hc, - language: language, } } @@ -129,7 +127,7 @@ const pipeAPIURL = "https://pipe.deezer.com/api" var strictPolicy = bluemonday.StrictPolicy() -func (c *client) getArtistBio(ctx context.Context, artistID int) (string, error) { +func (c *client) getArtistBio(ctx context.Context, artistID int, lang string) (string, error) { jwt, err := c.getJWT(ctx) if err != nil { return "", fmt.Errorf("deezer: failed to get JWT: %w", err) @@ -160,10 +158,10 @@ func (c *client) getArtistBio(ctx context.Context, artistID int) (string, error) } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept-Language", c.language) + req.Header.Set("Accept-Language", lang) req.Header.Set("Authorization", "Bearer "+jwt) - log.Trace(ctx, "Fetching Deezer artist biography via GraphQL", "artistId", artistID, "language", c.language) + log.Trace(ctx, "Fetching Deezer artist biography via GraphQL", "artistId", artistID, "language", lang) resp, err := c.httpDoer.Do(req) if err != nil { return "", err diff --git a/adapters/deezer/client_auth_test.go b/adapters/deezer/client_auth_test.go index b0c2d195d..915da39b7 100644 --- a/adapters/deezer/client_auth_test.go +++ b/adapters/deezer/client_auth_test.go @@ -21,7 +21,7 @@ var _ = Describe("JWT Authentication", func() { BeforeEach(func() { httpClient = &fakeHttpClient{} - client = newClient(httpClient, "en") + client = newClient(httpClient) ctx = context.Background() }) diff --git a/adapters/deezer/client_test.go b/adapters/deezer/client_test.go index a0e0a5cb1..9fa7afdd9 100644 --- a/adapters/deezer/client_test.go +++ b/adapters/deezer/client_test.go @@ -18,7 +18,7 @@ var _ = Describe("client", func() { BeforeEach(func() { httpClient = &fakeHttpClient{} - client = newClient(httpClient, "en") + client = newClient(httpClient) }) Describe("ArtistImages", func() { @@ -78,40 +78,33 @@ var _ = Describe("client", func() { }) It("returns artist bio from a successful request", func() { - f, err := os.Open("tests/fixtures/deezer.artist.bio.json") + f, err := os.Open("tests/fixtures/deezer.artist.bio.en.json") Expect(err).To(BeNil()) httpClient.mock("https://pipe.deezer.com/api", http.Response{Body: f, StatusCode: 200}) - bio, err := client.getArtistBio(GinkgoT().Context(), 27) + bio, err := client.getArtistBio(GinkgoT().Context(), 27, "en") Expect(err).To(BeNil()) Expect(bio).To(ContainSubstring("Schoolmates Thomas and Guy-Manuel")) Expect(bio).ToNot(ContainSubstring("

")) Expect(bio).ToNot(ContainSubstring("

")) }) - It("uses the configured language", func() { - client = newClient(httpClient, "fr") - // Mock JWT token for the new client instance with a valid JWT - testJWT := createTestJWT(5 * time.Minute) - httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ - StatusCode: 200, - Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s","refresh_token":""}`, testJWT))), - }) - f, err := os.Open("tests/fixtures/deezer.artist.bio.json") + It("uses the provided language", func() { + f, err := os.Open("tests/fixtures/deezer.artist.bio.fr.json") Expect(err).To(BeNil()) httpClient.mock("https://pipe.deezer.com/api", http.Response{Body: f, StatusCode: 200}) - _, err = client.getArtistBio(GinkgoT().Context(), 27) + _, err = client.getArtistBio(GinkgoT().Context(), 27, "fr") Expect(err).To(BeNil()) Expect(httpClient.lastRequest.Header.Get("Accept-Language")).To(Equal("fr")) }) It("includes the JWT token in the request", func() { - f, err := os.Open("tests/fixtures/deezer.artist.bio.json") + f, err := os.Open("tests/fixtures/deezer.artist.bio.en.json") Expect(err).To(BeNil()) httpClient.mock("https://pipe.deezer.com/api", http.Response{Body: f, StatusCode: 200}) - _, err = client.getArtistBio(GinkgoT().Context(), 27) + _, err = client.getArtistBio(GinkgoT().Context(), 27, "en") Expect(err).To(BeNil()) // Verify that the Authorization header has the Bearer token format authHeader := httpClient.lastRequest.Header.Get("Authorization") @@ -142,7 +135,7 @@ var _ = Describe("client", func() { Body: io.NopCloser(bytes.NewBufferString(errorResponse)), }) - _, err := client.getArtistBio(GinkgoT().Context(), 999) + _, err := client.getArtistBio(GinkgoT().Context(), 999, "en") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("GraphQL error")) Expect(err.Error()).To(ContainSubstring("Artist not found")) @@ -164,7 +157,7 @@ var _ = Describe("client", func() { Body: io.NopCloser(bytes.NewBufferString(emptyBioResponse)), }) - _, err := client.getArtistBio(GinkgoT().Context(), 27) + _, err := client.getArtistBio(GinkgoT().Context(), 27, "en") Expect(err).To(MatchError("deezer: biography not found")) }) @@ -174,7 +167,7 @@ var _ = Describe("client", func() { Body: io.NopCloser(bytes.NewBufferString(`{"error":"Internal server error"}`)), }) - _, err := client.getArtistBio(GinkgoT().Context(), 27) + _, err := client.getArtistBio(GinkgoT().Context(), 27, "en") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("failed to get JWT")) }) @@ -187,7 +180,7 @@ var _ = Describe("client", func() { Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s","refresh_token":""}`, expiredJWT))), }) - _, err := client.getArtistBio(GinkgoT().Context(), 27) + _, err := client.getArtistBio(GinkgoT().Context(), 27, "en") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("JWT token already expired or expires too soon")) }) diff --git a/adapters/deezer/deezer.go b/adapters/deezer/deezer.go index a6806595e..ed3071766 100644 --- a/adapters/deezer/deezer.go +++ b/adapters/deezer/deezer.go @@ -26,15 +26,19 @@ const deezerArtistSearchLimit = 50 type deezerAgent struct { dataStore model.DataStore client *client + languages []string } func deezerConstructor(dataStore model.DataStore) agents.Interface { - agent := &deezerAgent{dataStore: dataStore} + agent := &deezerAgent{ + dataStore: dataStore, + languages: conf.Server.Deezer.Languages, + } httpClient := &http.Client{ Timeout: consts.DefaultHttpClientTimeOut, } cachedHttpClient := cache.NewHTTPClient(httpClient, consts.DefaultHttpClientTimeOut) - agent.client = newClient(cachedHttpClient, conf.Server.Deezer.Language) + agent.client = newClient(cachedHttpClient) return agent } @@ -149,7 +153,14 @@ func (s *deezerAgent) GetArtistBiography(ctx context.Context, _, name, _ string) return "", err } - return s.client.getArtistBio(ctx, artist.ID) + for _, lang := range s.languages { + bio, err := s.client.getArtistBio(ctx, artist.ID, lang) + if err == nil && bio != "" { + return bio, nil + } + log.Debug(ctx, "Deezer/artist.bio returned empty/error, trying next language", "artist", name, "lang", lang, err) + } + return "", agents.ErrNotFound } func init() { diff --git a/adapters/deezer/deezer_test.go b/adapters/deezer/deezer_test.go new file mode 100644 index 000000000..4dd251585 --- /dev/null +++ b/adapters/deezer/deezer_test.go @@ -0,0 +1,171 @@ +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()) +} diff --git a/adapters/lastfm/agent.go b/adapters/lastfm/agent.go index 3677be6e0..6dc635b50 100644 --- a/adapters/lastfm/agent.go +++ b/adapters/lastfm/agent.go @@ -26,8 +26,8 @@ const ( sessionKeyProperty = "LastFMSessionKey" ) -var ignoredBiographies = []string{ - // Unknown Artist +var ignoredContent = []string{ + // Empty Artist/Album `Schoolmates Thomas and Guy-Manuel began their career in 1992 with the indie rock trio Darlin' (named after The Beach Boys song) but were scathingly dismissed by Melody Maker magazine as \"daft punk.\" Turning to house-inspired electronica, they used the put down as a name for their DJ-ing partnership and became a hugely successful and influential dance act. First major single \"Da Funk\" was accompanied by a Spike Jonze-directed video and more success followed with global dance floor anthem \"Around the World,\" \"One More Time,\" and \"Harder, Faster, Better, Stronger\" - which was sampled by Kanye West for his hit \"Stronger.\" Albums Homework (1997), Discovery (2001) and Human After All (2005) all made the UK Top 10 establishing a style of simple, Chicago house-inspired grooves exploding into a robotic, rave sound.

" + } + } + }, + "extensions": { + "queryCost": 3 + } +} diff --git a/tests/fixtures/deezer.artist.bio.fr.json b/tests/fixtures/deezer.artist.bio.fr.json new file mode 100644 index 000000000..435f6fcf0 --- /dev/null +++ b/tests/fixtures/deezer.artist.bio.fr.json @@ -0,0 +1,12 @@ +{ + "data": { + "artist": { + "bio": { + "full": "Guy-Manuel de Homem Christo et Thomas Bangalter se rencontrent en 1987 au lycée Carnot de Paris. Partageant une même passion pour la musique, les deux amis fondent en 1992 Darlin', un groupe de rock influencé par les Stooges et MC5, dont la production sera taxée par un critique de la presse anglaise de «daft punk» (« punk idiot »).
\n
\nDécouragés face à l'apathie du milieu rock, ils décident un peu plus tard de se lancer à corps perdus dans le courant Techno alors en pleine explosion. Arrive alors la découverte de la House, des clubs et des raves, dont une en particulier qui déterminera leur avenir : en 1993 est organisé à EuroDisney une rave où notre duo rencontre les dirigeants du label techno écossais Soma." + } + } + }, + "extensions": { + "queryCost": 3 + } +} diff --git a/tests/fixtures/deezer.artist.bio.json b/tests/fixtures/deezer.artist.bio.json deleted file mode 100644 index 80e439bae..000000000 --- a/tests/fixtures/deezer.artist.bio.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "data": { - "artist": { - "bio": { - "full": "

Schoolmates Thomas and Guy-Manuel began their career in 1992 with the indie rock trio Darlin' (named after The Beach Boys song) but were scathingly dismissed by Melody Maker magazine as \"daft punk.\" Turning to house-inspired electronica, they used the put down as a name for their DJ-ing partnership and became a hugely successful and influential dance act.

" - } - } - } -} diff --git a/tests/fixtures/lastfm.album.getinfo.empty.json b/tests/fixtures/lastfm.album.getinfo.empty.json new file mode 100644 index 000000000..06403ff8c --- /dev/null +++ b/tests/fixtures/lastfm.album.getinfo.empty.json @@ -0,0 +1 @@ +{"album":{"artist":"Legião Urbana","mbid":"1749dd07-5aa9-436e-babc-e3e982deb273","tags":{"tag":[{"url":"https:\/\/www.last.fm\/tag\/rock","name":"rock"},{"url":"https:\/\/www.last.fm\/tag\/80s","name":"80s"},{"url":"https:\/\/www.last.fm\/tag\/brazilian","name":"brazilian"},{"url":"https:\/\/www.last.fm\/tag\/brasil","name":"brasil"},{"url":"https:\/\/www.last.fm\/tag\/brazilian+rock","name":"brazilian rock"}]},"name":"Dois","image":[{"size":"small","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/34s\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"medium","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/64s\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"large","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/174s\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"extralarge","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/300x300\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"mega","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/300x300\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/300x300\/e7dbfc64cde5478484af18f0e30662e0.png"}],"tracks":{"track":[{"streamable":{"fulltrack":"0","#text":"0"},"duration":232,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Daniel+na+Cova+dos+Le%C3%B5es","name":"Daniel na Cova dos Leões","@attr":{"rank":1},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":null,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Quase+Sem+Querer","name":"Quase Sem Querer","@attr":{"rank":2},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":280,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Acrilic+On+Canvas","name":"Acrilic On Canvas","@attr":{"rank":3},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":271,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Eduardo+e+M%C3%B4nica","name":"Eduardo e Mônica","@attr":{"rank":4},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":94,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Central+Do+Brasil","name":"Central Do Brasil","@attr":{"rank":5},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":302,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Tempo+Perdido","name":"Tempo Perdido","@attr":{"rank":6},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":170,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Metr%C3%B3pole","name":"Metrópole","@attr":{"rank":7},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":175,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Plantas+Em+Baixo+Do+Aqu%C3%A1rio","name":"Plantas Em Baixo Do Aquário","@attr":{"rank":8},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":162,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/M%C3%BAsica+urbana+2","name":"Música urbana 2","@attr":{"rank":9},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":184,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Andrea+Doria","name":"Andrea Doria","@attr":{"rank":10},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":231,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/F%C3%A1brica","name":"Fábrica","@attr":{"rank":11},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":258,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/%C3%8Dndios","name":"Índios","@attr":{"rank":12},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}}]},"listeners":"494356","playcount":"9833783","url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois"}} \ No newline at end of file diff --git a/tests/fixtures/lastfm.album.getinfo.en.json b/tests/fixtures/lastfm.album.getinfo.en.json new file mode 100644 index 000000000..f5444f35b --- /dev/null +++ b/tests/fixtures/lastfm.album.getinfo.en.json @@ -0,0 +1 @@ +{"album":{"artist":"Legião Urbana","mbid":"1749dd07-5aa9-436e-babc-e3e982deb273","tags":{"tag":[{"url":"https:\/\/www.last.fm\/tag\/rock","name":"rock"},{"url":"https:\/\/www.last.fm\/tag\/80s","name":"80s"},{"url":"https:\/\/www.last.fm\/tag\/brazilian","name":"brazilian"},{"url":"https:\/\/www.last.fm\/tag\/brasil","name":"brasil"},{"url":"https:\/\/www.last.fm\/tag\/brazilian+rock","name":"brazilian rock"}]},"playcount":"9833783","image":[{"size":"small","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/34s\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"medium","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/64s\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"large","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/174s\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"extralarge","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/300x300\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"mega","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/300x300\/e7dbfc64cde5478484af18f0e30662e0.png"},{"size":"","#text":"https:\/\/lastfm.freetls.fastly.net\/i\/u\/300x300\/e7dbfc64cde5478484af18f0e30662e0.png"}],"tracks":{"track":[{"streamable":{"fulltrack":"0","#text":"0"},"duration":232,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Daniel+na+Cova+dos+Le%C3%B5es","name":"Daniel na Cova dos Leões","@attr":{"rank":1},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":null,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Quase+Sem+Querer","name":"Quase Sem Querer","@attr":{"rank":2},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":280,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Acrilic+On+Canvas","name":"Acrilic On Canvas","@attr":{"rank":3},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":271,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Eduardo+e+M%C3%B4nica","name":"Eduardo e Mônica","@attr":{"rank":4},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":94,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Central+Do+Brasil","name":"Central Do Brasil","@attr":{"rank":5},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":302,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Tempo+Perdido","name":"Tempo Perdido","@attr":{"rank":6},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":170,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Metr%C3%B3pole","name":"Metrópole","@attr":{"rank":7},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":175,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Plantas+Em+Baixo+Do+Aqu%C3%A1rio","name":"Plantas Em Baixo Do Aquário","@attr":{"rank":8},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":162,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/M%C3%BAsica+urbana+2","name":"Música urbana 2","@attr":{"rank":9},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":184,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/Andrea+Doria","name":"Andrea Doria","@attr":{"rank":10},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":231,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/F%C3%A1brica","name":"Fábrica","@attr":{"rank":11},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}},{"streamable":{"fulltrack":"0","#text":"0"},"duration":258,"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois\/%C3%8Dndios","name":"Índios","@attr":{"rank":12},"artist":{"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana","name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99"}}]},"url":"https:\/\/www.last.fm\/music\/Legi%C3%A3o+Urbana\/Dois","name":"Dois","listeners":"494356","wiki":{"published":"10 Oct 2023, 13:56","summary":"Dois é o segundo álbum de estúdio da banda brasileira de rock Legião Urbana, lançado em 20 de julho de 1986 pela EMI. Ocupa a 21ª posição da lista dos 100 maiores discos da música brasileira pela Rolling Stone Brasil. Em setembro de 2012, foi eleito pelo público da rádio Eldorado FM, do portal Estadao.com e do Caderno C2+Música (estes dois últimos pertencentes ao jornal O Estado de S. Paulo) como o terceiro melhor disco brasileiro da história. O álbum vendeu mais de 900 mil cópias no Brasil. \"Tempo Perdido\" fez um grande sucesso e se tornou num dos clássicos
Read more on Last.fm<\/a>.","content":"Dois é o segundo álbum de estúdio da banda brasileira de rock Legião Urbana, lançado em 20 de julho de 1986 pela EMI. Ocupa a 21ª posição da lista dos 100 maiores discos da música brasileira pela Rolling Stone Brasil. Em setembro de 2012, foi eleito pelo público da rádio Eldorado FM, do portal Estadao.com e do Caderno C2+Música (estes dois últimos pertencentes ao jornal O Estado de S. Paulo) como o terceiro melhor disco brasileiro da história. O álbum vendeu mais de 900 mil cópias no Brasil. \"Tempo Perdido\" fez um grande sucesso e se tornou num dos clássicos da Legião. \"Eduardo e Mônica\", \"\"Índios\"\" e \"Quase sem Querer\" também fizeram sucesso. Read more on Last.fm<\/a>. User-contributed text is available under the Creative Commons By-SA License; additional terms may apply."}}} \ No newline at end of file diff --git a/tests/fixtures/lastfm.artist.getinfo.empty.json b/tests/fixtures/lastfm.artist.getinfo.empty.json new file mode 100644 index 000000000..015b51701 --- /dev/null +++ b/tests/fixtures/lastfm.artist.getinfo.empty.json @@ -0,0 +1 @@ +{"artist":{"name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99","url":"https://www.last.fm/music/+noredirect/Legi%C3%A3o+Urbana","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}],"streamable":"0","ontour":"0","stats":{"listeners":"740591","playcount":"44493504"},"similar":{"artist":[{"name":"Renato Russo","url":"https://www.last.fm/music/Renato+Russo","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Engenheiros Do Hawaii","url":"https://www.last.fm/music/Engenheiros+Do+Hawaii","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Os Paralamas Do Sucesso","url":"https://www.last.fm/music/Os+Paralamas+Do+Sucesso","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Barão Vermelho","url":"https://www.last.fm/music/Bar%C3%A3o+Vermelho","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Capital Inicial","url":"https://www.last.fm/music/Capital+Inicial","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]}]},"tags":{"tag":[{"name":"rock","url":"https://www.last.fm/tag/rock"},{"name":"brazilian rock","url":"https://www.last.fm/tag/brazilian+rock"},{"name":"80s","url":"https://www.last.fm/tag/80s"},{"name":"brazilian","url":"https://www.last.fm/tag/brazilian"},{"name":"brasil","url":"https://www.last.fm/tag/brasil"}]},"bio":{"links":{"link":{"#text":"","rel":"original","href":"https://last.fm/music/+noredirect/Legi%C3%A3o+Urbana/+wiki"}},"published":"01 Jan 1970, 00:00","summary":" Read more on Last.fm","content":""}}} \ No newline at end of file diff --git a/tests/fixtures/lastfm.artist.getinfo.en.json b/tests/fixtures/lastfm.artist.getinfo.en.json new file mode 100644 index 000000000..6c643b8e2 --- /dev/null +++ b/tests/fixtures/lastfm.artist.getinfo.en.json @@ -0,0 +1 @@ +{"artist":{"name":"Legião Urbana","mbid":"47685be0-926f-4be9-b1ae-e32da47a3b99","url":"https://www.last.fm/music/+noredirect/Legi%C3%A3o+Urbana","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}],"streamable":"0","ontour":"0","stats":{"listeners":"740367","playcount":"44476703"},"similar":{"artist":[{"name":"Renato Russo","url":"https://www.last.fm/music/Renato+Russo","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Engenheiros Do Hawaii","url":"https://www.last.fm/music/Engenheiros+Do+Hawaii","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Os Paralamas Do Sucesso","url":"https://www.last.fm/music/Os+Paralamas+Do+Sucesso","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Barão Vermelho","url":"https://www.last.fm/music/Bar%C3%A3o+Vermelho","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]},{"name":"Capital Inicial","url":"https://www.last.fm/music/Capital+Inicial","image":[{"#text":"https://lastfm.freetls.fastly.net/i/u/34s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"small"},{"#text":"https://lastfm.freetls.fastly.net/i/u/64s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"medium"},{"#text":"https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png","size":"large"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"extralarge"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":"mega"},{"#text":"https://lastfm.freetls.fastly.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png","size":""}]}]},"tags":{"tag":[{"name":"rock","url":"https://www.last.fm/tag/rock"},{"name":"brazilian rock","url":"https://www.last.fm/tag/brazilian+rock"},{"name":"80s","url":"https://www.last.fm/tag/80s"},{"name":"brazilian","url":"https://www.last.fm/tag/brazilian"},{"name":"brasil","url":"https://www.last.fm/tag/brasil"}]},"bio":{"links":{"link":{"#text":"","rel":"original","href":"https://last.fm/music/+noredirect/Legi%C3%A3o+Urbana/+wiki"}},"published":"03 Mar 2006, 04:04","summary":"Legião Urbana was a Brazilian post-punk band from Brasília, Distrito Federal, Brazil.\n\nFronted by lead singer and lyricist Renato Russo, Legião Urbana was founded in 1983 and existed until 1996, when Renato passed away due to complications caused by AIDS. Besides being the lead vocalist, Renato was an occasional guitar, bass and keyboards player. He also wrote most of the band's songs. In 13 years of career, they released 8 studio albums - the last being posthumous - and one live. Read more on Last.fm","content":"Legião Urbana was a Brazilian post-punk band from Brasília, Distrito Federal, Brazil.\n\nFronted by lead singer and lyricist Renato Russo, Legião Urbana was founded in 1983 and existed until 1996, when Renato passed away due to complications caused by AIDS. Besides being the lead vocalist, Renato was an occasional guitar, bass and keyboards player. He also wrote most of the band's songs. In 13 years of career, they released 8 studio albums - the last being posthumous - and one live.\n\nLegião Urbana is probably the most famous Brazilian rock bands, especially known for Renato's poetic lyrics, which range from love and spiritualism to politics, family, sex and drugs.\n\nNowadays, Dado Villa-Lobos (ex-guitar player of Legião Urbana) has a solo career and recorded his first album, named \"Jardim De Cactus\", in 2005. Drum player Marcelo Bonfá also tried a solo career. Read more on Last.fm. User-contributed text is available under the Creative Commons By-SA License; additional terms may apply."}}} \ No newline at end of file