@lumiastream/spotify-cove
Version:
A Node.js wrapper for Spotify's Web API
1,229 lines (1,136 loc) • 76 kB
JavaScript
'use strict';
var AuthenticationRequest = require('./authentication-request'),
WebApiRequest = require('./webapi-request'),
HttpManager = require('./http-manager');
function SpotifyWebApi(credentials) {
this._credentials = credentials || {};
}
function _executePromiseOrCallback(promise, callback) {
if (callback) {
promise.then(
function (data) {
callback(null, data);
},
function (err) {
callback(err);
}
);
return;
}
return promise;
}
function _buildRemovedEndpointError(endpoint, replacementHint) {
var message =
endpoint +
' is unavailable in Spotify Development Mode as of February 9, 2026.' +
(replacementHint ? ' ' + replacementHint : '');
var err = new Error(message);
err.status = 410;
err.statusCode = 410;
return err;
}
function _returnRemovedEndpoint(endpoint, replacementHint, callback) {
return _executePromiseOrCallback(
Promise.reject(_buildRemovedEndpointError(endpoint, replacementHint)),
callback
);
}
function _toSpotifyUri(type, idOrUri) {
if (typeof idOrUri !== 'string') {
return idOrUri;
}
if (idOrUri.indexOf('spotify:') === 0) {
return idOrUri;
}
return 'spotify:' + type + ':' + idOrUri;
}
function _toUriList(type, idsOrUris) {
return (idsOrUris || []).map(function (idOrUri) {
return _toSpotifyUri(type, idOrUri);
});
}
function _toCommaSeparatedUris(type, idsOrUris) {
return _toUriList(type, idsOrUris).join(',');
}
function _collectBatchById(api, ids, key, fetchById, callback) {
var list = ids || [];
var promise = Promise.all(
list.map(function (id) {
return fetchById.call(api, id);
})
).then(function (results) {
return {
body: {
[key]: results.map(function (result) {
return result.body;
})
},
headers: results[0] ? results[0].headers : undefined,
statusCode: results[0] ? results[0].statusCode : 200
};
});
return _executePromiseOrCallback(promise, callback);
}
function _normalizeOptionsAndCallback(options, callback) {
if (typeof options === 'function') {
return { options: undefined, callback: options };
}
return { options: options, callback: callback };
}
SpotifyWebApi.prototype = {
setCredentials: function (credentials) {
for (var key in credentials) {
if (credentials.hasOwnProperty(key)) {
this._credentials[key] = credentials[key];
}
}
},
getCredentials: function () {
return this._credentials;
},
resetCredentials: function () {
this._credentials = null;
},
setClientId: function (clientId) {
this._setCredential('clientId', clientId);
},
setClientSecret: function (clientSecret) {
this._setCredential('clientSecret', clientSecret);
},
setAccessToken: function (accessToken) {
this._setCredential('accessToken', accessToken);
},
setRefreshToken: function (refreshToken) {
this._setCredential('refreshToken', refreshToken);
},
setRedirectURI: function (redirectUri) {
this._setCredential('redirectUri', redirectUri);
},
getRedirectURI: function () {
return this._getCredential('redirectUri');
},
getClientId: function () {
return this._getCredential('clientId');
},
getClientSecret: function () {
return this._getCredential('clientSecret');
},
getAccessToken: function () {
return this._getCredential('accessToken');
},
getRefreshToken: function () {
return this._getCredential('refreshToken');
},
resetClientId: function () {
this._resetCredential('clientId');
},
resetClientSecret: function () {
this._resetCredential('clientSecret');
},
resetAccessToken: function () {
this._resetCredential('accessToken');
},
resetRefreshToken: function () {
this._resetCredential('refreshToken');
},
resetRedirectURI: function () {
this._resetCredential('redirectUri');
},
_setCredential: function (credentialKey, value) {
this._credentials = this._credentials || {};
this._credentials[credentialKey] = value;
},
_getCredential: function (credentialKey) {
if (!this._credentials) {
return;
} else {
return this._credentials[credentialKey];
}
},
_resetCredential: function (credentialKey) {
if (!this._credentials) {
return;
} else {
this._credentials[credentialKey] = null;
}
},
/**
* Look up a track.
* @param {string} trackId The track's ID.
* @param {Object} [options] The possible options, currently only market.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getTrack('3Qm86XLflmIXVm1wcwkgDK').then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing information
* about the track. Not returned if a callback is given.
*/
getTrack: function (trackId, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/tracks/' + trackId)
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Look up several tracks.
* @param {string[]} trackIds The IDs of the artists.
* @param {Object} [options] The possible options, currently only market.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getArtists(['0oSGxfWSnnOXhD2fKuz2Gy', '3dBVyJ7JuOMt4GE9607Qin']).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing information
* about the artists. Not returned if a callback is given.
*/
getTracks: function (trackIds, options, callback) {
var normalized = _normalizeOptionsAndCallback(options, callback);
return _collectBatchById(
this,
trackIds,
'tracks',
function (trackId) {
return this.getTrack(trackId, normalized.options);
},
normalized.callback
);
},
/**
* Look up an album.
* @param {string} albumId The album's ID.
* @param {Object} [options] The possible options, currently only market.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getAlbum('0sNOF9WDwhWunNAHPD3Baj').then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing information
* about the album. Not returned if a callback is given.
*/
getAlbum: function (albumId, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/albums/' + albumId)
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Look up several albums.
* @param {string[]} albumIds The IDs of the albums.
* @param {Object} [options] The possible options, currently only market.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getAlbums(['0oSGxfWSnnOXhD2fKuz2Gy', '3dBVyJ7JuOMt4GE9607Qin']).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing information
* about the albums. Not returned if a callback is given.
*/
getAlbums: function (albumIds, options, callback) {
var normalized = _normalizeOptionsAndCallback(options, callback);
return _collectBatchById(
this,
albumIds,
'albums',
function (albumId) {
return this.getAlbum(albumId, normalized.options);
},
normalized.callback
);
},
/**
* Look up an artist.
* @param {string} artistId The artist's ID.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example api.getArtist('1u7kkVrr14iBvrpYnZILJR').then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing information
* about the artist. Not returned if a callback is given.
*/
getArtist: function (artistId, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/artists/' + artistId)
.build()
.execute(HttpManager.get, callback);
},
/**
* Look up several artists.
* @param {string[]} artistIds The IDs of the artists.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getArtists(['0oSGxfWSnnOXhD2fKuz2Gy', '3dBVyJ7JuOMt4GE9607Qin']).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing information
* about the artists. Not returned if a callback is given.
*/
getArtists: function (artistIds, callback) {
return _collectBatchById(
this,
artistIds,
'artists',
function (artistId) {
return this.getArtist(artistId);
},
callback
);
},
/**
* Get the User’s Queue.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into a paging object of tracks,
* otherwise an error. Not returned if a callback is given.
*/
getMyQueue: function( callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player/queue')
.build()
.execute(HttpManager.get, callback);
},
getPlaylistCoverImage: function (playlistId, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath("/v1/playlists/" + playlistId + "/images")
.withHeaders({ "Content-Type": 'application/json' })
.build()
.execute(HttpManager.get, callback);
},
addToPlaybackQueue: function (uri, callback) {
var baseUrl = "/v1/me"
var path = "/player/queue"
return WebApiRequest.builder(this.getAccessToken())
.withPath(baseUrl + path)
.withQueryParameters({ uri: uri })
.build()
.execute(HttpManager.post, callback);
},
getAvailableMarkets: function (callback) {
return _returnRemovedEndpoint(
'/v1/markets',
'There is no Development Mode replacement for this endpoint.',
callback
);
},
/**
* Search for music entities of certain types.
* @param {string} query The search query.
* @param {string[]} types An array of item types to search across.
* Valid types are: 'album', 'artist', 'playlist', 'track', 'show', and 'episode'.
* @param {Object} [options] The possible options, e.g. limit, offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example search('Abba', ['track', 'playlist'], { limit : 5, offset : 1 }).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* search results. The result is paginated. If the promise is rejected,
* it contains an error object. Not returned if a callback is given.
*/
search: function (query, types, options, callback) {
var normalized = _normalizeOptionsAndCallback(options, callback);
var _options = normalized.options
? Object.assign({}, normalized.options)
: undefined;
if (_options && _options.limit !== undefined) {
var limit = parseInt(_options.limit, 10);
if (!Number.isNaN(limit)) {
_options.limit = Math.max(0, Math.min(10, limit));
}
}
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/search/')
.withQueryParameters(
{
type: types.join(','),
q: query
},
_options
)
.build()
.execute(HttpManager.get, normalized.callback);
},
/**
* Search for an album.
* @param {string} query The search query.
* @param {Object} [options] The possible options, e.g. limit, offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example searchAlbums('Space Oddity', { limit : 5, offset : 1 }).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* search results. The result is paginated. If the promise is rejected,
* it contains an error object. Not returned if a callback is given.
*/
searchAlbums: function (query, options, callback) {
return this.search(query, ['album'], options, callback);
},
/**
* Search for an artist.
* @param {string} query The search query.
* @param {Object} [options] The possible options, e.g. limit, offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example searchArtists('David Bowie', { limit : 5, offset : 1 }).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* search results. The result is paginated. If the promise is rejected,
* it contains an error object. Not returned if a callback is given.
*/
searchArtists: function (query, options, callback) {
return this.search(query, ['artist'], options, callback);
},
/**
* Search for a track.
* @param {string} query The search query.
* @param {Object} [options] The possible options, e.g. limit, offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example searchTracks('Mr. Brightside', { limit : 3, offset : 2 }).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* search results. The result is paginated. If the promise is rejected,
* it contains an error object. Not returned if a callback is given.
*/
searchTracks: function (query, options, callback) {
return this.search(query, ['track'], options, callback);
},
/**
* Search for playlists.
* @param {string} query The search query.
* @param {Object} options The possible options.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example searchPlaylists('workout', { limit : 1, offset : 0 }).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* search results. The result is paginated. If the promise is rejected,
* it contains an error object. Not returned if a callback is given.
*/
searchPlaylists: function (query, options, callback) {
return this.search(query, ['playlist'], options, callback);
},
/**
* Get an artist's albums.
* @param {string} artistId The artist's ID.
* @options {Object} [options] The possible options, e.g. limit, offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getArtistAlbums('0oSGxfWSnnOXhD2fKuz2Gy', { album_type : 'album', country : 'GB', limit : 2, offset : 5 }).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the albums
* for the given artist. The result is paginated. If the promise is rejected,
* it contains an error object. Not returned if a callback is given.
*/
getArtistAlbums: function (artistId, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/artists/' + artistId + '/albums')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get the tracks of an album.
* @param albumId the album's ID.
* @options {Object} [options] The possible options, e.g. limit.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getAlbumTracks('41MnTivkwTO3UUJ8DrqEJJ', { limit : 5, offset : 1 }).then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* tracks in the album. The result is paginated. If the promise is rejected.
* it contains an error object. Not returned if a callback is given.
*/
getAlbumTracks: function (albumId, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/albums/' + albumId + '/tracks')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get an artist's top tracks.
* @param {string} artistId The artist's ID.
* @param {string} country The country/territory where the tracks are most popular. (format: ISO 3166-1 alpha-2)
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getArtistTopTracks('0oSGxfWSnnOXhD2fKuz2Gy', 'GB').then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* artist's top tracks in the given country. If the promise is rejected,
* it contains an error object. Not returned if a callback is given.
*/
getArtistTopTracks: function (artistId, country, callback) {
return _returnRemovedEndpoint(
'/v1/artists/{id}/top-tracks',
'There is no Development Mode replacement for this endpoint.',
callback
);
},
/**
* Get related artists.
* @param {string} artistId The artist's ID.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getArtistRelatedArtists('0oSGxfWSnnOXhD2fKuz2Gy').then(...)
* @returns {Promise|undefined} A promise that if successful, returns an object containing the
* related artists. If the promise is rejected, it contains an error object. Not returned if a callback is given.
*/
getArtistRelatedArtists: function (artistId, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/artists/' + artistId + '/related-artists')
.build()
.execute(HttpManager.get, callback);
},
/**
* Get information about a user.
* @param userId The user ID.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getUser('lumiastream').then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object
* containing information about the user. If the promise is
* rejected, it contains an error object. Not returned if a callback is given.
*/
getUser: function (userId, callback) {
if (userId === 'me') {
return this.getMe(callback);
}
return _returnRemovedEndpoint(
'/v1/users/{id}',
'Use getMe() for the current user profile.',
callback
);
},
/**
* Get information about the user that has signed in (the current user).
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getMe().then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object
* containing information about the user. The amount of information
* depends on the permissions given by the user. If the promise is
* rejected, it contains an error object. Not returned if a callback is given.
*/
getMe: function (callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me')
.build()
.execute(HttpManager.get, callback);
},
/**
* Get a user's playlists.
* @param {string} userId An optional id of the user. If you know the Spotify URI it is easy
* to find the id (e.g. spotify:user:<here_is_the_id>). If not provided, the id of the user that granted
* the permissions will be used.
* @param {Object} [options] The options supplied to this request.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getUserPlaylists('lumiastream').then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object containing
* a list of playlists. If rejected, it contains an error object. Not returned if a callback is given.
*/
getUserPlaylists: function (userId, options, callback) {
var path = '/v1/me/playlists';
if (typeof userId === 'function') {
callback = userId;
userId = undefined;
options = undefined;
} else if (typeof userId === 'string') {
if (userId !== 'me') {
return _returnRemovedEndpoint(
'/v1/users/{id}/playlists',
'Use getUserPlaylists(undefined, options) for the current user.',
callback
);
}
} else if (typeof userId === 'object') {
callback = options;
options = userId;
userId = undefined;
} else if (typeof options === 'function') {
callback = options;
options = undefined;
}
return WebApiRequest.builder(this.getAccessToken())
.withPath(path)
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get a playlist.
* @param {string} playlistId The playlist's ID.
* @param {Object} [options] The options supplied to this request.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getPlaylist('3EsfV6XzCHU8SPNdbnFogK').then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object containing
* the playlist. If rejected, it contains an error object. Not returned if a callback is given.
*/
getPlaylist: function (playlistId, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId)
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get playlist items.
* @param {string} playlistId The playlist's ID.
* @param {Object} [options] Optional options, such as fields.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getPlaylistTracks('3ktAYNcRHpazJ9qecm3ptn').then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object that containing
* the tracks in the playlist. If rejected, it contains an error object. Not returned if a callback is given.
*/
getPlaylistTracks: function (playlistId, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/items')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Create a playlist.
* @param {string} [name] The name of the playlist.
* @param {Object} [options] The possible options, being description, collaborative and public.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example createPlaylist('My playlist', {''description': 'My description', 'collaborative' : false, 'public': true}).then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object containing information about the
* created playlist. If rejected, it contains an error object. Not returned if a callback is given.
*/
createPlaylist: function (name, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/playlists')
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters(
{
name: name
},
options
)
.build()
.execute(HttpManager.post, callback);
},
/**
* Follow a playlist.
* @param {string} playlistId The playlist's ID
* @param {Object} [options] The possible options, currently only public.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, simply resolves to an empty object. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
followPlaylist: function (playlistId, options, callback) {
var normalized = _normalizeOptionsAndCallback(options, callback);
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library')
.withQueryParameters({
uris: _toSpotifyUri('playlist', playlistId)
})
.build()
.execute(HttpManager.put, normalized.callback);
},
/**
* Unfollow a playlist.
* @param {string} playlistId The playlist's ID
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, simply resolves to an empty object. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
unfollowPlaylist: function (playlistId, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library')
.withQueryParameters({
uris: _toSpotifyUri('playlist', playlistId)
})
.build()
.execute(HttpManager.del, callback);
},
/**
* Change playlist details.
* @param {string} playlistId The playlist's ID
* @param {Object} [options] The possible options, e.g. name, public.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example changePlaylistDetails('3EsfV6XzCHU8SPNdbnFogK', {name: 'New name', public: true}).then(...)
* @returns {Promise|undefined} A promise that if successful, simply resolves to an empty object. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
changePlaylistDetails: function (playlistId, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId)
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters(options)
.build()
.execute(HttpManager.put, callback);
},
/**
* Replace the image used to represent a specific playlist.
* @param {string} playlistId The playlist's ID
* @param {string} base64URI Base64 encoded JPEG image data, maximum payload size is 256 KB
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example uploadCustomPlaylistCoverImage('3EsfV6XzCHU8SPNdbnFogK', 'longbase64uri').then(...)
* @returns {Promise|undefined} A promise that if successful, simply resolves to an empty object. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
uploadCustomPlaylistCoverImage: function (playlistId, base64URI, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/images')
.withHeaders({ 'Content-Type': 'image/jpeg' })
.withBodyParameters(base64URI)
.build()
.execute(HttpManager.put, callback);
},
/**
* Add tracks to a playlist.
* @param {string} playlistId The playlist's ID
* @param {string[]} tracks URIs of the tracks to add to the playlist.
* @param {Object} [options] Options, position being the only one.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example addTracksToPlaylist('3EsfV6XzCHU8SPNdbnFogK',
'["spotify:track:4iV5W9uYEdYUVa79Axb7Rh", "spotify:track:1301WleyT98MSxVHPZCA6M"]').then(...)
* @returns {Promise|undefined} A promise that if successful returns an object containing a snapshot_id. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
addTracksToPlaylist: function (playlistId, tracks, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/items')
.withHeaders({ 'Content-Type': 'application/json' })
.withQueryParameters(options)
.withBodyParameters({
uris: tracks
})
.build()
.execute(HttpManager.post, callback);
},
/**
* Remove tracks from a playlist.
* @param {string} playlistId The playlist's ID
* @param {Object[]} tracks An array of objects containing a property called uri with the track URI (String), and
* an optional property called positions (int[]), e.g. { uri : "spotify:track:491rM2JN8KvmV6p0oDDuJT", positions : [0, 15] }
* @param {Object} options Options, snapshot_id being the only one.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns an object containing a snapshot_id. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
removeTracksFromPlaylist: function (playlistId, tracks, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/items')
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters(
{
items: tracks
},
options
)
.build()
.execute(HttpManager.del, callback);
},
/**
* Remove tracks from a playlist by position instead of specifying the tracks' URIs.
* @param {string} playlistId The playlist's ID
* @param {int[]} positions The positions of the tracks in the playlist that should be removed
* @param {string} snapshot_id The snapshot ID, or version, of the playlist. Required
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns an object containing a snapshot_id. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
removeTracksFromPlaylistByPosition: function (
playlistId,
positions,
snapshotId,
callback
) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/items')
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters({
positions: positions,
snapshot_id: snapshotId
})
.build()
.execute(HttpManager.del, callback);
},
/**
* Replace tracks in a playlist.
* @param {string} playlistId The playlist's ID
* @param {Object[]} uris An array of track URIs (strings)
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns an empty object. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
replaceTracksInPlaylist: function (playlistId, uris, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/items')
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters({
uris: uris
})
.build()
.execute(HttpManager.put, callback);
},
/**
* Reorder tracks in a playlist.
* @param {string} playlistId The playlist's ID
* @param {int} rangeStart The position of the first track to be reordered.
* @param {int} insertBefore The position where the tracks should be inserted.
* @param {Object} options Optional parameters, i.e. range_length and snapshot_id.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns an object containing a snapshot_id. If rejected,
* it contains an error object. Not returned if a callback is given.
*/
reorderTracksInPlaylist: function (
playlistId,
rangeStart,
insertBefore,
options,
callback
) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/playlists/' + playlistId + '/items')
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters(
{
range_start: rangeStart,
insert_before: insertBefore
},
options
)
.build()
.execute(HttpManager.put, callback);
},
/**
* Get audio features for a single track identified by its unique Spotify ID.
* @param {string} trackId The track ID
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getAudioFeaturesForTrack('38P3Q4QcdjQALGF2Z92BmR').then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object
* containing information about the audio features. If the promise is
* rejected, it contains an error object. Not returned if a callback is given.
*/
getAudioFeaturesForTrack: function (trackId, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/audio-features/' + trackId)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get audio analysis for a single track identified by its unique Spotify ID.
* @param {string} trackId The track ID
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getAudioAnalysisForTrack('38P3Q4QcdjQALGF2Z92BmR').then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object
* containing information about the audio analysis. If the promise is
* rejected, it contains an error object. Not returned if a callback is given.
*/
getAudioAnalysisForTrack: function (trackId, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/audio-analysis/' + trackId)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get audio features for multiple tracks identified by their unique Spotify ID.
* @param {string[]} trackIds The track IDs
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getAudioFeaturesForTracks(['38P3Q4QcdjQALGF2Z92BmR', '2HO2bnoMrpnZUbUqiilLHi']).then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object
* containing information about the audio features for the tracks. If the promise is
* rejected, it contains an error object. Not returned if a callback is given.
*/
getAudioFeaturesForTracks: function (trackIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/audio-features')
.withQueryParameters({
ids: trackIds.join(',')
})
.build()
.execute(HttpManager.get, callback);
},
/**
* Create a playlist-style listening experience based on seed artists, tracks and genres.
* @param {Object} [options] The options supplied to this request.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getRecommendations({ min_energy: 0.4, seed_artists: ['6mfK6Q2tzLMEchAr0e9Uzu', '4DYFVNKZ1uixa6SQTvzQwJ'], min_popularity: 50 }).then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object containing
* a list of tracks and a list of seeds. If rejected, it contains an error object. Not returned if a callback is given.
*/
getRecommendations: function (options, callback) {
var _opts = {};
var optionsOfTypeArray = ['seed_artists', 'seed_genres', 'seed_tracks'];
for (var option in options) {
if (options.hasOwnProperty(option)) {
if (
optionsOfTypeArray.indexOf(option) !== -1 &&
Object.prototype.toString.call(options[option]) === '[object Array]'
) {
_opts[option] = options[option].join(',');
} else {
_opts[option] = options[option];
}
}
}
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/recommendations')
.withQueryParameters(_opts)
.build()
.execute(HttpManager.get, callback);
},
/**
* Retrieve a list of available genres seed parameter values for recommendations.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example getAvailableGenreSeeds().then(...)
* @returns {Promise|undefined} A promise that if successful, resolves to an object containing
* a list of available genres to be used as seeds for recommendations.
* If rejected, it contains an error object. Not returned if a callback is given.
*/
getAvailableGenreSeeds: function (callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/recommendations/available-genre-seeds')
.build()
.execute(HttpManager.get, callback);
},
/**
* Retrieve the tracks that are saved to the authenticated users Your Music library.
* @param {Object} [options] Options, being market, limit, and/or offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves to an object containing a paging object which in turn contains
* playlist track objects. Not returned if a callback is given.
*/
getMySavedTracks: function (options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/tracks')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Check if one or more tracks is already saved in the current Spotify user’s “Your Music” library.
* @param {string[]} trackIds The track IDs
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into an array of booleans. The order
* of the returned array's elements correspond to the track ID in the request.
* The boolean value of true indicates that the track is part of the user's library, otherwise false.
* Not returned if a callback is given.
*/
containsMySavedTracks: function (trackIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library/contains')
.withQueryParameters({
uris: _toCommaSeparatedUris('track', trackIds)
})
.build()
.execute(HttpManager.get, callback);
},
/**
* Remove a track from the authenticated user's Your Music library.
* @param {string[]} trackIds The track IDs
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns null, otherwise an error.
* Not returned if a callback is given.
*/
removeFromMySavedTracks: function (trackIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library')
.withQueryParameters({
uris: _toCommaSeparatedUris('track', trackIds)
})
.build()
.execute(HttpManager.del, callback);
},
/**
* Add a track from the authenticated user's Your Music library.
* @param {string[]} trackIds The track IDs
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns null, otherwise an error. Not returned if a callback is given.
*/
addToMySavedTracks: function (trackIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library')
.withQueryParameters({
uris: _toCommaSeparatedUris('track', trackIds)
})
.build()
.execute(HttpManager.put, callback);
},
/**
* Remove an album from the authenticated user's Your Music library.
* @param {string[]} albumIds The album IDs
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns null, otherwise an error.
* Not returned if a callback is given.
*/
removeFromMySavedAlbums: function (albumIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library')
.withQueryParameters({
uris: _toCommaSeparatedUris('album', albumIds)
})
.build()
.execute(HttpManager.del, callback);
},
/**
* Add an album from the authenticated user's Your Music library.
* @param {string[]} albumIds The track IDs
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful returns null, otherwise an error. Not returned if a callback is given.
*/
addToMySavedAlbums: function (albumIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library')
.withQueryParameters({
uris: _toCommaSeparatedUris('album', albumIds)
})
.build()
.execute(HttpManager.put, callback);
},
/**
* Retrieve the albums that are saved to the authenticated users Your Music library.
* @param {Object} [options] Options, being market, limit, and/or offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves to an object containing a paging object which in turn contains
* playlist album objects. Not returned if a callback is given.
*/
getMySavedAlbums: function (options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/albums')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Check if one or more albums is already saved in the current Spotify user’s “Your Music” library.
* @param {string[]} albumIds The album IDs
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into an array of booleans. The order
* of the returned array's elements correspond to the album ID in the request.
* The boolean value of true indicates that the album is part of the user's library, otherwise false.
* Not returned if a callback is given.
*/
containsMySavedAlbums: function (albumIds, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/library/contains')
.withQueryParameters({
uris: _toCommaSeparatedUris('album', albumIds)
})
.build()
.execute(HttpManager.get, callback);
},
/**
* Get the current user's top artists based on calculated affinity.
* @param {Object} [options] Options, being time_range, limit, offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into a paging object of artists,
* otherwise an error. Not returned if a callback is given.
*/
getMyTopArtists: function (options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/top/artists')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get the current user's top tracks based on calculated affinity.
* @param {Object} [options] Options, being time_range, limit, offset.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into a paging object of tracks,
* otherwise an error. Not returned if a callback is given.
*/
getMyTopTracks: function (options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/top/tracks')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get the Current User's Recently Played Tracks
* @param {Object} [options] Options, being type, after, limit, before.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into a paging object of play history objects,
* otherwise an error. Not returned if a callback is given. Note that the response will be empty
* in case the user has enabled private session.
*/
getMyRecentlyPlayedTracks: function (options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player/recently-played')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Add track or episode to device queue
* @param {string} [uri] uri of the track or episode to add
* @param {Object} [options] Options, being device_id.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into a paging object of tracks,
* otherwise an error. Not returned if a callback is given.
*/
addToQueue: function (uri, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player/queue')
.withQueryParameters(
{
uri: uri
},
options
)
.build()
.execute(HttpManager.post, callback);
},
/**
* Get the Current User's Available Devices
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into an array of device objects,
* otherwise an error. Not returned if a callback is given.
*/
getMyDevices: function (callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player/devices')
.build()
.execute(HttpManager.get, callback);
},
/**
* Get the Current User's Currently Playing Track.
* @param {Object} [options] Options, being market.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into a paging object of tracks,
* otherwise an error. Not returned if a callback is given.
*/
getMyCurrentPlayingTrack: function (options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player/currently-playing')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Get Information About The User's Current Playback State
* @param {Object} [options] Options, being market and additional_types.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into a paging object of tracks,
* otherwise an error. Not returned if a callback is given.
*/
getMyCurrentPlaybackState: function (options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player')
.withQueryParameters(options)
.build()
.execute(HttpManager.get, callback);
},
/**
* Transfer a User's Playback
* @param {string[]} [deviceIds] An _array_ containing a device ID on which playback should be started/transferred.
* (NOTE: The API is currently only supporting a single device ID.)
* @param {Object} [options] Options, the only one being 'play'.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @returns {Promise|undefined} A promise that if successful, resolves into an empty response,
* otherwise an error. Not returned if a callback is given.
*/
transferMyPlayback: function (deviceIds, options, callback) {
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player')
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters(
{
device_ids: deviceIds
},
options
)
.build()
.execute(HttpManager.put, callback);
},
/**
* Starts o Resumes the Current User's Playback
* @param {Object} [options] Options, being device_id, context_uri, offset, uris, position_ms.
* @param {requestCallback} [callback] Optional callback method to be called instead of the promise.
* @example play({context_uri: 'spotify:album:5ht7ItJgpBH7W6vJ5BqpPr'}).then(...)
* @returns {Promise|undefined} A promise that if successful, resolves into an empty response,
* otherwise an error. Not returned if a callback is given.
*/
play: function (options, callback) {
/*jshint camelcase: false */
var _options = options || {};
var queryParams = _options.device_id
? { device_id: _options.device_id }
: null;
var postData = {};
['context_uri', 'uris', 'offset', 'position_ms'].forEach(function (field) {
if (field in _options) {
postData[field] = _options[field];
}
});
return WebApiRequest.builder(this.getAccessToken())
.withPath('/v1/me/player/play')
.withQueryParameters(queryParams)
.withHeaders({ 'Content-Type': 'application/json' })
.withBodyParameters(postData)
.build()
.execute(HttpManager.put, callback);
},
/**
* Pauses the Current User's Playback
* @param {Object} [options] Options, being device_id. If left empty will target the u