From ba8b86f8f4782ad9c76a4e464d20f2b42744349c Mon Sep 17 00:00:00 2001 From: sashman-jaspin Date: Wed, 19 Jun 2013 10:30:00 -0400 Subject: [PATCH 1/3] Adding playlist4service proto --- proto/playlist4service.desc | 6 ++++++ proto/playlist4service.orig | Bin 0 -> 3476 bytes proto/playlist4service.proto | 8 ++++++++ 3 files changed, 14 insertions(+) create mode 100644 proto/playlist4service.desc create mode 100644 proto/playlist4service.orig create mode 100644 proto/playlist4service.proto 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.orig b/proto/playlist4service.orig new file mode 100644 index 0000000000000000000000000000000000000000..4a0278a70f269abb0cf440a76461a424a9c80343 GIT binary patch literal 3476 zcmcImL2u(k6eexcCNEK%PP?hwQnfNGZB?MVxPTT3q)k&Ru{O1XowN%GSxz#WSR{6^ zC*1~4oH%piz=5BzcW(Rx{t+`{Cw0;|+GWep= zc3{IFIjt+3bZf&V&83UmLYn;&O(>KxDGjNILv(y?`!sgZ>-dO99(uNrpk`V06xLb+ zI1VKvJvxa=|71EB@s9-Tvm1g^D2~BWby`?wrMbt8&@P~tp4%VCSBL#3_Jm~$KZyj$ z6L;nht6x(5I{}Yl;aymy)wP{Yt#^8fy?d_Ae*=sVz+eJLNYW(IVhavpbI>svdJcL9 zs~+};JwhA0e3X%%Ea+cAg1Xh|;C9eA0~7H(KpN!^!IXcvN~w8|%@co3z-D96rjaKE z<>9v=I~X&z@$PIKlbW$2i#8!+`;+^EffD~oz`8+)rR%wl{nNcN^ckdm+`}zCz6?S} z`Z%@?tioIm9Ab=}&}VcEgF)hGGt$Gw{(-GXV2ARdZf zgH?ajZ@N9MQE4WZ1MKxJhe{jAltN4i!x*0DdZnJ_`@EECRDqoBG~FAS{djIWZ9ZJN z1(Py?rA+yWzb9ZLLZNU1zl=;EYyC&Dtqb~%+Yt&N8Pa&1b=Z3;HoyefY`w(4lCU-V zw`BEts0b@Ru$-CP=cLm&4t;@1*KMWIT=y%EW8V3-mqgvw*QL2k&V{ z`g~#BSZ`DY!YUVuc&5yg z%wOn>7F)LYFB)GTGEyZLE^E--Le7te8n8nl%oiNKlVqfL_R`Dddy0RqKw*}>;dI>t zf2T%_EnJI0&ce8rq(cx``KS3DpbUf&S#ZEC<`y(GWXnlLdiwSa3Cr<5Y)x}JjLY_g z6crP`4~B#de+67Sj=eHM+3=EmcrZiSFcjNHQ^u_N1B9LXIB5hN7NnFHBXdo5gBXyOPiIOLWaf!Lg!V z7OTp+*)U{1zkF*xIoFl^3fhLvz`Uv$b+fK%W>r(m`Q-8ICD@%@9ZGdXPZ8uU3?hY0GYtP3->9XEuBpnmaz2UnU^_f-)XtfGtwDW_3N6bj v`U<{yldH{(V!c#R)UsKto|>1k&XKR758xo;Zm7kYQDJ3|q?bBdswDmedV*bh literal 0 HcmV?d00001 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 From 1629cbbe9cfb8ff97316ae5fda0c2e4825a8e392 Mon Sep 17 00:00:00 2001 From: sashman-jaspin Date: Wed, 19 Jun 2013 11:09:35 -0400 Subject: [PATCH 2/3] Adding ablity to add track, add playlist and add playlist to rootlist --- addtrack.js | 16 ++++++ example/addtrack.js | 16 ++++++ lib/schemas.js | 3 ++ lib/spotify.js | 119 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 addtrack.js create mode 100644 example/addtrack.js diff --git a/addtrack.js b/addtrack.js new file mode 100644 index 0000000..d62d15c --- /dev/null +++ b/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/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. * From 88d34a70303615f0bd397ac757f00a8eb2af2514 Mon Sep 17 00:00:00 2001 From: sashman-jaspin Date: Wed, 19 Jun 2013 11:15:40 -0400 Subject: [PATCH 3/3] Pop. --- addtrack.js | 16 ---------------- proto/playlist4service.orig | Bin 3476 -> 0 bytes 2 files changed, 16 deletions(-) delete mode 100644 addtrack.js delete mode 100644 proto/playlist4service.orig diff --git a/addtrack.js b/addtrack.js deleted file mode 100644 index d62d15c..0000000 --- a/addtrack.js +++ /dev/null @@ -1,16 +0,0 @@ -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/proto/playlist4service.orig b/proto/playlist4service.orig deleted file mode 100644 index 4a0278a70f269abb0cf440a76461a424a9c80343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3476 zcmcImL2u(k6eexcCNEK%PP?hwQnfNGZB?MVxPTT3q)k&Ru{O1XowN%GSxz#WSR{6^ zC*1~4oH%piz=5BzcW(Rx{t+`{Cw0;|+GWep= zc3{IFIjt+3bZf&V&83UmLYn;&O(>KxDGjNILv(y?`!sgZ>-dO99(uNrpk`V06xLb+ zI1VKvJvxa=|71EB@s9-Tvm1g^D2~BWby`?wrMbt8&@P~tp4%VCSBL#3_Jm~$KZyj$ z6L;nht6x(5I{}Yl;aymy)wP{Yt#^8fy?d_Ae*=sVz+eJLNYW(IVhavpbI>svdJcL9 zs~+};JwhA0e3X%%Ea+cAg1Xh|;C9eA0~7H(KpN!^!IXcvN~w8|%@co3z-D96rjaKE z<>9v=I~X&z@$PIKlbW$2i#8!+`;+^EffD~oz`8+)rR%wl{nNcN^ckdm+`}zCz6?S} z`Z%@?tioIm9Ab=}&}VcEgF)hGGt$Gw{(-GXV2ARdZf zgH?ajZ@N9MQE4WZ1MKxJhe{jAltN4i!x*0DdZnJ_`@EECRDqoBG~FAS{djIWZ9ZJN z1(Py?rA+yWzb9ZLLZNU1zl=;EYyC&Dtqb~%+Yt&N8Pa&1b=Z3;HoyefY`w(4lCU-V zw`BEts0b@Ru$-CP=cLm&4t;@1*KMWIT=y%EW8V3-mqgvw*QL2k&V{ z`g~#BSZ`DY!YUVuc&5yg z%wOn>7F)LYFB)GTGEyZLE^E--Le7te8n8nl%oiNKlVqfL_R`Dddy0RqKw*}>;dI>t zf2T%_EnJI0&ce8rq(cx``KS3DpbUf&S#ZEC<`y(GWXnlLdiwSa3Cr<5Y)x}JjLY_g z6crP`4~B#de+67Sj=eHM+3=EmcrZiSFcjNHQ^u_N1B9LXIB5hN7NnFHBXdo5gBXyOPiIOLWaf!Lg!V z7OTp+*)U{1zkF*xIoFl^3fhLvz`Uv$b+fK%W>r(m`Q-8ICD@%@9ZGdXPZ8uU3?hY0GYtP3->9XEuBpnmaz2UnU^_f-)XtfGtwDW_3N6bj v`U<{yldH{(V!c#R)UsKto|>1k&XKR758xo;Zm7kYQDJ3|q?bBdswDmedV*bh