├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── README.md
├── docs
└── api.md
├── package-lock.json
├── package.json
├── src
└── index.js
└── test
├── album.js
├── artist.js
├── class.js
├── image.js
├── login.js
├── playlist.js
├── search.js
└── track.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015","stage-0"],
3 | "plugins": ["transform-runtime", "add-module-exports"]
4 | }
5 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "env": {
4 | "browser": true,
5 | "node": true,
6 | "mocha": true
7 | },
8 | "rules": {
9 | "padded-blocks": 0,
10 | "import/no-extraneous-dependencies": 0,
11 | "class-methods-use-this": 0
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib/
2 | out/
3 | coverage/
4 | .env
5 | docs.js
6 | node_modules
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | out/
3 | coverage/
4 | test/
5 | docs/
6 | .eslintrc
7 | .babelrc
8 | .gitignore
9 | .travis.yml
10 | .env
11 | docs.js
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '4'
4 | - node
5 | script: npm run-script test:cover
6 | after_script: cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
7 | env:
8 | global:
9 | - secure: GMiDv6PVRI45p/0v5FPzIxaQps+XMhErd33T5lfeYrkhp9MLxQPvCvc3V4oHs+e1hxNtSX8zOtuuAgoGPEVLyW3Fv29TYY4Ct+aPh7o2dJe6w/8MOEvuH28pOZIZMvvY3giN4PxCg5BsU07P2vEJFMleLqMYxZ/sERf9cuh/VeP6SYOBfNu8eBbi3rUcBizj/FxScstKhIfrmFAl6Xk6sJcexi4AWOcD0e7a9uHmuA0igrtOwZO8hWSH+tguCE8tbGf6s2/HEW+h9Hb2PC6FQNgtRy+BhxdLzY4uzN7UPuNC7DvTU4Le2J+ECzyXeC238PS3M/mdcvxK4SrwZ9IOg+hbhWCxnJtZh0yNuaDzuv5boCyrhCpu0sk5T3JIK+Yq7gz3qz79hsK8VzfFIuTWvSGleOriurNH1NbTwwoXL2jAPLZOQC0ggUp4pKDrIwivYCbL8mKdT2zKVn2RPIN/achSBbV0D9D9iDwtAjDDHhwNBvyhJwLYakbiNhRKW0CCiXEumE92P00cvSySjkCePcl821KdVXTGiEXIHyPvQKDkTbR1mWpf6EMIb0zjuS0+eDcdUb2irBuDFB3vqWQnS+CnedokWnFqb+Txdmf5mZA65Gr7DVTZPg6NC6PpiT0yTFmsQl2l7bxiObXJm12AMF4vHqCbJovADTjLodcDhjI=
10 | - secure: xb/69+bDQhx8FBIEHGfWlFTnZ7Kk1UzZ5nWXavdOwECf/v09sbt5dabbvKuQ2YgyIh/srPTKnOQH35j6C7mva5EVev7/ypx9xJcl+azgcsW3RHVNb3+TWLtnMxIIamDW+1H/edD/bM06Q5aZjwqk3+Mm7mKnxrCejGzap9vvaP+6MKuRGHIFrwASZINAj+jO6bfZY6sRJ2KGrHBSObTsiRxciKkA125FWndJiK3nt2kSCe6wB25qwqg7DG+KjJQdcVZXfvd4kseSgaeBKAxkIcDx0+6dLDOjuL0v6fUjq4mKmNTDtCLakwrUr8t0jH1lB8mtlficifAsJC8rasknZPXUSw8yLrtzmc38/NKlXW1v2oAKP21LPoQdNOQpiVHwDydZ23lrdObpZMa6RCtkqFX28jKbEBIBx9L6UwP9yDBFpdyg8jkOl9tx1RLdBAYgB5B11rQsMjKb8RklyX6iMXIzP2VR/rFrPYcTQWzhNje3FQEhEi9B1qV/V4YlM2szrqN8t6jYlcx+WCs9gC7vSYATmTiy+PbJ64/mUwGTwnAJDeG+8XJPd3oOQjn1WxG5Sjn5JLOEadptflPdW/b0eJIaZHj5Zdcl8Oy6/FCUnYgWHvrBuNFAGLTHgEYVRf3+lBnVxrnlq1f3kZ5oL1XzKjvL3LrxJ1Fr2yELXip8ywo=
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | THIS IS UNMAINTAINED
2 | --------------------
3 |
4 | # tidal-api-wrapper
5 |
6 | This library is no longer maintained. It will remain hosted in read-only mode for forking.
7 |
8 | ## Documentation
9 |
10 | [API Reference](https://github.com/spencercharest/tidal-api/tree/master/docs/api.md)
11 |
--------------------------------------------------------------------------------
/docs/api.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Tidal
4 | Class
5 |
6 | **Kind**: global class
7 |
8 | * [Tidal](#Tidal)
9 | * [new Tidal([options])](#new_Tidal_new)
10 | * [.login(username, password)](#Tidal+login) ⇒ Promise
11 | * [.search(query, type, [limit])](#Tidal+search) ⇒ Promise
12 | * [.getTrack(id)](#Tidal+getTrack) ⇒ Promise
13 | * [.getFavoriteTracks()](#Tidal+getFavoriteTracks) ⇒ Promise
14 | * [.getAlbum(id)](#Tidal+getAlbum) ⇒ Promise
15 | * [.getAlbumTracks(id)](#Tidal+getAlbumTracks) ⇒ Promise
16 | * [.getTopAlbums()](#Tidal+getTopAlbums) ⇒ Promise
17 | * [.getNewAlbums()](#Tidal+getNewAlbums) ⇒ Promise
18 | * [.getStaffPickAlbums()](#Tidal+getStaffPickAlbums) ⇒ Promise
19 | * [.getFavoriteAlbums()](#Tidal+getFavoriteAlbums) ⇒ Promise
20 | * [.getArtist(id)](#Tidal+getArtist) ⇒ Promise
21 | * [.getArtistAlbums(id)](#Tidal+getArtistAlbums) ⇒ Promise
22 | * [.getArtistEPsAndSingles(id)](#Tidal+getArtistEPsAndSingles) ⇒ Promise
23 | * [.getArtistCompilations(id)](#Tidal+getArtistCompilations) ⇒ Promise
24 | * [.getArtistTopTracks(id, [limit])](#Tidal+getArtistTopTracks) ⇒ Promise
25 | * [.getSimilarArtists(id)](#Tidal+getSimilarArtists) ⇒ Promise
26 | * [.getFavoriteArtists()](#Tidal+getFavoriteArtists) ⇒ Promise
27 | * [.getPlaylist(uuid)](#Tidal+getPlaylist) ⇒ Promise
28 | * [.getPlaylistTracks(uuid)](#Tidal+getPlaylistTracks) ⇒ Promise
29 | * [.getFavoritePlaylists()](#Tidal+getFavoritePlaylists) ⇒ Promise
30 | * [.getPlaylists()](#Tidal+getPlaylists) ⇒ Promise
31 | * [.artistPicToUrl(uuid)](#Tidal+artistPicToUrl) ⇒ Object
32 | * [.albumArtToUrl(uuid)](#Tidal+albumArtToUrl) ⇒ Object
33 |
34 |
35 |
36 | ### new Tidal([options])
37 |
38 | | Param | Type | Default | Description |
39 | | --- | --- | --- | --- |
40 | | [options] | object
| | Tidal options (optional) |
41 | | [options.countryCode] | string
| "US"
| Tidal country code |
42 | | [options.limit] | number
| 1000
| API results limit |
43 |
44 |
45 |
46 | ### tidal.login(username, password) ⇒ Promise
47 | login to Tidal in order to use methods that require authentication
48 |
49 | **Kind**: instance method of [Tidal
](#Tidal)
50 | **Fulfil**: Object
- user data object (see example for object properties)
51 | **Reject**: Error
52 |
53 | | Param | Type | Description |
54 | | --- | --- | --- |
55 | | username | string
| Tidal username or email |
56 | | password | string
| Tidal password |
57 |
58 | **Example**
59 | ```js
60 | tidal.login('username', 'password')
61 | // returns a promise that resolves to
62 | {
63 | userId: 49927020,
64 | sessionId: '24d3d406-e6b9-457a-bf57-eac7b113a20c',
65 | countryCode: 'US'
66 | }
67 | ```
68 |
69 |
70 | ### tidal.search(query, type, [limit]) ⇒ Promise
71 | search for artists, albums, tracks, or playlists
72 |
73 | **Kind**: instance method of [Tidal
](#Tidal)
74 | **Fulfil**: Array
- an array of objects (object properties are dependent on search type)
75 | **Reject**: Error
76 |
77 | | Param | Type | Default | Description |
78 | | --- | --- | --- | --- |
79 | | query | string
| | search query |
80 | | type | string
| | search type ('artists', 'albums', 'tracks', 'playlists') |
81 | | [limit] | number
| 25
| search limit |
82 |
83 | **Example**
84 | ```js
85 | tidal.search('Four Year Strong', 'artists', 1)
86 | // returns a promise that resolves to:
87 | [
88 | {
89 | "id": 3575680,
90 | "name": "Four Year Strong",
91 | "url": "http://www.tidal.com/artist/3575680",
92 | "picture": "04d63cd8-a1a5-42e0-b1ec-8e336b7d9200",
93 | "popularity": 28
94 | }
95 | ]
96 | ```
97 |
98 |
99 | ### tidal.getTrack(id) ⇒ Promise
100 | get a track by its id
101 |
102 | **Kind**: instance method of [Tidal
](#Tidal)
103 | **Fulfil**: Object
- a track object (see example for object properties)
104 | **Reject**: Error
105 |
106 | | Param | Type | Description |
107 | | --- | --- | --- |
108 | | id | number
| track id |
109 |
110 | **Example**
111 | ```js
112 | tidal.getTrack(64975224)
113 | // returns a promise that resolves to:
114 | {
115 | "id": 64975224,
116 | "title": "22 (OVER S∞∞N)",
117 | "duration": 168,
118 | "replayGain": -10.71,
119 | "peak": 0.692531,
120 | "allowStreaming": true,
121 | "streamReady": true,
122 | "streamStartDate": "2016-09-30T00:00:00.000+0000",
123 | "premiumStreamingOnly": false,
124 | "trackNumber": 1,
125 | "volumeNumber": 1,
126 | "version": null,
127 | "popularity": 47,
128 | "copyright": "2016 Jagjaguwar",
129 | "url": "http://www.tidal.com/track/64975224",
130 | "isrc": "US38Y1630001",
131 | "editable": true,
132 | "explicit": false,
133 | "audioQuality": "LOSSLESS",
134 | "artist": {
135 | "id": 3566315,
136 | "name": "Bon Iver",
137 | "type": "MAIN"
138 | },
139 | "artists": [
140 | {
141 | "id": 3566315,
142 | "name": "Bon Iver",
143 | "type": "MAIN"
144 | }
145 | ],
146 | "album": {
147 | "id": 64975223,
148 | "title": "22, A Million",
149 | "cover": "5ac41fbb-927b-427e-8224-87bf12d218a3"
150 | }
151 | }
152 | ```
153 |
154 |
155 | ### tidal.getFavoriteTracks() ⇒ Promise
156 | get your favorite (starred) tracks (requires login() to be called)
157 |
158 | **Kind**: instance method of [Tidal
](#Tidal)
159 | **Fulfil**: Array
- an array of track objects
160 | **Reject**: Error
161 | **See**
162 |
163 | - [login](#Tidal+login) - login method must be called first
164 | - [getTrack](#Tidal+getTrack) - track object example
165 |
166 | **Example**
167 | ```js
168 | tidal.getFavoriteTracks()
169 | ```
170 |
171 |
172 | ### tidal.getAlbum(id) ⇒ Promise
173 | get an album by its id
174 |
175 | **Kind**: instance method of [Tidal
](#Tidal)
176 | **Fulfil**: Object
- an album object (see example for object properties)
177 | **Reject**: Error
178 |
179 | | Param | Type | Description |
180 | | --- | --- | --- |
181 | | id | number
| album id |
182 |
183 | **Example**
184 | ```js
185 | tidal.getAlbum(80216363)
186 | // returns a promise that resolves to:
187 | {
188 | "id": 80216363,
189 | "title": "Pacific Daydream",
190 | "duration": 2069,
191 | "streamReady": true,
192 | "streamStartDate": "2017-10-27T00:00:00.000+0000",
193 | "allowStreaming": true,
194 | "premiumStreamingOnly": false,
195 | "numberOfTracks": 10,
196 | "numberOfVideos": 0,
197 | "numberOfVolumes": 1,
198 | "releaseDate": "2017-10-27",
199 | "copyright": "2017 Weezer under exclusive license to Crush Music / \
200 | Atlantic Recording Corporation for the United States and Crush Music / \
201 | WEA International Inc. for the world excluding the United States. \
202 | A Warner Music Company.",
203 | "type": "ALBUM",
204 | "version": null,
205 | "url": "http://www.tidal.com/album/80216363",
206 | "cover": "86538ca7-08fd-40ff-9a75-af88d74d1f48",
207 | "videoCover": null,
208 | "explicit": false,
209 | "upc": "075679889355",
210 | "popularity": 58,
211 | "audioQuality": "LOSSLESS",
212 | "artist": {
213 | "id": 30157,
214 | "name": "Weezer",
215 | "type": "MAIN"
216 | },
217 | "artists": [
218 | {
219 | "id": 30157,
220 | "name": "Weezer",
221 | "type": "MAIN"
222 | }
223 | ]
224 | }
225 | ```
226 |
227 |
228 | ### tidal.getAlbumTracks(id) ⇒ Promise
229 | get album tracks by album id
230 |
231 | **Kind**: instance method of [Tidal
](#Tidal)
232 | **Fulfil**: Array
- an array of track objects
233 | **Reject**: Error
234 | **See**: [getTrack](#Tidal+getTrack) - track object example
235 |
236 | | Param | Type | Description |
237 | | --- | --- | --- |
238 | | id | number
| album id |
239 |
240 | **Example**
241 | ```js
242 | tidal.getAlbumTracks(80216363)
243 | ```
244 |
245 |
246 | ### tidal.getTopAlbums() ⇒ Promise
247 | get top 20 albums on Tidal
248 |
249 | **Kind**: instance method of [Tidal
](#Tidal)
250 | **Fulfil**: Array
- an array of album objects
251 | **Reject**: Error
252 | **See**: [getAlbum](#Tidal+getAlbum) - album object example
253 | **Example**
254 | ```js
255 | tidal.getTopAlbums()
256 | ```
257 |
258 |
259 | ### tidal.getNewAlbums() ⇒ Promise
260 | get new albums on Tidal
261 |
262 | **Kind**: instance method of [Tidal
](#Tidal)
263 | **Fulfil**: Array
- an array of album objects
264 | **Reject**: Error
265 | **See**: [getAlbum](#Tidal+getAlbum) - album object example
266 | **Example**
267 | ```js
268 | tidal.getNewAlbums()
269 | ```
270 |
271 |
272 | ### tidal.getStaffPickAlbums() ⇒ Promise
273 | get staff pick albums on Tidal
274 |
275 | **Kind**: instance method of [Tidal
](#Tidal)
276 | **Fulfil**: Array
- an array of album objects
277 | **Reject**: Error
278 | **See**: [getAlbum](#Tidal+getAlbum) - album object example
279 | **Example**
280 | ```js
281 | tidal.getStaffPickAlbums()
282 | ```
283 |
284 |
285 | ### tidal.getFavoriteAlbums() ⇒ Promise
286 | get your favorite (starred) albums (requires login() to be called)
287 |
288 | **Kind**: instance method of [Tidal
](#Tidal)
289 | **Fulfil**: Array
- an array of album objects
290 | **Reject**: Error
291 | **See**
292 |
293 | - [login](#Tidal+login) - login method must be called first
294 | - [getAlbum](#Tidal+getAlbum) - album object example
295 |
296 | **Example**
297 | ```js
298 | tidal.getFavoriteAlbums()
299 | ```
300 |
301 |
302 | ### tidal.getArtist(id) ⇒ Promise
303 | get an artist by its id
304 |
305 | **Kind**: instance method of [Tidal
](#Tidal)
306 | **Fulfil**: Object
- an artist object (see example for object properties)
307 | **Reject**: Error
308 |
309 | | Param | Type | Description |
310 | | --- | --- | --- |
311 | | id | number
| artist id |
312 |
313 | **Example**
314 | ```js
315 | tidal.getArtist(3575680)
316 | // returns a promise that resolves to:
317 | {
318 | "id": 3575680,
319 | "name": "Four Year Strong",
320 | "url": "http://www.tidal.com/artist/3575680",
321 | "picture": "04d63cd8-a1a5-42e0-b1ec-8e336b7d9200",
322 | "popularity": 28
323 | }
324 | ```
325 |
326 |
327 | ### tidal.getArtistAlbums(id) ⇒ Promise
328 | get artist albums by artist id
329 |
330 | **Kind**: instance method of [Tidal
](#Tidal)
331 | **Fulfil**: Array
- an array of album objects
332 | **Reject**: Error
333 | **See**: [getAlbum](#Tidal+getAlbum) - album object example
334 |
335 | | Param | Type | Description |
336 | | --- | --- | --- |
337 | | id | number
| artist id |
338 |
339 | **Example**
340 | ```js
341 | tidal.getArtistAlbums(3575680)
342 | ```
343 |
344 |
345 | ### tidal.getArtistEPsAndSingles(id) ⇒ Promise
346 | get artist EPs and singles by artist id
347 |
348 | **Kind**: instance method of [Tidal
](#Tidal)
349 | **Fulfil**: Array
- an array of album objects
350 | **Reject**: Error
351 | **See**: [getAlbum](#Tidal+getAlbum) - album object example
352 |
353 | | Param | Type | Description |
354 | | --- | --- | --- |
355 | | id | number
| artist id |
356 |
357 | **Example**
358 | ```js
359 | tidal.getArtistEPsAndSingles(3575680)
360 | ```
361 |
362 |
363 | ### tidal.getArtistCompilations(id) ⇒ Promise
364 | get compliations that artist has appeared on by artist id
365 |
366 | **Kind**: instance method of [Tidal
](#Tidal)
367 | **Fulfil**: Array
- an array of album objects
368 | **Reject**: Error
369 | **See**: [getAlbum](#Tidal+getAlbum) - album object example
370 |
371 | | Param | Type | Description |
372 | | --- | --- | --- |
373 | | id | number
| artist id |
374 |
375 | **Example**
376 | ```js
377 | tidal.getArtistCompilations(3575680)
378 | ```
379 |
380 |
381 | ### tidal.getArtistTopTracks(id, [limit]) ⇒ Promise
382 | get top tracks by artist
383 |
384 | **Kind**: instance method of [Tidal
](#Tidal)
385 | **Fulfil**: Array
- an array of track objects
386 | **Reject**: Error
387 | **See**: [getTrack](#Tidal+getTrack) - track object example
388 |
389 | | Param | Type | Default | Description |
390 | | --- | --- | --- | --- |
391 | | id | number
| | artist id |
392 | | [limit] | number
| 10
| results limit |
393 |
394 | **Example**
395 | ```js
396 | tidal.getArtistTopTracks(3575680)
397 | ```
398 |
399 |
400 | ### tidal.getSimilarArtists(id) ⇒ Promise
401 | get similar artists
402 |
403 | **Kind**: instance method of [Tidal
](#Tidal)
404 | **Fulfil**: Object
- artist object
405 | **Reject**: Error
406 | **See**: [getArtist](#Tidal+getArtist) - artist object example
407 |
408 | | Param | Type | Description |
409 | | --- | --- | --- |
410 | | id | number
| artist id |
411 |
412 | **Example**
413 | ```js
414 | tidal.getSimilarArtists(3575680)
415 | ```
416 |
417 |
418 | ### tidal.getFavoriteArtists() ⇒ Promise
419 | get your favorite (starred) artists (requires login() to be called)
420 |
421 | **Kind**: instance method of [Tidal
](#Tidal)
422 | **Fulfil**: Array
- an array of artist objects
423 | **Reject**: Error
424 | **See**
425 |
426 | - [login](#Tidal+login) - login method must be called first
427 | - [getArtist](#Tidal+getArtist) - artist object example
428 |
429 | **Example**
430 | ```js
431 | tidal.getFavoriteArtists()
432 | ```
433 |
434 |
435 | ### tidal.getPlaylist(uuid) ⇒ Promise
436 | get a playlist by its uuid
437 |
438 | **Kind**: instance method of [Tidal
](#Tidal)
439 | **Fulfil**: Object
- playlist object (see example for object properties)
440 | **Reject**: Error
441 |
442 | | Param | Type | Description |
443 | | --- | --- | --- |
444 | | uuid | string
| playlist uuid |
445 |
446 | **Example**
447 | ```js
448 | tidal.getPlaylist('1c5d01ed-4f05-40c4-bd28-0f73099e9648')
449 | // returns a promise that resolves to:
450 | {
451 | "uuid": "1c5d01ed-4f05-40c4-bd28-0f73099e9648",
452 | "title": "Get Down On It: Soul, Funk and Disco Supremo",
453 | "numberOfTracks": 100,
454 | "numberOfVideos": 0,
455 | "creator": {
456 | "id": 0
457 | },
458 | "description": "Get down and dirty with some of the finest soul, funk \
459 | and four-to-the floor disco out there. Bound to get the blood pumping, \
460 | this playlist boasts more hits than a boxing match, more hooks than a \
461 | tackle box and marks the perfect prescription both for champagne days \
462 | and boogie nights and alike. Whether you feel like being a sex machine \
463 | or simply wish to dance to the music, rock your body, dig it and don't \
464 | stop 'til you get enough! ",
465 | "duration": 25732,
466 | "lastUpdated": "2017-01-18T16:31:51.839+0000",
467 | "created": "2016-09-22T16:42:40.911+0000",
468 | "type": "EDITORIAL",
469 | "publicPlaylist": true,
470 | "url": "http://www.tidal.com/playlist/1c5d01ed-4f05-40c4-bd28-0f73099e9648",
471 | "image": "7a707631-02cf-47d8-a34c-e1395165f169",
472 | "popularity": 0
473 | }
474 | ```
475 |
476 |
477 | ### tidal.getPlaylistTracks(uuid) ⇒ Promise
478 | get playlist tracks by playlist uuid
479 |
480 | **Kind**: instance method of [Tidal
](#Tidal)
481 | **Fulfil**: Array
- an array of track objects
482 | **Reject**: Error
483 | **See**: [getTrack](#Tidal+getTrack) - track object example
484 |
485 | | Param | Type | Description |
486 | | --- | --- | --- |
487 | | uuid | string
| playlist uuid |
488 |
489 | **Example**
490 | ```js
491 | tidal.getPlaylistTracks('1c5d01ed-4f05-40c4-bd28-0f73099e9648')
492 | ```
493 |
494 |
495 | ### tidal.getFavoritePlaylists() ⇒ Promise
496 | get your favorite (starred) playlists (requires login() to be called)
497 |
498 | **Kind**: instance method of [Tidal
](#Tidal)
499 | **Fulfil**: Array
- an array of playlist objects
500 | **Reject**: Error
501 | **See**
502 |
503 | - [login](#Tidal+login) - login method must be called first
504 | - [getPlaylist](#Tidal+getPlaylist) - playlist object example
505 |
506 | **Example**
507 | ```js
508 | tidal.getFavoritePlaylists()
509 | ```
510 |
511 |
512 | ### tidal.getPlaylists() ⇒ Promise
513 | get your created playlists (requires login() to be called)
514 |
515 | **Kind**: instance method of [Tidal
](#Tidal)
516 | **Fulfil**: Array
- an array of playlist objects
517 | **Reject**: Error
518 | **See**
519 |
520 | - [login](#Tidal+login) - login method must be called first
521 | - [getPlaylist](#Tidal+getPlaylist) - playlist object example
522 |
523 | **Example**
524 | ```js
525 | tidal.getPlaylists()
526 | ```
527 |
528 |
529 | ### tidal.artistPicToUrl(uuid) ⇒ Object
530 | get valid urls to artist pictures
531 |
532 | **Kind**: instance method of [Tidal
](#Tidal)
533 |
534 | | Param | Type | Description |
535 | | --- | --- | --- |
536 | | uuid | string
| artist picture uuid (can be found as picture property in artist object) |
537 |
538 | **Example**
539 | ```js
540 | tidal.artistPicToUrl('04d63cd8-a1a5-42e0-b1ec-8e336b7d9200')
541 | // returns
542 | {
543 | sm: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/160x107.jpg',
544 | md: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/320x214.jpg',
545 | lg: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/640x428.jpg'
546 | }
547 | ```
548 |
549 |
550 | ### tidal.albumArtToUrl(uuid) ⇒ Object
551 | get valid urls to album art
552 |
553 | **Kind**: instance method of [Tidal
](#Tidal)
554 |
555 | | Param | Type | Description |
556 | | --- | --- | --- |
557 | | uuid | string
| album art uuid (can be found as cover property in album object) |
558 |
559 | **Example**
560 | ```js
561 | tidal.albumArtToUrl('9a56f482-e9cf-46c3-bb21-82710e7854d4')
562 | // returns
563 | {
564 | sm: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/160x160.jpg',
565 | md: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/320x320.jpg',
566 | lg: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/640x640.jpg',
567 | xl: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/1280x1280.jpg'
568 | }
569 | ```
570 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tidal-api-wrapper",
3 | "version": "1.7.1",
4 | "description": "An unofficial API wrapper for Tidal Music",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "build": "babel -d lib/ src/",
8 | "start": "babel-node src/",
9 | "pretest": "npm run build",
10 | "test": "mocha --timeout 5000 --require babel-core/register --require babel-polyfill ./test/**/*.js",
11 | "test:cover": "babel-node ./node_modules/.bin/babel-istanbul cover _mocha -- --timeout 10000",
12 | "prepublish": "npm run build",
13 | "docs": "jsdoc2md src/*.js > docs/api.md"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/spencercharest/tidal-api.git"
18 | },
19 | "keywords": [
20 | "flac",
21 | "alac",
22 | "lossless",
23 | "music",
24 | "stream",
25 | "streaming",
26 | "tidal"
27 | ],
28 | "author": "Spencer Charest",
29 | "license": "ISC",
30 | "bugs": {
31 | "url": "https://github.com/spencercharest/tidal-api/issues"
32 | },
33 | "homepage": "https://github.com/spencercharest/tidal-api#readme",
34 | "dependencies": {
35 | "axios": "^0.17.0",
36 | "babel-runtime": "^6.26.0"
37 | },
38 | "devDependencies": {
39 | "babel-cli": "^6.26.0",
40 | "babel-core": "^6.26.0",
41 | "babel-istanbul": "^0.12.2",
42 | "babel-plugin-add-module-exports": "^0.2.1",
43 | "babel-plugin-transform-runtime": "^6.23.0",
44 | "babel-preset-es2015": "^6.24.1",
45 | "babel-preset-stage-0": "^6.24.1",
46 | "babel-register": "^6.26.0",
47 | "chai": "^4.1.2",
48 | "chai-as-promised": "^7.1.1",
49 | "chai-things": "^0.2.0",
50 | "coveralls": "^3.0.0",
51 | "dotenv": "^4.0.0",
52 | "eslint": "^4.9.0",
53 | "eslint-config-airbnb": "^16.1.0",
54 | "eslint-plugin-import": "^2.8.0",
55 | "eslint-plugin-jsx-a11y": "^6.0.2",
56 | "eslint-plugin-react": "^7.4.0",
57 | "jsdoc-to-markdown": "^3.1.0-0",
58 | "mocha": "^4.0.1"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import qs from 'querystring';
3 |
4 | /** Class */
5 | class Tidal {
6 |
7 | /**
8 | *
9 | * @param {object} [options] - Tidal options (optional)
10 | * @param {string} [options.countryCode=US] - Tidal country code
11 | * @param {number} [options.limit=1000] - API results limit
12 | */
13 | constructor(options = {}) {
14 | this.url = 'https://api.tidal.com/v1';
15 | this.webToken = 'kgsOOmYk3zShYrNP';
16 | this.countryCode = options.countryCode || 'US';
17 | this.limit = options.limit || 1000;
18 | this.api = axios.create({
19 | baseURL: this.url,
20 | headers: {
21 | 'x-tidal-token': this.webToken,
22 | },
23 | });
24 | // some base params for GET requests
25 | this.params = `limit=${this.limit}&countryCode=${this.countryCode}`;
26 | // params for Tidal pages that require a locale and device type
27 | this.localeParams = `locale=en_${this.countryCode}&deviceType=BROWSER&countryCode=${this.countryCode}`;
28 | }
29 |
30 | /**
31 | * login to Tidal in order to use methods that require authentication
32 | * @param {string} username - Tidal username or email
33 | * @param {string} password - Tidal password
34 | * @example tidal.login('username', 'password')
35 | * // returns a promise that resolves to
36 | {
37 | userId: 49927020,
38 | sessionId: '24d3d406-e6b9-457a-bf57-eac7b113a20c',
39 | countryCode: 'US'
40 | }
41 | * @returns {Promise}
42 | * @fulfil {Object} - user data object (see example for object properties)
43 | * @reject {Error}
44 | */
45 | async login(username, password) {
46 |
47 | if (!username || !password) {
48 | throw new Error('Username and password are required arguments of login()');
49 | }
50 |
51 | const params = qs.stringify({
52 | username,
53 | password,
54 | });
55 |
56 | const res = await this.api({
57 | method: 'POST',
58 | url: `/login/username?token=${this.webToken}`,
59 | data: params,
60 | });
61 |
62 | // store this info for use in other methods
63 | this.userId = res.data.userId;
64 | this.sessionId = res.data.sessionId;
65 | this.params = `${this.params}&sessionId=${res.data.sessionId}`;
66 |
67 | return res.data;
68 | }
69 |
70 | /**
71 | * search for artists, albums, tracks, or playlists
72 | * @param {string} query - search query
73 | * @param {string} type - search type ('artists', 'albums', 'tracks', 'playlists')
74 | * @param {number} [limit] - search limit
75 | * @example
76 | * tidal.search('Four Year Strong', 'artists', 1)
77 | * // returns a promise that resolves to:
78 | [
79 | {
80 | "id": 3575680,
81 | "name": "Four Year Strong",
82 | "url": "http://www.tidal.com/artist/3575680",
83 | "picture": "04d63cd8-a1a5-42e0-b1ec-8e336b7d9200",
84 | "popularity": 28
85 | }
86 | ]
87 | * @returns {Promise}
88 | * @fulfil {Array} - an array of objects (object properties are dependent on search type)
89 | * @reject {Error}
90 | */
91 | async search(query, type, limit = 25) {
92 |
93 | const accTypes = ['artists', 'albums', 'tracks', 'playlists'];
94 |
95 | if (!type) {
96 | throw new Error('Search requires type as a second argument (artists, albums, tracks, or playlists)');
97 | }
98 |
99 | if (accTypes.indexOf(type) < 0) {
100 | throw new Error(`${type} is not a valid search type('artists', 'albums', 'tracks', 'playlists' are valid).`);
101 | }
102 |
103 | const res = await this.api({
104 | method: 'GET',
105 | url: `/search/${type}?query=${query}&limit=${limit}&countryCode=${this.countryCode}`,
106 | });
107 |
108 | return res.data.items;
109 | }
110 |
111 | /**
112 | * get a track by its id
113 | * @param {number} id - track id
114 | * @example
115 | * tidal.getTrack(64975224)
116 | * // returns a promise that resolves to:
117 | {
118 | "id": 64975224,
119 | "title": "22 (OVER S∞∞N)",
120 | "duration": 168,
121 | "replayGain": -10.71,
122 | "peak": 0.692531,
123 | "allowStreaming": true,
124 | "streamReady": true,
125 | "streamStartDate": "2016-09-30T00:00:00.000+0000",
126 | "premiumStreamingOnly": false,
127 | "trackNumber": 1,
128 | "volumeNumber": 1,
129 | "version": null,
130 | "popularity": 47,
131 | "copyright": "2016 Jagjaguwar",
132 | "url": "http://www.tidal.com/track/64975224",
133 | "isrc": "US38Y1630001",
134 | "editable": true,
135 | "explicit": false,
136 | "audioQuality": "LOSSLESS",
137 | "artist": {
138 | "id": 3566315,
139 | "name": "Bon Iver",
140 | "type": "MAIN"
141 | },
142 | "artists": [
143 | {
144 | "id": 3566315,
145 | "name": "Bon Iver",
146 | "type": "MAIN"
147 | }
148 | ],
149 | "album": {
150 | "id": 64975223,
151 | "title": "22, A Million",
152 | "cover": "5ac41fbb-927b-427e-8224-87bf12d218a3"
153 | }
154 | }
155 | * @returns {Promise}
156 | * @fulfil {Object} - a track object (see example for object properties)
157 | * @reject {Error}
158 | */
159 | async getTrack(id) {
160 |
161 | const res = await this.api({
162 | method: 'GET',
163 | url: `/tracks/${id}?${this.params}`,
164 | });
165 |
166 | return res.data;
167 | }
168 |
169 | /**
170 | * get your favorite (starred) tracks (requires login() to be called)
171 | * @example tidal.getFavoriteTracks()
172 | * @returns {Promise}
173 | * @fulfil {Array} - an array of track objects
174 | * @reject {Error}
175 | * @see {@link Tidal#login} - login method must be called first
176 | * @see {@link Tidal#getTrack} - track object example
177 | */
178 | async getFavoriteTracks() {
179 |
180 | if (!this.userId || !this.sessionId) {
181 | throw new Error('You must call the login method first');
182 | }
183 |
184 | const res = await this.api({
185 | method: 'GET',
186 | url: `/users/${this.userId}/favorites/tracks?${this.params}`,
187 | });
188 |
189 | const { items } = res.data;
190 |
191 | const tracks = items.map(item => item.item);
192 |
193 | return tracks;
194 | }
195 |
196 | /**
197 | * get an album by its id
198 | * @param {number} id - album id
199 | * @example tidal.getAlbum(80216363)
200 | * // returns a promise that resolves to:
201 | {
202 | "id": 80216363,
203 | "title": "Pacific Daydream",
204 | "duration": 2069,
205 | "streamReady": true,
206 | "streamStartDate": "2017-10-27T00:00:00.000+0000",
207 | "allowStreaming": true,
208 | "premiumStreamingOnly": false,
209 | "numberOfTracks": 10,
210 | "numberOfVideos": 0,
211 | "numberOfVolumes": 1,
212 | "releaseDate": "2017-10-27",
213 | "copyright": "2017 Weezer under exclusive license to Crush Music / \
214 | Atlantic Recording Corporation for the United States and Crush Music / \
215 | WEA International Inc. for the world excluding the United States. \
216 | A Warner Music Company.",
217 | "type": "ALBUM",
218 | "version": null,
219 | "url": "http://www.tidal.com/album/80216363",
220 | "cover": "86538ca7-08fd-40ff-9a75-af88d74d1f48",
221 | "videoCover": null,
222 | "explicit": false,
223 | "upc": "075679889355",
224 | "popularity": 58,
225 | "audioQuality": "LOSSLESS",
226 | "artist": {
227 | "id": 30157,
228 | "name": "Weezer",
229 | "type": "MAIN"
230 | },
231 | "artists": [
232 | {
233 | "id": 30157,
234 | "name": "Weezer",
235 | "type": "MAIN"
236 | }
237 | ]
238 | }
239 | * @returns {Promise}
240 | * @fulfil {Object} - an album object (see example for object properties)
241 | * @reject {Error}
242 | */
243 | async getAlbum(id) {
244 |
245 | const res = await this.api({
246 | method: 'GET',
247 | url: `/albums/${id}?${this.params}`,
248 | });
249 |
250 | return res.data;
251 | }
252 |
253 | /**
254 | * get album tracks by album id
255 | * @param {number} id - album id
256 | * @example tidal.getAlbumTracks(80216363)
257 | * @returns {Promise}
258 | * @fulfil {Array} - an array of track objects
259 | * @reject {Error}
260 | * @see {@link Tidal#getTrack} - track object example
261 | */
262 | async getAlbumTracks(id) {
263 |
264 | const res = await this.api({
265 | method: 'GET',
266 | url: `/albums/${id}/tracks?${this.params}`,
267 | });
268 |
269 | return res.data.items;
270 | }
271 |
272 | // this is an internal method and so won't be included in JSDOC
273 | async getFeaturedAlbums() {
274 |
275 | const res = await this.api({
276 | method: 'GET',
277 | url: `/pages/show_more_featured_albums?${this.localeParams}`,
278 | });
279 |
280 | const { tabs } = res.data.rows[0].modules[0];
281 |
282 | const topAlbums = tabs.find(tab => tab.key === 'featured-top');
283 | const newAlbums = tabs.find(tab => tab.key === 'featured-new');
284 | const staffPicks = tabs.find(tab => tab.key === 'featured-recommended');
285 |
286 | return {
287 | topAlbums: topAlbums.pagedList.items,
288 | newAlbums: newAlbums.pagedList.items,
289 | staffPicks: staffPicks.pagedList.items,
290 | };
291 | }
292 |
293 | /**
294 | * get top 20 albums on Tidal
295 | * @example tidal.getTopAlbums()
296 | * @returns {Promise}
297 | * @fulfil {Array} - an array of album objects
298 | * @reject {Error}
299 | * @see {@link Tidal#getAlbum} - album object example
300 | */
301 | async getTopAlbums() {
302 |
303 | const featuredAlbums = await this.getFeaturedAlbums();
304 |
305 | return featuredAlbums.topAlbums;
306 | }
307 |
308 | /**
309 | * get new albums on Tidal
310 | * @example tidal.getNewAlbums()
311 | * @returns {Promise}
312 | * @fulfil {Array} - an array of album objects
313 | * @reject {Error}
314 | * @see {@link Tidal#getAlbum} - album object example
315 | */
316 | async getNewAlbums() {
317 |
318 | const featuredAlbums = await this.getFeaturedAlbums();
319 |
320 | return featuredAlbums.newAlbums;
321 | }
322 |
323 | /**
324 | * get staff pick albums on Tidal
325 | * @example tidal.getStaffPickAlbums()
326 | * @returns {Promise}
327 | * @fulfil {Array} - an array of album objects
328 | * @reject {Error}
329 | * @see {@link Tidal#getAlbum} - album object example
330 | */
331 | async getStaffPickAlbums() {
332 |
333 | const featuredAlbums = await this.getFeaturedAlbums();
334 |
335 | return featuredAlbums.staffPicks;
336 | }
337 |
338 | /**
339 | * get your favorite (starred) albums (requires login() to be called)
340 | * @example tidal.getFavoriteAlbums()
341 | * @returns {Promise}
342 | * @fulfil {Array} - an array of album objects
343 | * @reject {Error}
344 | * @see {@link Tidal#login} - login method must be called first
345 | * @see {@link Tidal#getAlbum} - album object example
346 | */
347 | async getFavoriteAlbums() {
348 |
349 | if (!this.userId || !this.sessionId) {
350 | throw new Error('You must call the login method first');
351 | }
352 |
353 | const res = await this.api({
354 | method: 'GET',
355 | url: `/users/${this.userId}/favorites/albums?${this.params}`,
356 | });
357 |
358 | const { items } = res.data;
359 |
360 | const albums = items.map(item => item.item);
361 |
362 | return albums;
363 | }
364 |
365 | /**
366 | * get an artist by its id
367 | * @param {number} id - artist id
368 | * @example tidal.getArtist(3575680)
369 | * // returns a promise that resolves to:
370 | {
371 | "id": 3575680,
372 | "name": "Four Year Strong",
373 | "url": "http://www.tidal.com/artist/3575680",
374 | "picture": "04d63cd8-a1a5-42e0-b1ec-8e336b7d9200",
375 | "popularity": 28
376 | }
377 | * @returns {Promise}
378 | * @fulfil {Object} - an artist object (see example for object properties)
379 | * @reject {Error}
380 | */
381 | async getArtist(id) {
382 |
383 | const res = await this.api({
384 | method: 'GET',
385 | url: `/artists/${id}?${this.params}`,
386 | });
387 |
388 | return res.data;
389 | }
390 |
391 | /**
392 | * get artist albums by artist id
393 | * @param {number} id - artist id
394 | * @example tidal.getArtistAlbums(3575680)
395 | * @returns {Promise}
396 | * @fulfil {Array} - an array of album objects
397 | * @reject {Error}
398 | * @see {@link Tidal#getAlbum} - album object example
399 | */
400 | async getArtistAlbums(id) {
401 |
402 | const res = await this.api({
403 | method: 'GET',
404 | url: `/artists/${id}/albums?${this.params}`,
405 | });
406 |
407 | return res.data.items;
408 | }
409 |
410 | /**
411 | * get artist EPs and singles by artist id
412 | * @param {number} id - artist id
413 | * @example tidal.getArtistEPsAndSingles(3575680)
414 | * @returns {Promise}
415 | * @fulfil {Array} - an array of album objects
416 | * @reject {Error}
417 | * @see {@link Tidal#getAlbum} - album object example
418 | */
419 | async getArtistEPsAndSingles(id) {
420 |
421 | const res = await this.api({
422 | method: 'GET',
423 | url: `/artists/${id}/albums?${this.params}`,
424 | });
425 |
426 | return res.data.items;
427 | }
428 |
429 | /**
430 | * get compliations that artist has appeared on by artist id
431 | * @param {number} id - artist id
432 | * @example tidal.getArtistCompilations(3575680)
433 | * @returns {Promise}
434 | * @fulfil {Array} - an array of album objects
435 | * @reject {Error}
436 | * @see {@link Tidal#getAlbum} - album object example
437 | */
438 | async getArtistCompilations(id) {
439 |
440 | const res = await this.api({
441 | method: 'GET',
442 | url: `/artists/${id}/albums?${this.params}&filter=COMPILATIONS`,
443 | });
444 |
445 | return res.data.items;
446 | }
447 |
448 | /**
449 | * get top tracks by artist
450 | * @param {number} id - artist id
451 | * @param {number} [limit] - results limit
452 | * @example tidal.getArtistTopTracks(3575680)
453 | * @returns {Promise}
454 | * @fulfil {Array} - an array of track objects
455 | * @reject {Error}
456 | * @see {@link Tidal#getTrack} - track object example
457 | */
458 | async getArtistTopTracks(id, limit = 10) {
459 |
460 | const res = await this.api({
461 | method: 'GET',
462 | url: `/artists/${id}/toptracks?limit=${limit}&countryCode=${this.countryCode}`,
463 | });
464 |
465 | return res.data.items;
466 | }
467 |
468 | /**
469 | * get similar artists
470 | * @param {number} id - artist id
471 | * @example tidal.getSimilarArtists(3575680)
472 | * @returns {Promise}
473 | * @fulfil {Object} - artist object
474 | * @reject {Error}
475 | * @see {@link Tidal#getArtist} - artist object example
476 | */
477 | async getSimilarArtists(id) {
478 | const res = await this.api({
479 | method: 'GET',
480 | url: `/artists/${id}/similar?${this.params}`,
481 | });
482 |
483 | return res.data.items;
484 | }
485 |
486 | /**
487 | * get your favorite (starred) artists (requires login() to be called)
488 | * @example tidal.getFavoriteArtists()
489 | * @returns {Promise}
490 | * @fulfil {Array} - an array of artist objects
491 | * @reject {Error}
492 | * @see {@link Tidal#login} - login method must be called first
493 | * @see {@link Tidal#getArtist} - artist object example
494 | */
495 | async getFavoriteArtists() {
496 |
497 | if (!this.userId || !this.sessionId) {
498 | throw new Error('You must call the login method first');
499 | }
500 |
501 | const res = await this.api({
502 | method: 'GET',
503 | url: `/users/${this.userId}/favorites/artists?${this.params}`,
504 | });
505 |
506 | const { items } = res.data;
507 |
508 | const artists = items.map(item => item.item);
509 |
510 | return artists;
511 | }
512 |
513 | /**
514 | * get a playlist by its uuid
515 | * @param {string} uuid - playlist uuid
516 | * @example tidal.getPlaylist('1c5d01ed-4f05-40c4-bd28-0f73099e9648')
517 | * // returns a promise that resolves to:
518 | {
519 | "uuid": "1c5d01ed-4f05-40c4-bd28-0f73099e9648",
520 | "title": "Get Down On It: Soul, Funk and Disco Supremo",
521 | "numberOfTracks": 100,
522 | "numberOfVideos": 0,
523 | "creator": {
524 | "id": 0
525 | },
526 | "description": "Get down and dirty with some of the finest soul, funk \
527 | and four-to-the floor disco out there. Bound to get the blood pumping, \
528 | this playlist boasts more hits than a boxing match, more hooks than a \
529 | tackle box and marks the perfect prescription both for champagne days \
530 | and boogie nights and alike. Whether you feel like being a sex machine \
531 | or simply wish to dance to the music, rock your body, dig it and don't \
532 | stop 'til you get enough! ",
533 | "duration": 25732,
534 | "lastUpdated": "2017-01-18T16:31:51.839+0000",
535 | "created": "2016-09-22T16:42:40.911+0000",
536 | "type": "EDITORIAL",
537 | "publicPlaylist": true,
538 | "url": "http://www.tidal.com/playlist/1c5d01ed-4f05-40c4-bd28-0f73099e9648",
539 | "image": "7a707631-02cf-47d8-a34c-e1395165f169",
540 | "popularity": 0
541 | }
542 | * @returns {Promise}
543 | * @fulfil {Object} - playlist object (see example for object properties)
544 | * @reject {Error}
545 | */
546 | async getPlaylist(uuid) {
547 |
548 | const res = await this.api({
549 | method: 'GET',
550 | url: `/playlists/${uuid}?${this.params}`,
551 | });
552 |
553 | return res.data;
554 | }
555 |
556 | /**
557 | * get playlist tracks by playlist uuid
558 | * @param {string} uuid - playlist uuid
559 | * @example tidal.getPlaylistTracks('1c5d01ed-4f05-40c4-bd28-0f73099e9648')
560 | * @returns {Promise}
561 | * @fulfil {Array} - an array of track objects
562 | * @reject {Error}
563 | * @see {@link Tidal#getTrack} - track object example
564 | */
565 | async getPlaylistTracks(uuid) {
566 |
567 | const res = await this.api({
568 | method: 'GET',
569 | url: `/playlists/${uuid}/tracks?${this.params}`,
570 | });
571 |
572 | return res.data.items;
573 | }
574 |
575 | /**
576 | * get your favorite (starred) playlists (requires login() to be called)
577 | * @example tidal.getFavoritePlaylists()
578 | * @returns {Promise}
579 | * @fulfil {Array} - an array of playlist objects
580 | * @reject {Error}
581 | * @see {@link Tidal#login} - login method must be called first
582 | * @see {@link Tidal#getPlaylist} - playlist object example
583 | */
584 | async getFavoritePlaylists() {
585 |
586 | if (!this.userId || !this.sessionId) {
587 | throw new Error('You must call the login method first');
588 | }
589 |
590 | const res = await this.api({
591 | method: 'GET',
592 | url: `/users/${this.userId}/favorites/playlists?${this.params}`,
593 | });
594 |
595 | const { items } = res.data;
596 |
597 | const playlists = items.map(item => item.item);
598 |
599 | return playlists;
600 | }
601 |
602 | /**
603 | * get your created playlists (requires login() to be called)
604 | * @example tidal.getPlaylists()
605 | * @returns {Promise}
606 | * @fulfil {Array} - an array of playlist objects
607 | * @reject {Error}
608 | * @see {@link Tidal#login} - login method must be called first
609 | * @see {@link Tidal#getPlaylist} - playlist object example
610 | */
611 | async getPlaylists() {
612 |
613 | if (!this.userId || !this.sessionId) {
614 | throw new Error('You must call the login method first');
615 | }
616 |
617 | const res = await this.api({
618 | method: 'GET',
619 | url: `/users/${this.userId}/playlists?${this.params}`,
620 | });
621 |
622 | return res.data.items;
623 | }
624 |
625 | /**
626 | * get valid urls to artist pictures
627 | * @param {string} uuid - artist picture uuid (can be found as picture property in artist object)
628 | * @example tidal.artistPicToUrl('04d63cd8-a1a5-42e0-b1ec-8e336b7d9200')
629 | * // returns
630 | {
631 | sm: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/160x107.jpg',
632 | md: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/320x214.jpg',
633 | lg: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/640x428.jpg'
634 | }
635 | * @returns {Object}
636 | */
637 | artistPicToUrl(uuid) {
638 | const baseUrl = `https://resources.tidal.com/images/${uuid.replace(/-/g, '/')}`;
639 | return {
640 | sm: `${baseUrl}/160x107.jpg`,
641 | md: `${baseUrl}/320x214.jpg`,
642 | lg: `${baseUrl}/640x428.jpg`,
643 | };
644 | }
645 |
646 | /**
647 | * get valid urls to album art
648 | * @param {string} uuid - album art uuid (can be found as cover property in album object)
649 | * @example tidal.albumArtToUrl('9a56f482-e9cf-46c3-bb21-82710e7854d4')
650 | * // returns
651 | {
652 | sm: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/160x160.jpg',
653 | md: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/320x320.jpg',
654 | lg: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/640x640.jpg',
655 | xl: 'https://resources.tidal.com/images/9a56f482-e9cf-46c3-bb21-82710e7854d4/1280x1280.jpg'
656 | }
657 | * @returns {Object}
658 | */
659 | albumArtToUrl(uuid) {
660 | const baseUrl = `https://resources.tidal.com/images/${uuid.replace(/-/g, '/')}`;
661 | return {
662 | sm: `${baseUrl}/160x160.jpg`,
663 | md: `${baseUrl}/320x320.jpg`,
664 | lg: `${baseUrl}/640x640.jpg`,
665 | xl: `${baseUrl}/1280x1280.jpg`,
666 | };
667 | }
668 |
669 | }
670 |
671 | export default Tidal;
672 |
--------------------------------------------------------------------------------
/test/album.js:
--------------------------------------------------------------------------------
1 | import chai, { expect } from 'chai';
2 | import chaiAsPromised from 'chai-as-promised';
3 | import Tidal from '../src';
4 |
5 | chai.use(chaiAsPromised);
6 |
7 | const tidal = new Tidal();
8 |
9 | describe('album', () => {
10 |
11 | describe('getAlbum', () => {
12 |
13 | it('should return the correct album object', async () => {
14 | const album = await tidal.getAlbum(80216363);
15 |
16 | expect(album).to.be.an('object')
17 | .and.to.include({ id: 80216363 });
18 |
19 | });
20 |
21 | });
22 |
23 | describe('getAlbumTracks', () => {
24 |
25 | it('should return an array with the correct number of track objects', async () => {
26 | const tracks = await tidal.getAlbumTracks(80216363);
27 | expect(tracks).to.be.an('array')
28 | .and.to.have.lengthOf(10);
29 |
30 | expect(tracks[0]).to.have.property('trackNumber');
31 |
32 | });
33 |
34 | });
35 |
36 | describe('getTopAlbums', () => {
37 |
38 | it('should return an array of album objects', async () => {
39 |
40 | const albums = await tidal.getTopAlbums();
41 |
42 | expect(albums).to.be.an('array');
43 |
44 | expect(albums[0]).to.have.property('numberOfTracks');
45 |
46 | });
47 |
48 | });
49 |
50 | describe('getNewAlbums', () => {
51 |
52 | it('should return an array of album objects', async () => {
53 |
54 | const albums = await tidal.getNewAlbums();
55 |
56 | expect(albums).to.be.an('array');
57 |
58 | expect(albums[0]).to.have.property('numberOfTracks');
59 |
60 | });
61 |
62 | });
63 |
64 | describe('getStaffPickAlbums', () => {
65 |
66 | it('should return an array of album objects', async () => {
67 |
68 | const albums = await tidal.getStaffPickAlbums();
69 |
70 | expect(albums).to.be.an('array');
71 |
72 | expect(albums[0]).to.have.property('numberOfTracks');
73 |
74 | });
75 |
76 | });
77 |
78 | describe('getFavoriteAlbums', () => {
79 |
80 | it('should reject if login() has not been called', () => expect(tidal.getFavoriteAlbums()).to.eventually.be.rejectedWith(Error));
81 |
82 | it('should return an array of album object', async () => {
83 |
84 | await tidal.login(process.env.USERNAME, process.env.PASSWORD);
85 |
86 | const favorites = await tidal.getFavoriteAlbums();
87 | const album = favorites[0];
88 |
89 | expect(favorites).to.be.an('array');
90 |
91 | expect(album).to.be.an('object')
92 | .and.to.have.property('title');
93 |
94 | });
95 |
96 | });
97 |
98 | });
99 |
--------------------------------------------------------------------------------
/test/artist.js:
--------------------------------------------------------------------------------
1 | import chai, { expect } from 'chai';
2 | import chaiAsPromised from 'chai-as-promised';
3 | import Tidal from '../src';
4 |
5 | chai.use(chaiAsPromised);
6 |
7 | const tidal = new Tidal();
8 |
9 | describe('artist', () => {
10 |
11 | describe('getArtist', () => {
12 |
13 | it('should return the expected artist object', async () => {
14 | const artist = await tidal.getArtist(3575680);
15 |
16 | expect(artist).to.be.an('object')
17 | .and.to.include({ id: 3575680 });
18 |
19 | });
20 |
21 | });
22 |
23 | describe('getArtistAlbums', () => {
24 |
25 | it('should return an array of album objects', async () => {
26 | const albums = await tidal.getArtistAlbums(3575680);
27 |
28 | expect(albums).to.be.an('array');
29 | expect(albums[0]).to.be.an('object')
30 | .and.to.include({ type: 'ALBUM' });
31 |
32 | });
33 |
34 | });
35 |
36 | describe('getArtistEPsAndSingles', () => {
37 |
38 | it('should return an array of album objects', async () => {
39 | const albums = await tidal.getArtistEPsAndSingles(3575680);
40 |
41 | expect(albums).to.be.an('array')
42 | .and.to.have.lengthOf.above(0);
43 |
44 | expect(albums[0]).to.be.an('object');
45 |
46 | });
47 | });
48 |
49 | describe('getArtistCompilations', () => {
50 |
51 | it('should return an array of album objects', async () => {
52 | const albums = await tidal.getArtistCompilations(3575680);
53 |
54 | expect(albums).to.be.an('array')
55 | .and.to.have.lengthOf.above(0);
56 |
57 | expect(albums[0]).to.be.an('object');
58 |
59 | });
60 | });
61 |
62 | describe('getArtistTopTracks', () => {
63 |
64 | it('should return an array of 10 track objects', async () => {
65 | const tracks = await tidal.getArtistTopTracks(3575680);
66 |
67 | expect(tracks).to.be.an('array')
68 | .and.to.have.lengthOf(10);
69 |
70 | expect(tracks[0]).to.be.an('object')
71 | .and.to.have.property('trackNumber');
72 |
73 | });
74 |
75 | it('should return an array of 25 track objects', async () => {
76 | const tracks = await tidal.getArtistTopTracks(3575680, 25);
77 |
78 | expect(tracks).to.be.an('array')
79 | .and.to.have.lengthOf(25);
80 | });
81 | });
82 |
83 | describe('getSimilarArtists', () => {
84 |
85 | it('should return an array of artist objects', async () => {
86 |
87 | const similar = await tidal.getSimilarArtists(3575680);
88 | const artist = similar[0];
89 |
90 | expect(similar).to.be.an('array')
91 | .and.to.have.length.above(0);
92 |
93 | expect(artist).to.be.an('object')
94 | .and.to.have.property('id');
95 |
96 | });
97 |
98 | });
99 |
100 | describe('getFavoriteArtists', () => {
101 |
102 | it('should reject if login() has not been called', () => expect(tidal.getFavoriteArtists()).to.eventually.be.rejectedWith(Error));
103 |
104 | it('should return an array of artist objects', async () => {
105 |
106 | await tidal.login(process.env.USERNAME, process.env.PASSWORD);
107 |
108 | const favorites = await tidal.getFavoriteArtists();
109 | const artist = favorites[0];
110 |
111 | expect(favorites).to.be.an('array');
112 |
113 | expect(artist).to.be.an('object')
114 | .and.to.have.property('name');
115 |
116 | });
117 |
118 | });
119 |
120 | });
121 |
--------------------------------------------------------------------------------
/test/class.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import Tidal from '../src';
3 |
4 | describe('Tidal', () => {
5 |
6 | it('should be instantiated with default options', () => {
7 | const tidal = new Tidal();
8 |
9 | expect(tidal.countryCode).to.equal('US');
10 |
11 | expect(tidal.limit).to.equal(1000);
12 | });
13 |
14 | it('should be instantiated with custom options', () => {
15 |
16 | const tidal = new Tidal({
17 | countryCode: 'DK',
18 | limit: 500,
19 | });
20 |
21 | expect(tidal.countryCode).to.equal('DK');
22 |
23 | expect(tidal.limit).to.equal(500);
24 | });
25 |
26 | });
27 |
28 |
--------------------------------------------------------------------------------
/test/image.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import Tidal from '../src';
3 |
4 | const tidal = new Tidal();
5 |
6 | describe('images', () => {
7 |
8 | describe('artistPicToUrl', () => {
9 |
10 | it('should return an object with the correct sm, md, lg properties', () => {
11 |
12 | expect(tidal.artistPicToUrl('04d63cd8-a1a5-42e0-b1ec-8e336b7d9200'))
13 | .to.deep.equal({
14 | sm: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/160x107.jpg',
15 | md: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/320x214.jpg',
16 | lg: 'https://resources.tidal.com/images/04d63cd8/a1a5/42e0/b1ec/8e336b7d9200/640x428.jpg',
17 | });
18 |
19 | });
20 |
21 | });
22 |
23 | describe('albumArtToUrl', () => {
24 |
25 | it('should return an object with the correct sm, md, lg, xl properties', () => {
26 |
27 | expect(tidal.albumArtToUrl('9a56f482-e9cf-46c3-bb21-82710e7854d4'))
28 | .to.deep.equal({
29 | sm: 'https://resources.tidal.com/images/9a56f482/e9cf/46c3/bb21/82710e7854d4/160x160.jpg',
30 | md: 'https://resources.tidal.com/images/9a56f482/e9cf/46c3/bb21/82710e7854d4/320x320.jpg',
31 | lg: 'https://resources.tidal.com/images/9a56f482/e9cf/46c3/bb21/82710e7854d4/640x640.jpg',
32 | xl: 'https://resources.tidal.com/images/9a56f482/e9cf/46c3/bb21/82710e7854d4/1280x1280.jpg',
33 | });
34 |
35 | });
36 |
37 | });
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/test/login.js:
--------------------------------------------------------------------------------
1 | import chai, { expect } from 'chai';
2 | import chaiAsPromised from 'chai-as-promised';
3 | import dotenv from 'dotenv';
4 | import Tidal from '../src';
5 |
6 | chai.use(chaiAsPromised);
7 |
8 | dotenv.config();
9 |
10 | const tidal = new Tidal();
11 |
12 | describe('login', () => {
13 |
14 | it('should return user and session data', async () => {
15 |
16 | const data = await tidal.login(process.env.USERNAME, process.env.PASSWORD);
17 |
18 | expect(data).to.be.an('object');
19 |
20 | expect(data).to.have.property('sessionId');
21 |
22 | expect(data).to.have.property('userId');
23 |
24 | });
25 |
26 | it('should throw an error if username or password are not provided', () => expect(tidal.login()).to.eventually.be.rejectedWith(Error));
27 |
28 | });
29 |
30 |
--------------------------------------------------------------------------------
/test/playlist.js:
--------------------------------------------------------------------------------
1 | import chai, { expect } from 'chai';
2 | import chaiAsPromised from 'chai-as-promised';
3 | import Tidal from '../src';
4 |
5 | chai.use(chaiAsPromised);
6 |
7 | describe('playlist', () => {
8 | let tidal;
9 |
10 | beforeEach(() => {
11 | tidal = new Tidal();
12 | });
13 |
14 | describe('getPlaylist', () => {
15 |
16 | it('should return the correct playlist object', async () => {
17 | const playlist = await tidal.getPlaylist('1c5d01ed-4f05-40c4-bd28-0f73099e9648');
18 |
19 | expect(playlist).to.be.an('object')
20 | .and.to.include({ uuid: '1c5d01ed-4f05-40c4-bd28-0f73099e9648' });
21 |
22 | });
23 |
24 | });
25 |
26 | describe('getPlaylistTracks', () => {
27 |
28 | it('should return an array of track objects from the specified playlist', async () => {
29 | const tracks = await tidal.getPlaylistTracks('1c5d01ed-4f05-40c4-bd28-0f73099e9648');
30 | const track = tracks[0];
31 |
32 | expect(tracks).to.be.an('array');
33 |
34 | expect(track).to.be.an('object')
35 | .and.to.have.property('trackNumber');
36 | });
37 |
38 | });
39 |
40 | describe('getFavoritePlaylists', () => {
41 | it('should reject if login() has not been called', () => {
42 | expect(tidal.getFavoritePlaylists()).to.eventually.be.rejectedWith(Error);
43 | });
44 |
45 | it('should return an array of playlist objects', async () => {
46 | await tidal.login(process.env.USERNAME, process.env.PASSWORD);
47 | const favorites = await tidal.getFavoritePlaylists();
48 | const playlist = favorites[0];
49 |
50 | expect(favorites).to.be.an('array');
51 |
52 | expect(playlist).to.be.an('object')
53 | .and.to.have.property('uuid');
54 | });
55 |
56 | });
57 |
58 | describe('getPlaylists', () => {
59 | it('should reject if login() has not been called', () => expect(tidal.getPlaylists()).to.eventually.be.rejectedWith(Error));
60 |
61 | it('should return an array of playlist objects', async () => {
62 | await tidal.login(process.env.USERNAME, process.env.PASSWORD);
63 | const playlists = await tidal.getPlaylists();
64 | const playlist = playlists[0];
65 |
66 | expect(playlists).to.be.an('array')
67 | .and.to.have.lengthOf(1);
68 |
69 | expect(playlist).to.be.an('object')
70 | .and.to.have.property('uuid');
71 | });
72 |
73 | });
74 |
75 | });
76 |
--------------------------------------------------------------------------------
/test/search.js:
--------------------------------------------------------------------------------
1 | import chai, { expect } from 'chai';
2 | import chaiAsPromised from 'chai-as-promised';
3 | import Tidal from '../src';
4 |
5 | chai.use(chaiAsPromised);
6 |
7 | const tidal = new Tidal();
8 |
9 | describe('search', () => {
10 |
11 | it('should return an array of 25 track objects', async () => {
12 | const tracks = await tidal.search('adele', 'tracks', 25);
13 | expect(tracks).to.be.an('array')
14 | .and.to.have.lengthOf(25);
15 |
16 | expect(tracks[0]).to.be.an('object')
17 | .and.to.have.property('trackNumber');
18 | });
19 |
20 | it('should throw an error when type is undefined', () =>
21 | expect(tidal.search('adele')).to.be.rejectedWith(Error));
22 |
23 | it('should throw an error when type is not valid', () =>
24 | expect(tidal.search('adele', 'wrong')).to.be.rejectedWith(Error));
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/test/track.js:
--------------------------------------------------------------------------------
1 | import chai, { expect } from 'chai';
2 | import chaiAsPromised from 'chai-as-promised';
3 | import Tidal from '../src';
4 |
5 | chai.use(chaiAsPromised);
6 |
7 | const tidal = new Tidal();
8 |
9 | describe('track', () => {
10 |
11 | describe('getTrack', () => {
12 |
13 | it('should return the correct track object', async () => {
14 | const track = await tidal.getTrack(64975224);
15 |
16 | expect(track).to.be.an('object')
17 | .and.to.include({ id: 64975224 });
18 |
19 | });
20 |
21 | });
22 |
23 | describe('getFavoriteTracks', () => {
24 |
25 | it('should reject if login() has not been called', () => expect(tidal.getFavoriteTracks()).to.eventually.be.rejectedWith(Error));
26 |
27 | it('should return an array of track objects', async () => {
28 |
29 | await tidal.login(process.env.USERNAME, process.env.PASSWORD);
30 | const favorites = await tidal.getFavoriteTracks();
31 | const track = favorites[0];
32 |
33 | expect(favorites).to.be.an('array');
34 |
35 | expect(track).to.be.an('object');
36 | expect(track).to.have.property('title');
37 | expect(track).to.have.property('album');
38 |
39 |
40 | });
41 |
42 | });
43 |
44 | });
45 |
--------------------------------------------------------------------------------