diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d0b4fb1..559904d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## unreleased Bugfixes - ([#564](https://github.com/ramsayleung/rspotify/pull/564)) Fix: FullArtist: deprecate and add default for genres. +- ([#563](https://github.com/ramsayleung/rspotify/pull/563)) Fix: allow both items and tracks in FullPlaylist ## 0.16.0 (2026.03.22) diff --git a/rspotify-model/src/playlist.rs b/rspotify-model/src/playlist.rs index 670876fc..0f219dec 100644 --- a/rspotify-model/src/playlist.rs +++ b/rspotify-model/src/playlist.rs @@ -112,13 +112,19 @@ struct FullPlaylistShadow { pub owner: PublicUser, pub public: Option, pub snapshot_id: String, - #[serde(alias = "tracks")] - pub items: Page, + #[serde(default)] + pub items: Option>, + #[serde(default)] + pub tracks: Option>, } #[allow(deprecated)] impl From for FullPlaylist { fn from(shadow: FullPlaylistShadow) -> Self { + let items = shadow + .items + .or(shadow.tracks) + .expect("missing items/tracks"); Self { collaborative: shadow.collaborative, description: shadow.description, @@ -131,8 +137,8 @@ impl From for FullPlaylist { owner: shadow.owner, public: shadow.public, snapshot_id: shadow.snapshot_id, - tracks: shadow.items.clone(), - items: shadow.items, + tracks: items.clone(), + items, } } } diff --git a/tests/test_models.rs b/tests/test_models.rs index 1faf267f..b47fe92a 100644 --- a/tests/test_models.rs +++ b/tests/test_models.rs @@ -160,7 +160,7 @@ fn test_private_user() { "product": "open", "type": "user", "uri": "spotify:user:waq5aexykhm6nlv0cnwdieng0" - } + } "#; let private_user: PrivateUser = deserialize(json_str); assert_eq!(private_user.display_name.unwrap(), "Sergey"); @@ -404,7 +404,7 @@ fn test_recommendations_seed() { "href": "https://api.spotify.com/v1/artists/4NHQUGzhtTLFvgF5SZesLK", "id": "4NHQUGzhtTLFvgF5SZesLK", "type": "ARTIST" - } + } "#; let seed: RecommendationsSeed = deserialize(json_str); assert_eq!(seed._type, RecommendationsSeedType::Artist); @@ -546,6 +546,436 @@ fn test_full_playlist() { "spotify:playlist:3cEYpjA9oz9GiPac4AsH4n".to_string() ); assert_eq!(full_playlist.followers.total, 109); + assert_eq!(full_playlist.items.total, 5); + #[allow(deprecated)] + { + assert_eq!(full_playlist.tracks.total, 5); + } +} + +#[test] +#[wasm_bindgen_test] +fn test_full_playlist_accepts_items_field() { + // yes the pourposes typo comes from the official spotify api ._. + let json = r#" + { + "collaborative": false, + "description": "A playlist for testing pourposes", + "external_urls": { + "spotify": "https://open.spotify.com/playlist/3cEYpjA9oz9GiPac4AsH4n" + }, + "href": "https://api.spotify.com/v1/playlists/3cEYpjA9oz9GiPac4AsH4n?locale=en-US%2Cen%3Bq%3D0.9", + "id": "3cEYpjA9oz9GiPac4AsH4n", + "images": [ + { + "url": "https://image-cdn-ak.spotifycdn.com/image/ab67706c0000da848d0ce13d55f634e290f744ba", + "height": null, + "width": null + } + ], + "name": "Spotify Web API Testing playlist", + "owner": { + "external_urls": { + "spotify": "https://open.spotify.com/user/jmperezperez" + }, + "href": "https://api.spotify.com/v1/users/jmperezperez", + "id": "jmperezperez", + "type": "user", + "uri": "spotify:user:jmperezperez", + "display_name": "JMPerez²" + }, + "public": true, + "snapshot_id": "AAAAEur2+1I6iINI0+04uFfVLiVJraUQ", + "items": { + "href": "https://api.spotify.com/v1/playlists/3cEYpjA9oz9GiPac4AsH4n/items?offset=0&limit=100&locale=en-US,en;q%3D0.9", + "limit": 100, + "next": null, + "offset": 0, + "previous": null, + "total": 5, + "items": [ + { + "added_at": "2015-01-15T12:39:22Z", + "added_by": { + "external_urls": { + "spotify": "https://open.spotify.com/user/jmperezperez" + }, + "href": "https://api.spotify.com/v1/users/jmperezperez", + "id": "jmperezperez", + "type": "user", + "uri": "spotify:user:jmperezperez" + }, + "is_local": false, + "item": { + "album": { + "album_type": "compilation", + "total_tracks": 20, + "available_markets": [], + "external_urls": { + "spotify": "https://open.spotify.com/album/2pANdqPvxInB0YvcDiw4ko" + }, + "href": "https://api.spotify.com/v1/albums/2pANdqPvxInB0YvcDiw4ko", + "id": "2pANdqPvxInB0YvcDiw4ko", + "images": [ + { + "url": "https://i.scdn.co/image/ab67616d0000b273ce6d0eef0c1ce77e5f95bbbc", + "height": 640, + "width": 640 + }, + { + "url": "https://i.scdn.co/image/ab67616d00001e02ce6d0eef0c1ce77e5f95bbbc", + "height": 300, + "width": 300 + }, + { + "url": "https://i.scdn.co/image/ab67616d00004851ce6d0eef0c1ce77e5f95bbbc", + "height": 64, + "width": 64 + } + ], + "name": "Progressive Psy Trance Picks Vol.8", + "release_date": "2012-04-02", + "release_date_precision": "day", + "type": "album", + "uri": "spotify:album:2pANdqPvxInB0YvcDiw4ko", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" + }, + "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", + "id": "0LyfQWJT6nXafLPZqxe9Of", + "name": "Various Artists", + "type": "artist", + "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" + } + ] + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6eSdhw46riw2OUHgMwR8B5" + }, + "href": "https://api.spotify.com/v1/artists/6eSdhw46riw2OUHgMwR8B5", + "id": "6eSdhw46riw2OUHgMwR8B5", + "name": "Odiseo", + "type": "artist", + "uri": "spotify:artist:6eSdhw46riw2OUHgMwR8B5" + } + ], + "available_markets": [], + "disc_number": 1, + "duration_ms": 376000, + "explicit": false, + "external_ids": { + "isrc": "DEKC41200989" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/4rzfv0JLZfVhOhbSQ8o5jZ" + }, + "href": "https://api.spotify.com/v1/tracks/4rzfv0JLZfVhOhbSQ8o5jZ", + "id": "4rzfv0JLZfVhOhbSQ8o5jZ", + "name": "Api", + "popularity": 6, + "preview_url": null, + "track_number": 10, + "type": "track", + "uri": "spotify:track:4rzfv0JLZfVhOhbSQ8o5jZ", + "is_local": false, + "episode": false, + "track": true + }, + "track": { + "album": { + "album_type": "compilation", + "total_tracks": 20, + "available_markets": [], + "external_urls": { + "spotify": "https://open.spotify.com/album/2pANdqPvxInB0YvcDiw4ko" + }, + "href": "https://api.spotify.com/v1/albums/2pANdqPvxInB0YvcDiw4ko", + "id": "2pANdqPvxInB0YvcDiw4ko", + "images": [ + { + "url": "https://i.scdn.co/image/ab67616d0000b273ce6d0eef0c1ce77e5f95bbbc", + "height": 640, + "width": 640 + }, + { + "url": "https://i.scdn.co/image/ab67616d00001e02ce6d0eef0c1ce77e5f95bbbc", + "height": 300, + "width": 300 + }, + { + "url": "https://i.scdn.co/image/ab67616d00004851ce6d0eef0c1ce77e5f95bbbc", + "height": 64, + "width": 64 + } + ], + "name": "Progressive Psy Trance Picks Vol.8", + "release_date": "2012-04-02", + "release_date_precision": "day", + "type": "album", + "uri": "spotify:album:2pANdqPvxInB0YvcDiw4ko", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" + }, + "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", + "id": "0LyfQWJT6nXafLPZqxe9Of", + "name": "Various Artists", + "type": "artist", + "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" + } + ] + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6eSdhw46riw2OUHgMwR8B5" + }, + "href": "https://api.spotify.com/v1/artists/6eSdhw46riw2OUHgMwR8B5", + "id": "6eSdhw46riw2OUHgMwR8B5", + "name": "Odiseo", + "type": "artist", + "uri": "spotify:artist:6eSdhw46riw2OUHgMwR8B5" + } + ], + "available_markets": [], + "disc_number": 1, + "duration_ms": 376000, + "explicit": false, + "external_ids": { + "isrc": "DEKC41200989" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/4rzfv0JLZfVhOhbSQ8o5jZ" + }, + "href": "https://api.spotify.com/v1/tracks/4rzfv0JLZfVhOhbSQ8o5jZ", + "id": "4rzfv0JLZfVhOhbSQ8o5jZ", + "name": "Api", + "popularity": 6, + "preview_url": null, + "track_number": 10, + "type": "track", + "uri": "spotify:track:4rzfv0JLZfVhOhbSQ8o5jZ", + "is_local": false, + "episode": false, + "track": true + }, + "primary_color": null, + "video_thumbnail": { + "url": null + } + } + ] + }, + "tracks": { + "href": "https://api.spotify.com/v1/playlists/3cEYpjA9oz9GiPac4AsH4n/tracks?offset=0&limit=100&locale=en-US,en;q%3D0.9", + "limit": 100, + "next": null, + "offset": 0, + "previous": null, + "total": 5, + "items": [ + { + "added_at": "2015-01-15T12:39:22Z", + "added_by": { + "external_urls": { + "spotify": "https://open.spotify.com/user/jmperezperez" + }, + "href": "https://api.spotify.com/v1/users/jmperezperez", + "id": "jmperezperez", + "type": "user", + "uri": "spotify:user:jmperezperez" + }, + "is_local": false, + "item": { + "album": { + "album_type": "compilation", + "total_tracks": 20, + "available_markets": [], + "external_urls": { + "spotify": "https://open.spotify.com/album/2pANdqPvxInB0YvcDiw4ko" + }, + "href": "https://api.spotify.com/v1/albums/2pANdqPvxInB0YvcDiw4ko", + "id": "2pANdqPvxInB0YvcDiw4ko", + "images": [ + { + "url": "https://i.scdn.co/image/ab67616d0000b273ce6d0eef0c1ce77e5f95bbbc", + "height": 640, + "width": 640 + }, + { + "url": "https://i.scdn.co/image/ab67616d00001e02ce6d0eef0c1ce77e5f95bbbc", + "height": 300, + "width": 300 + }, + { + "url": "https://i.scdn.co/image/ab67616d00004851ce6d0eef0c1ce77e5f95bbbc", + "height": 64, + "width": 64 + } + ], + "name": "Progressive Psy Trance Picks Vol.8", + "release_date": "2012-04-02", + "release_date_precision": "day", + "type": "album", + "uri": "spotify:album:2pANdqPvxInB0YvcDiw4ko", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" + }, + "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", + "id": "0LyfQWJT6nXafLPZqxe9Of", + "name": "Various Artists", + "type": "artist", + "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" + } + ] + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6eSdhw46riw2OUHgMwR8B5" + }, + "href": "https://api.spotify.com/v1/artists/6eSdhw46riw2OUHgMwR8B5", + "id": "6eSdhw46riw2OUHgMwR8B5", + "name": "Odiseo", + "type": "artist", + "uri": "spotify:artist:6eSdhw46riw2OUHgMwR8B5" + } + ], + "available_markets": [], + "disc_number": 1, + "duration_ms": 376000, + "explicit": false, + "external_ids": { + "isrc": "DEKC41200989" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/4rzfv0JLZfVhOhbSQ8o5jZ" + }, + "href": "https://api.spotify.com/v1/tracks/4rzfv0JLZfVhOhbSQ8o5jZ", + "id": "4rzfv0JLZfVhOhbSQ8o5jZ", + "name": "Api", + "popularity": 6, + "preview_url": null, + "track_number": 10, + "type": "track", + "uri": "spotify:track:4rzfv0JLZfVhOhbSQ8o5jZ", + "is_local": false, + "episode": false, + "track": true + }, + "track": { + "album": { + "album_type": "compilation", + "total_tracks": 20, + "available_markets": [], + "external_urls": { + "spotify": "https://open.spotify.com/album/2pANdqPvxInB0YvcDiw4ko" + }, + "href": "https://api.spotify.com/v1/albums/2pANdqPvxInB0YvcDiw4ko", + "id": "2pANdqPvxInB0YvcDiw4ko", + "images": [ + { + "url": "https://i.scdn.co/image/ab67616d0000b273ce6d0eef0c1ce77e5f95bbbc", + "height": 640, + "width": 640 + }, + { + "url": "https://i.scdn.co/image/ab67616d00001e02ce6d0eef0c1ce77e5f95bbbc", + "height": 300, + "width": 300 + }, + { + "url": "https://i.scdn.co/image/ab67616d00004851ce6d0eef0c1ce77e5f95bbbc", + "height": 64, + "width": 64 + } + ], + "name": "Progressive Psy Trance Picks Vol.8", + "release_date": "2012-04-02", + "release_date_precision": "day", + "type": "album", + "uri": "spotify:album:2pANdqPvxInB0YvcDiw4ko", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/0LyfQWJT6nXafLPZqxe9Of" + }, + "href": "https://api.spotify.com/v1/artists/0LyfQWJT6nXafLPZqxe9Of", + "id": "0LyfQWJT6nXafLPZqxe9Of", + "name": "Various Artists", + "type": "artist", + "uri": "spotify:artist:0LyfQWJT6nXafLPZqxe9Of" + } + ] + }, + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/6eSdhw46riw2OUHgMwR8B5" + }, + "href": "https://api.spotify.com/v1/artists/6eSdhw46riw2OUHgMwR8B5", + "id": "6eSdhw46riw2OUHgMwR8B5", + "name": "Odiseo", + "type": "artist", + "uri": "spotify:artist:6eSdhw46riw2OUHgMwR8B5" + } + ], + "available_markets": [], + "disc_number": 1, + "duration_ms": 376000, + "explicit": false, + "external_ids": { + "isrc": "DEKC41200989" + }, + "external_urls": { + "spotify": "https://open.spotify.com/track/4rzfv0JLZfVhOhbSQ8o5jZ" + }, + "href": "https://api.spotify.com/v1/tracks/4rzfv0JLZfVhOhbSQ8o5jZ", + "id": "4rzfv0JLZfVhOhbSQ8o5jZ", + "name": "Api", + "popularity": 6, + "preview_url": null, + "track_number": 10, + "type": "track", + "uri": "spotify:track:4rzfv0JLZfVhOhbSQ8o5jZ", + "is_local": false, + "episode": false, + "track": true + }, + "primary_color": null, + "video_thumbnail": { + "url": null + } + } + ] + }, + "type": "playlist", + "uri": "spotify:playlist:3cEYpjA9oz9GiPac4AsH4n", + "followers": { + "href": null, + "total": 1475 + }, + "primary_color": null + } + "#; + let full_playlist: FullPlaylist = deserialize(json); + assert_eq!(full_playlist.items.total, 5); + assert_eq!( + full_playlist.items.href, + "https://api.spotify.com/v1/playlists/3cEYpjA9oz9GiPac4AsH4n/items?offset=0&limit=100&locale=en-US,en;q%3D0.9" + ); + #[allow(deprecated)] + { + assert_eq!(full_playlist.tracks.total, 5); + assert_eq!(full_playlist.tracks.href, full_playlist.items.href); + } } #[test] @@ -665,7 +1095,7 @@ fn test_resume_point() { { "fully_played": false, "resume_position_ms": 423432 - } + } "#; let resume_point: ResumePoint = deserialize(json); let duration = Duration::try_milliseconds(423432).unwrap(); @@ -1177,7 +1607,7 @@ fn test_simplified_playlist() { }, "type": "playlist", "uri": "spotify:playlist:37i9dQZF1DX8mBRYewE6or" - } + } "#; let simplified_playlist: SimplifiedPlaylist = deserialize(json); assert_eq!(