From da70f148868313721e79630baf3866a8047ca02e Mon Sep 17 00:00:00 2001 From: Tom Jowitt <11221+tomjowitt@users.noreply.github.com> Date: Sat, 20 Apr 2024 16:38:27 +1000 Subject: [PATCH] Add multiple tracks --- album.go | 31 +++- album_test.go | 161 +++++++++++++++++++-- examples/track/main.go | 17 ++- testdata/invalid-json.json | 1 + testdata/multiple-tracks.json | 263 ++++++++++++++++++++++++++++++++++ 5 files changed, 456 insertions(+), 17 deletions(-) create mode 100644 testdata/invalid-json.json create mode 100644 testdata/multiple-tracks.json diff --git a/album.go b/album.go index 9c8b5fe..63c60e1 100644 --- a/album.go +++ b/album.go @@ -79,6 +79,10 @@ type ItemMetaData struct { Total int `json:"total"` } +type idListParams struct { + ids string +} + // GetSingleAlbum returns an album that matches an ID. func (c *Client) GetSingleAlbum(ctx context.Context, id string) (*Album, error) { if id == "" { @@ -184,11 +188,7 @@ func (c *Client) GetAlbumByBarcodeID(ctx context.Context, barcodeID string) ([]A // GetMultipleAlbums returns a list of albums filtered by their IDs. func (c *Client) GetMultipleAlbums(ctx context.Context, ids []string) ([]Album, error) { - type multiAlbumParams struct { - ids string - } - - params := multiAlbumParams{ + params := idListParams{ ids: strings.Join(ids, ","), } @@ -286,3 +286,24 @@ func (c *Client) GetTracksByISRC(ctx context.Context, isrc string, params Pagina return result.Data, nil } + +// GetMultipleTracks returns a list of tracks filtered by their IDs. +func (c *Client) GetMultipleTracks(ctx context.Context, ids []string) ([]Track, error) { + params := idListParams{ + ids: strings.Join(ids, ","), + } + + response, err := c.request(ctx, http.MethodGet, "/tracks", params) + if err != nil { + return nil, fmt.Errorf("failed to connect to the multiple tracks endpoint: %w", err) + } + + var results trackResults + + err = json.Unmarshal(response, &results) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal the multiple tracks response body: %w", err) + } + + return results.Data, nil +} diff --git a/album_test.go b/album_test.go index dba825f..3c7d771 100644 --- a/album_test.go +++ b/album_test.go @@ -7,6 +7,8 @@ import ( "testing" ) +const countryCode = "AU" + func TestGetSingleAlbum(t *testing.T) { t.Parallel() @@ -42,6 +44,24 @@ func TestGetSingleAlbum(t *testing.T) { expected expected wantErr bool }{ + { + "Token Error", + args{ + httpClient: &mockHTTPClient{FilePath: "testdata/401-token-error.json", StatusCode: http.StatusUnauthorized}, + ID: "51584178", + }, + expected{}, + true, + }, + { + "Missing ID", + args{ + httpClient: &mockHTTPClient{FilePath: "testdata/401-token-error.json", StatusCode: http.StatusBadRequest}, + ID: "", + }, + expected{}, + true, + }, { "Single album parses correctly", args{ @@ -76,11 +96,20 @@ func TestGetSingleAlbum(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - client := &Client{httpClient: tt.args.httpClient} + client := &Client{httpClient: tt.args.httpClient, CountryCode: countryCode} album, err := client.GetSingleAlbum(context.Background(), tt.args.ID) - if (err != nil) != tt.wantErr { - t.Errorf("Client.GetSingleAlbum() error = %v, wantErr %v", err, tt.wantErr) + if err == nil && tt.wantErr { + t.Error("Client.GetSingleAlbum() expected error") + return + } + + if err != nil && tt.wantErr { + return + } + + if err != nil && !tt.wantErr { + t.Errorf("Client.GetTracksByISRC() error = %v", err) return } @@ -196,7 +225,8 @@ func TestGetAlbumTracks(t *testing.T) { t.Parallel() c := &Client{ - httpClient: tt.args.httpClient, + CountryCode: countryCode, + httpClient: tt.args.httpClient, } tracks, err := c.GetAlbumTracks(context.Background(), tt.args.id) @@ -250,7 +280,8 @@ func TestGetSingleTrack(t *testing.T) { t.Parallel() c := &Client{ - httpClient: tt.args.httpClient, + CountryCode: countryCode, + httpClient: tt.args.httpClient, } track, err := c.GetSingleTrack(context.Background(), tt.args.id) @@ -287,10 +318,33 @@ func TestGetTracksByISRC(t *testing.T) { } tests := []struct { - name string - args args - expected expected + name string + args args + expected expected + expectedError bool }{ + { + "Token Error", + args{ + httpClient: &mockHTTPClient{FilePath: "testdata/401-token-error.json", StatusCode: http.StatusUnauthorized}, + id: "51584179", + }, + expected{ + count: 0, + }, + true, + }, + { + "Bad Response", + args{ + httpClient: &mockHTTPClient{FilePath: "testdata/invalid-json.json", StatusCode: http.StatusInternalServerError}, + id: "51584179", + }, + expected{ + count: 0, + }, + true, + }, { "Count of tracks by ISRC", args{ @@ -300,6 +354,7 @@ func TestGetTracksByISRC(t *testing.T) { expected{ count: 2, }, + false, }, } for _, tt := range tests { @@ -308,11 +363,17 @@ func TestGetTracksByISRC(t *testing.T) { t.Parallel() c := &Client{ - httpClient: tt.args.httpClient, + CountryCode: countryCode, + httpClient: tt.args.httpClient, } tracks, err := c.GetTracksByISRC(context.Background(), tt.args.id, PaginationParams{Limit: 5}) - if err != nil { + if err == nil && tt.expectedError { + t.Error("Client.GetTracksByISRC() expected an error", err) + return + } + + if err != nil && !tt.expectedError { t.Errorf("Client.GetTracksByISRC() error = %v", err) return } @@ -323,3 +384,83 @@ func TestGetTracksByISRC(t *testing.T) { }) } } + +func TestGetMultipleTracks(t *testing.T) { + t.Parallel() + + type args struct { + httpClient HTTPClient + ids string + } + + type expected struct { + count int + } + + tests := []struct { + name string + args args + expected expected + expectedError bool + }{ + { + "Token Error", + args{ + httpClient: &mockHTTPClient{FilePath: "testdata/401-token-error.json", StatusCode: http.StatusUnauthorized}, + ids: "251380837,251380838", + }, + expected{ + count: 0, + }, + true, + }, + { + "Bad Response", + args{ + httpClient: &mockHTTPClient{FilePath: "testdata/invalid-json.json", StatusCode: http.StatusInternalServerError}, + ids: "251380837,251380838", + }, + expected{ + count: 0, + }, + true, + }, + { + "Count of tracks by ISRC", + args{ + httpClient: &mockHTTPClient{FilePath: "testdata/multiple-tracks.json", StatusCode: http.StatusOK}, + ids: "251380837,251380838", + }, + expected{ + count: 2, + }, + false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + c := &Client{ + CountryCode: countryCode, + httpClient: tt.args.httpClient, + } + + tracks, err := c.GetMultipleTracks(context.Background(), []string{}) + if err == nil && tt.expectedError { + t.Error("Client.GetMultipleTracks() expected an error", err) + return + } + + if err != nil && !tt.expectedError { + t.Errorf("Client.GetMultipleTracks() error = %v", err) + return + } + + if len(tracks) != tt.expected.count { + t.Errorf("Client.GetMultipleTracks() artist count %v, want %v", len(tracks), tt.expected.count) + } + }) + } +} diff --git a/examples/track/main.go b/examples/track/main.go index 6f24334..adc8470 100644 --- a/examples/track/main.go +++ b/examples/track/main.go @@ -30,16 +30,29 @@ func main() { log.Printf("%s - %s", track.Title, track.Artists[0].Name) + log.Println("-------------------------------------------------") + log.Println("Tracks By ISRC") + log.Println("-------------------------------------------------") + tracks, err := client.GetTracksByISRC(ctx, "GBBLY1600675", gotidal.PaginationParams{Limit: 5}) if err != nil { log.Fatal(err) } + for _, track := range tracks { + log.Printf("%s - %s - %s", track.Title, track.Artists[0].Name, track.Album.Title) + } + log.Println("-------------------------------------------------") - log.Println("Tracks By ISRC") + log.Println("Multiple Tracks") log.Println("-------------------------------------------------") - for _, track := range tracks { + multipleTracks, err := client.GetMultipleTracks(ctx, []string{"251380837", "251380838"}) + if err != nil { + log.Fatal(err) + } + + for _, track := range multipleTracks { log.Printf("%s - %s - %s", track.Title, track.Artists[0].Name, track.Album.Title) } } diff --git a/testdata/invalid-json.json b/testdata/invalid-json.json new file mode 100644 index 0000000..98232c6 --- /dev/null +++ b/testdata/invalid-json.json @@ -0,0 +1 @@ +{ diff --git a/testdata/multiple-tracks.json b/testdata/multiple-tracks.json new file mode 100644 index 0000000..4f7a8b3 --- /dev/null +++ b/testdata/multiple-tracks.json @@ -0,0 +1,263 @@ +{ + "data": [ + { + "resource": { + "artifactType": "track", + "id": "251380837", + "title": "I'M THAT GIRL", + "artists": [ + { + "id": "1566", + "name": "Beyoncé", + "picture": [ + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/1024x256.jpg", + "width": 1024, + "height": 256 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/1080x720.jpg", + "width": 1080, + "height": 720 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/160x107.jpg", + "width": 160, + "height": 107 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/160x160.jpg", + "width": 160, + "height": 160 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/320x214.jpg", + "width": 320, + "height": 214 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/320x320.jpg", + "width": 320, + "height": 320 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/480x480.jpg", + "width": 480, + "height": 480 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/640x428.jpg", + "width": 640, + "height": 428 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/750x500.jpg", + "width": 750, + "height": 500 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/750x750.jpg", + "width": 750, + "height": 750 + } + ], + "main": true + } + ], + "album": { + "id": "251380836", + "title": "RENAISSANCE", + "imageCover": [ + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/1080x1080.jpg", + "width": 1080, + "height": 1080 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/1280x1280.jpg", + "width": 1280, + "height": 1280 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/160x160.jpg", + "width": 160, + "height": 160 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/320x320.jpg", + "width": 320, + "height": 320 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/640x640.jpg", + "width": 640, + "height": 640 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/750x750.jpg", + "width": 750, + "height": 750 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/80x80.jpg", + "width": 80, + "height": 80 + } + ], + "videoCover": [] + }, + "duration": 208, + "trackNumber": 1, + "volumeNumber": 1, + "isrc": "USSM12209515", + "copyright": "(P) 2022 Parkwood Entertainment LLC, under exclusive license to Columbia Records, a Division of Sony Music Entertainment", + "mediaMetadata": { + "tags": [ + "DOLBY_ATMOS" + ] + }, + "properties": { + "content": [ + "explicit" + ] + }, + "tidalUrl": "https://tidal.com/browse/track/251380837" + }, + "id": "251380837", + "status": 200, + "message": "success" + }, + { + "resource": { + "artifactType": "track", + "id": "251380838", + "title": "COZY", + "artists": [ + { + "id": "1566", + "name": "Beyoncé", + "picture": [ + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/1024x256.jpg", + "width": 1024, + "height": 256 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/1080x720.jpg", + "width": 1080, + "height": 720 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/160x107.jpg", + "width": 160, + "height": 107 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/160x160.jpg", + "width": 160, + "height": 160 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/320x214.jpg", + "width": 320, + "height": 214 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/320x320.jpg", + "width": 320, + "height": 320 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/480x480.jpg", + "width": 480, + "height": 480 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/640x428.jpg", + "width": 640, + "height": 428 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/750x500.jpg", + "width": 750, + "height": 500 + }, + { + "url": "https://resources.tidal.com/images/0073e74d/ccf0/41d6/855d/374fdf71e5ed/750x750.jpg", + "width": 750, + "height": 750 + } + ], + "main": true + } + ], + "album": { + "id": "251380836", + "title": "RENAISSANCE", + "imageCover": [ + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/1080x1080.jpg", + "width": 1080, + "height": 1080 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/1280x1280.jpg", + "width": 1280, + "height": 1280 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/160x160.jpg", + "width": 160, + "height": 160 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/320x320.jpg", + "width": 320, + "height": 320 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/640x640.jpg", + "width": 640, + "height": 640 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/750x750.jpg", + "width": 750, + "height": 750 + }, + { + "url": "https://resources.tidal.com/images/d3c4372b/a652/40e0/bdb1/fc8d032708f6/80x80.jpg", + "width": 80, + "height": 80 + } + ], + "videoCover": [] + }, + "duration": 210, + "trackNumber": 2, + "volumeNumber": 1, + "isrc": "USSM12209516", + "copyright": "(P) 2022 Parkwood Entertainment LLC, under exclusive license to Columbia Records, a Division of Sony Music Entertainment", + "mediaMetadata": { + "tags": [ + "DOLBY_ATMOS" + ] + }, + "properties": { + "content": [ + "explicit" + ] + }, + "tidalUrl": "https://tidal.com/browse/track/251380838" + }, + "id": "251380838", + "status": 200, + "message": "success" + } + ], + "metadata": { + "requested": 2, + "success": 2, + "failure": 0 + } +}