groove-music-js
Version:
Groove Music Node.JS SDK wrapper
523 lines (444 loc) • 18.1 kB
JavaScript
'use strict';
var AuthenticationRequest = require('./authentication-request'),
WebApiRequest = require('./web-api-request'),
HttpManager = require('./http-wrapper');
function GrooveMusicWebApi(credentials) {
this._credentials = credentials || {};
}
GrooveMusicWebApi.prototype = {
_addBodyParameters: function (request, options) {
if (options) {
for (var key in options) {
if (key !== 'credentials') {
request.addBodyParameter(key, options[key]);
}
}
}
},
_addQueryParameters: function (request, options) {
if (!options) {
return;
}
for (var key in options) {
if (key !== 'credentials') {
request.addQueryParameter(key, options[key]);
}
}
},
_performRequest: function (method, request) {
var promiseFunction = function (resolve, reject) {
method(request, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
};
return new Promise(promiseFunction);
},
_addAccessToken: function (request, accessToken) {
if (accessToken) {
request.addHeaders({
'Authorization': 'Bearer ' + accessToken
});
}
},
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;
}
},
/**
* Attempts to login with the client ID and client secret
*/
authenticateApp: function (callback) {
var request = AuthenticationRequest.builder()
.withBodyParameters({
client_id: this.getClientId(),
"client_secret": this.getClientSecret(),
"scope": "app.music.xboxlive.com",
"grant_type": "client_credentials"
})
.withHeaders({
"Content-Type": "application/x-www-form-urlencoded"
})
.build();
var promise = this._performRequest(HttpManager.post, request);
var self = this;
promise.then(function (data) {
if (data.body["token_type"] == "bearer") {
self.setAccessToken(data.body.access_token);
}
callback(null, null);
}, function (err) {
callback(err);
});
},
/**
* 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) {
// In case someone is using a version where options parameter did not exist.
var actualCallback;
if (typeof options === 'function') {
actualCallback = options; _addAccessToken
} else {
actualCallback = callback;
}
var actualOptions = {};
if (typeof options === 'object') {
Object.keys(options).forEach(function (key) {
actualOptions[key] = options[key];
});
}
var request = WebApiRequest.builder()
.withPath('/1/content/' + trackId + "/lookup")
.withQueryParameters(actualOptions)
.build();
this._addAccessToken(request, this.getAccessToken());
var promise = this._performRequest(HttpManager.get, request);
if (actualCallback) {
promise.then(function (data) {
actualCallback(null, data);
}, function (err) {
actualCallback(err);
});
} else {
return promise;
}
},
/**
* 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) {
// In case someone is using a version where options parameter did not exist.
var actualCallback;
if (typeof options === 'function') {
actualCallback = options;
} else {
actualCallback = callback;
}
var actualOptions = {};
if (typeof options === 'object') {
Object.keys(options).forEach(function (key) {
actualOptions[key] = options[key];
});
}
var request = WebApiRequest.builder()
.withPath('/1/content/' + trackIds.join('+') + '/lookup')
.build();
this._addAccessToken(request, this.getAccessToken());
this._addQueryParameters(request, actualOptions);
var promise = this._performRequest(HttpManager.get, request);
if (actualCallback) {
promise.then(function (data) {
actualCallback(null, data);
}, function (err) {
actualCallback(err);
});
} else {
return promise;
}
},
/**
* 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) {
// In case someone is using a version where options parameter did not exist.
var actualCallback;
if (typeof options === 'function') {
actualCallback = options;
} else {
actualCallback = callback;
}
var actualOptions = {};
if (typeof options === 'object') {
Object.keys(options).forEach(function (key) {
actualOptions[key] = options[key];
});
}
var request = WebApiRequest.builder()
.withPath('/v1/albums/' + albumId)
.withQueryParameters(actualOptions)
.build();
this._addAccessToken(request, this.getAccessToken());
var promise = this._performRequest(HttpManager.get, request);
if (actualCallback) {
promise.then(function (data) {
actualCallback(null, data);
}, function (err) {
actualCallback(err);
});
} else {
return promise;
}
},
/**
* 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) {
// In case someone is using a version where options parameter did not exist.
var actualCallback;
if (typeof options === 'function') {
actualCallback = options;
} else {
actualCallback = callback;
}
var actualOptions = {};
if (typeof options === 'object') {
Object.keys(options).forEach(function (key) {
actualOptions[key] = options[key];
});
}
var request = WebApiRequest.builder()
.withPath('/v1/albums')
.withQueryParameters({
'ids': albumIds.join(',')
})
.build();
this._addAccessToken(request, this.getAccessToken());
this._addQueryParameters(request, actualOptions);
var promise = this._performRequest(HttpManager.get, request);
if (actualCallback) {
promise.then(function (data) {
actualCallback(null, data);
}, function (err) {
actualCallback(err);
});
} else {
return promise;
}
},
/**
* 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) {
var request = WebApiRequest.builder()
.withPath('/v1/artists/' + artistId)
.build();
this._addAccessToken(request, this.getAccessToken());
var promise = this._performRequest(HttpManager.get, request);
if (callback) {
promise.then(function (data) {
callback(null, data);
}, function (err) {
callback(err);
});
} else {
return promise;
}
},
/**
* 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', and 'track'.
* @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 request = WebApiRequest.builder()
.withPath('/1/content/music/search')
.withQueryParameters({
filters: types.join('+'),
q: query
}).build();
var actualOptions = {};
if (typeof options === 'object') {
Object.keys(options).forEach(function (key) {
actualOptions[key] = options[key];
});
}
this._addQueryParameters(request, actualOptions);
this._addAccessToken(request, this.getAccessToken());
this._addQueryParameters(request, options);
var promise = this._performRequest(HttpManager.get, request);
if (callback) {
promise.then(function (data) {
callback(null, data);
}, function (err) {
callback(err);
});
} else {
return promise;
}
},
/**
* 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, ['tracks'], options, 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) {
var request = WebApiRequest.builder()
.withPath('/v1/me')
.build();
this._addAccessToken(request, this.getAccessToken());
var promise = this._performRequest(HttpManager.get, request);
if (callback) {
promise.then(function (data) {
callback(null, data);
}, function (err) {
callback(err);
});
} else {
return promise;
}
},
/**
* 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('thelinmichael').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;
if (typeof userId === 'string') {
path = '/v1/users/' + encodeURIComponent(userId) + '/playlists';
} else {
path = '/v1/me/playlists';
}
var request = WebApiRequest.builder()
.withPath(path)
.build();
this._addAccessToken(request, this.getAccessToken());
this._addQueryParameters(request, options);
var promise = this._performRequest(HttpManager.get, request);
if (callback) {
promise.then(function (data) {
callback(null, data);
}, function (err) {
callback(err);
});
} else {
return promise;
}
},
};
module.exports = GrooveMusicWebApi;