diff --git a/example/addtrack.js b/example/addtrack.js new file mode 100644 index 0000000..d62d15c --- /dev/null +++ b/example/addtrack.js @@ -0,0 +1,16 @@ +var Spotify = require('spotify-web'); + +// Spotify credentials... +var username = process.env.USERNAME; +var password = process.env.PASSWORD; + +Spotify.login(username, password, function (err, spotify) { + if (err) throw err; + + spotify.createplaylist(username, 'new playlist', function (err, ret) { + spotify.addtrack(ret,'spotify:track:6tdp8sdXrXlPV6AZZN2PE8', function (err, ret) { + console.log(ret); + }); + }); + +}); \ No newline at end of file diff --git a/lib/schemas.js b/lib/schemas.js index f58105e..fd7e55b 100644 --- a/lib/schemas.js +++ b/lib/schemas.js @@ -34,5 +34,8 @@ exports.playlist4content = new protobuf.Schema(fs.readFileSync(path.resolve(prot // playlist4changes.proto exports.playlist4changes = new protobuf.Schema(fs.readFileSync(path.resolve(proto, 'playlist4changes.desc'))); +// playlist4service.proto +exports.playlist4service = new protobuf.Schema(fs.readFileSync(path.resolve(proto, 'playlist4service.desc'))); + // toplist.proto exports.toplist = new protobuf.Schema(fs.readFileSync(path.resolve(proto, 'toplist.desc'))); diff --git a/lib/spotify.js b/lib/spotify.js index f92d1c0..25cbee9 100644 --- a/lib/spotify.js +++ b/lib/spotify.js @@ -36,6 +36,11 @@ var Track = require('./track'); var Image = require('./image'); require('./restriction'); +var playlist4ops = schemas.playlist4ops; + + +var playlist4service = schemas.playlist4service; + var playlist4changes = schemas.playlist4changes; var ListDump = playlist4changes['spotify.playlist4.proto.ListDump']; @@ -653,6 +658,120 @@ Spotify.prototype.rootlist = function (user, from, length, fn) { }); }; +/** + * Add track to a playlist. + * + * @param {String} uri playlist uri + * @param {String} uri track uri. + * @param {Function} fn callback function + * @api public + */ + +Spotify.prototype.addtrack = function (playlisturi, trackuri, fn) { + var self = this; + var playlist = playlisturi.split(':'); + var user = playlist[2]; + var playlistid = (playlist[3] == 'starred') ? 'starred' : "playlist/" + playlist[4]; + + var hm = 'hm://playlist/user/' + user + '/' + playlistid + '?syncpublished=1'; + var request = MercuryRequest.serialize({ body: 'ADD', uri: hm }).toString('base64'); + var data = new Buffer(trackuri).toString('base64'); + + var args = [ 0, request, data ]; + + this.sendCommand('sp/hm_b64', args, function (err, res) { + if (err) return fn(err); + + var data = res.result; + if (data.length >= 2) { + // success! + fn(err, trackuri); + } else { + // TODO: real error handling + var header = MercuryReply.parse(new Buffer(res.result[0], 'base64')); + fn(err, null); + } + + }); +}; + +/** + * Add a playlist to rootlist + * + * @param {String} username + * @param {String} uri playlist uri. + * @param {Function} fn callback function + * @api public + */ + +Spotify.prototype.addrootlist = function (user, playlisturi, fn) { + + var self = this; + var hm = 'hm://playlist/user/' + user + "/rootlist?add_first=1&syncpublished=1"; + var request = MercuryRequest.serialize({ body: 'ADD', uri: hm }).toString('base64'); + var data = new Buffer(playlisturi).toString('base64'); + + var args = [ 0, request, data ]; + + this.sendCommand('sp/hm_b64', args, function (err, res) { + if (err) return fn(err); + + var data = res.result; + + if (data.length >= 2) { + // success! + fn(err, playlisturi); + } else { + // TODO: real error handling + var header = MercuryReply.parse(new Buffer(res.result[0], 'base64')); + fn(err, null); + } + + }); +}; + +/** + * create a playlist and add it to rootlist + * + * @param {String} username + * @param {String} playlist name + * @param {Function} fn callback function + * @api public + */ + +Spotify.prototype.createplaylist = function (user, name, fn) { + + var self = this; + var hm = 'hm://playlist/user/' + user; + var request = MercuryRequest.serialize({ body: 'PUT', uri: hm }).toString('base64'); + // kind values can be found in playlist4ops schema + + var data = playlist4ops['spotify.playlist4.proto.Op'].serialize({kind:6, updateListAttributes : {newAttributes : { values : { name : name}}}}); + // need to serialize everything, including data, with mercuryrequest + data = MercuryRequest.serialize({ uri : data}).toString('base64'); + + var args = [ 0, request, data ]; + + this.sendCommand('sp/hm_b64', args, function (err, res) { + if (err) return fn(err); + + var obj; + var data = res.result; + + if (data.length >= 2) { + // success! + obj = self._parse(playlist4service['spotify.playlist4.proto.CreateListReply'], new Buffer(res.result[1], 'base64')); + self.addrootlist(user, obj.uri.toString(), fn); + } else { + // TODO: real error handling + var header = MercuryReply.parse(new Buffer(res.result[0], 'base64')); + fn(err, obj); + } + + }); +}; + + /** * Gets the MP3 160k audio URL for the given "track" metadata object. * diff --git a/proto/playlist4service.desc b/proto/playlist4service.desc new file mode 100644 index 0000000..07b937a --- /dev/null +++ b/proto/playlist4service.desc @@ -0,0 +1,6 @@ + +g +playlist4service.protospotify.playlist4.proto"0 +CreateListReply +uri (  +revision ( BH \ No newline at end of file diff --git a/proto/playlist4service.proto b/proto/playlist4service.proto new file mode 100644 index 0000000..d58652f --- /dev/null +++ b/proto/playlist4service.proto @@ -0,0 +1,8 @@ +package spotify.playlist4.proto; +option optimize_for = SPEED; + + + message CreateListReply { + required bytes uri = 1; + optional bytes revision = 2; + } \ No newline at end of file