Skip to content

Commit f17cdff

Browse files
committed
Add support for timestamped_ids to SpotifyWebAPI::addMyTracks()
1 parent 4e82140 commit f17cdff

File tree

4 files changed

+139
-12
lines changed

4 files changed

+139
-12
lines changed

docs/examples/managing-user-library.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,44 @@ It's also possible to list the albums, podcast episodes, or podcast shows in a u
2222

2323
```php
2424
$api->addMyTracks([
25-
'TRACK_ID',
26-
'TRACK_ID',
25+
'ids' => [
26+
'TRACK_ID',
27+
'TRACK_ID',
28+
],
29+
]);
30+
```
31+
32+
```php
33+
$api->addMyTracks([
34+
'timestamped_ids' => [
35+
['id' => 'TRACK_ID', 'added_at' => '2025-10-01T11:00:00.000Z'],
36+
['id' => 'TRACK_ID', 'added_at' => '2025-10-01T12:00:00.000Z'],
37+
],
2738
]);
2839
```
2940

30-
It's also possible to add an album, a podcast episode, or a podcast show to a user's library using `addMyAlbums`, `addMyEpisodes`, or `addMyShows`.
41+
## Adding albums, episodes, or shows to a user's library
42+
43+
```php
44+
$api->addMyAlbums([
45+
'ALBUM_ID',
46+
'ALBUM_ID',
47+
]);
48+
```
49+
50+
```php
51+
$api->addMyEpisodes([
52+
'EPISODE_ID',
53+
'EPISODE_ID',
54+
]);
55+
```
56+
57+
```php
58+
$api->addMyShows([
59+
'SHOW_ID',
60+
'SHOW_ID',
61+
]);
62+
```
3163

3264
## Deleting tracks from a user's library
3365

docs/method-reference/SpotifyWebAPI.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ Add tracks to the current user's Spotify library.<br>
175175
https://developer.spotify.com/documentation/web-api/reference/save-tracks-user
176176

177177
#### Arguments
178-
* `$tracks` **string\|array** - Track IDs or URIs to add.
178+
* `$tracks` **array\|object** - Track IDs or URIs to add. One of "ids" or "timestamped_ids" key must be present.
179+
* array ids Optional. An array of track IDs or URIs.
180+
* array timestamped_ids Optional. An array of objects containing track IDs or URIs and added_at timestamp.
179181

180182
#### Return values
181183
* **bool** Whether the tracks was successfully added.

src/SpotifyWebAPI.php

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,24 +260,51 @@ public function addMyShows(string|array $shows): bool
260260
* Add tracks to the current user's Spotify library.
261261
* https://developer.spotify.com/documentation/web-api/reference/save-tracks-user
262262
*
263-
* @param string|array $tracks Track IDs or URIs to add.
263+
* @param array|object $tracks Track IDs or URIs to add. One of "ids" or "timestamped_ids" key must be present.
264+
* - array ids Optional. An array of track IDs or URIs.
265+
* - array timestamped_ids Optional. An array of objects containing track IDs or URIs and added_at timestamp.
264266
*
265267
* @return bool Whether the tracks was successfully added.
266268
*/
267-
public function addMyTracks(string|array $tracks): bool
269+
public function addMyTracks(string|array|object $tracks): bool
268270
{
269-
$tracks = $this->uriToId($tracks, 'track');
270-
$tracks = json_encode([
271-
'ids' => (array) $tracks,
272-
]);
271+
$tracks = is_object($tracks) ? (array) $tracks : $tracks;
272+
273+
if (is_array($tracks) && isset($tracks['ids'])) {
274+
$tracks = (array) $this->uriToId($tracks['ids'], 'track');
275+
$options = json_encode([
276+
'ids' => $tracks,
277+
]);
278+
} elseif (is_array($tracks) && isset($tracks['timestamped_ids'])) {
279+
$tracks = array_map(function ($item) {
280+
$item['id'] = $this->uriToId($item['id'], 'track');
281+
282+
return $item;
283+
}, $tracks['timestamped_ids']);
284+
285+
$options = json_encode([
286+
'timestamped_ids' => $tracks,
287+
]);
288+
} else {
289+
trigger_error(
290+
// phpcs:ignore
291+
'Passing string or array without "ids" or "timestamped_ids" key to SpotifyWebAPI::addMyTracks() is deprecated',
292+
E_USER_DEPRECATED
293+
);
294+
295+
$tracks = (array) $this->uriToId($tracks, 'track');
296+
$options = json_encode([
297+
'ids' => $tracks,
298+
]);
299+
}
273300

274301
$headers = [
275302
'Content-Type' => 'application/json',
276303
];
277304

278305
$uri = '/v1/me/tracks';
279306

280-
$this->lastResponse = $this->sendRequest('PUT', $uri, $tracks, $headers);
307+
$this->lastResponse = $this->sendRequest('PUT', $uri, $options, $headers);
281308

282309
return $this->lastResponse['status'] == 200;
283310
}

