mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
paginate tidal artist album fetches across all pages
This commit is contained in:
@@ -310,31 +310,49 @@ func (c *Client) fetchArtistAlbums(ctx context.Context, artistID string) ([]map[
|
|||||||
out := make([]map[string]any, 0)
|
out := make([]map[string]any, 0)
|
||||||
seen := map[string]struct{}{}
|
seen := map[string]struct{}{}
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
resp, status, err := c.apiRequest(ctx, p.path, p.params, c.baseURL)
|
offset := 0
|
||||||
if err != nil {
|
for {
|
||||||
return nil, err
|
params := url.Values{}
|
||||||
}
|
for k, values := range p.params {
|
||||||
if status != http.StatusOK {
|
for _, v := range values {
|
||||||
return nil, fmt.Errorf("tidal artist albums failed: status=%d", status)
|
params.Add(k, v)
|
||||||
}
|
}
|
||||||
items, _ := resp["items"].([]any)
|
|
||||||
for _, raw := range items {
|
|
||||||
itm, ok := raw.(map[string]any)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if wrapped, ok := itm["item"].(map[string]any); ok {
|
params.Set("offset", strconv.Itoa(offset))
|
||||||
itm = wrapped
|
|
||||||
|
resp, status, err := c.apiRequest(ctx, p.path, params, c.baseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
id := stringify(itm["id"])
|
if status != http.StatusOK {
|
||||||
if id == "" {
|
return nil, fmt.Errorf("tidal artist albums failed: status=%d offset=%d", status, offset)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if _, dup := seen[id]; dup {
|
items, _ := resp["items"].([]any)
|
||||||
continue
|
if len(items) == 0 {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
seen[id] = struct{}{}
|
for _, raw := range items {
|
||||||
out = append(out, itm)
|
itm, ok := raw.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if wrapped, ok := itm["item"].(map[string]any); ok {
|
||||||
|
itm = wrapped
|
||||||
|
}
|
||||||
|
id := stringify(itm["id"])
|
||||||
|
if id == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, dup := seen[id]; dup {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[id] = struct{}{}
|
||||||
|
out = append(out, itm)
|
||||||
|
}
|
||||||
|
if len(items) < 100 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
offset += 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"streamrip-go/internal/config"
|
"streamrip-go/internal/config"
|
||||||
@@ -98,3 +99,64 @@ func TestBestHLSVariantURLFallsBackToMaster(t *testing.T) {
|
|||||||
t.Fatalf("url = %q, want %q", got, master)
|
t.Fatalf("url = %q, want %q", got, master)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMetadataArtistPaginatesAlbums(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/v1/sessions":
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"countryCode": "US", "userId": 123})
|
||||||
|
case "/v1/artists/9":
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": 9, "name": "Artist X"})
|
||||||
|
case "/v1/artists/9/albums":
|
||||||
|
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
|
||||||
|
filter := r.URL.Query().Get("filter")
|
||||||
|
if filter == "" {
|
||||||
|
if offset == 0 {
|
||||||
|
items := make([]any, 0, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
items = append(items, map[string]any{"id": i + 1})
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"items": items})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if offset == 100 {
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"items": []any{map[string]any{"id": 101}}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"items": []any{}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if filter == "EPSANDSINGLES" {
|
||||||
|
if offset == 0 {
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"items": []any{map[string]any{"id": 101}, map[string]any{"id": 102}}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"items": []any{}})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
cfgData := config.DefaultConfigData()
|
||||||
|
cfgData.Tidal.AccessToken = "token"
|
||||||
|
cfgData.Tidal.CountryCode = "US"
|
||||||
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
||||||
|
c.baseURL = ts.URL + "/v1"
|
||||||
|
|
||||||
|
if err := c.Login(context.Background()); err != nil {
|
||||||
|
t.Fatalf("login err = %v", err)
|
||||||
|
}
|
||||||
|
meta, err := c.GetMetadata(context.Background(), "9", "artist")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetMetadata() err = %v", err)
|
||||||
|
}
|
||||||
|
albumsObj, _ := meta["albums"].(map[string]any)
|
||||||
|
items, _ := albumsObj["items"].([]map[string]any)
|
||||||
|
if len(items) != 102 {
|
||||||
|
t.Fatalf("albums len = %d, want 102", len(items))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user