tests/SpotifyWebAPITest.php

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,73 @@ public function testAddMyShows()
222222
);
223223
}
224224

225-
public function testAddMyTracks()
225+
public function testAddMyTracksIds()
226+
{
227+
$tracks = [
228+
'ids' => [
229+
'1id6H6vcwSB9GGv9NXh5cl',
230+
'3mqRLlD9j92BBv1ueFhJ1l',
231+
'spotify:track:1id6H6vcwSB9GGv9NXh5cl',
232+
],
233+
];
234+
235+
$expected = json_encode([
236+
'ids' => [
237+
'1id6H6vcwSB9GGv9NXh5cl',
238+
'3mqRLlD9j92BBv1ueFhJ1l',
239+
'1id6H6vcwSB9GGv9NXh5cl',
240+
],
241+
]);
242+
243+
$headers = ['Content-Type' => 'application/json'];
244+
$return = ['status' => 200];
245+
$api = $this->setupApi(
246+
'PUT',
247+
'/v1/me/tracks',
248+
$expected,
249+
$headers,
250+
$return
251+
);
252+
253+
$this->assertTrue(
254+
$api->addMyTracks($tracks)
255+
);
256+
}
257+
258+
public function testAddMyTracksTimestampedIds()
259+
{
260+
$tracks = [
261+
'timestamped_ids' => [
262+
['id' => '1id6H6vcwSB9GGv9NXh5cl', 'added_at' => '2025-10-01T10:00:00.000Z'],
263+
['id' => '3mqRLlD9j92BBv1ueFhJ1l', 'added_at' => '2025-10-01T11:00:00.000Z'],
264+
['id' => 'spotify:track:1id6H6vcwSB9GGv9NXh5cl', 'added_at' => '2025-10-01T12:00:00.000Z'],
265+
],
266+
];
267+
268+
$expected = json_encode([
269+
'timestamped_ids' => [
270+
['id' => '1id6H6vcwSB9GGv9NXh5cl', 'added_at' => '2025-10-01T10:00:00.000Z'],
271+
['id' => '3mqRLlD9j92BBv1ueFhJ1l', 'added_at' => '2025-10-01T11:00:00.000Z'],
272+
['id' => '1id6H6vcwSB9GGv9NXh5cl', 'added_at' => '2025-10-01T12:00:00.000Z'],
273+
],
274+
]);
275+
276+
$headers = ['Content-Type' => 'application/json'];
277+
$return = ['status' => 200];
278+
$api = $this->setupApi(
279+
'PUT',
280+
'/v1/me/tracks',
281+
$expected,
282+
$headers,
283+
$return
284+
);
285+
286+
$this->assertTrue(
287+
$api->addMyTracks($tracks)
288+
);
289+
}
290+
291+
public function testAddMyTracksDeprecated()
226292
{
227293
$tracks = [
228294
'1id6H6vcwSB9GGv9NXh5cl',

0 commit comments

Comments
 (0)