From 9c3e8606af195ac13429ef4eed351f3fbd670559 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 21 May 2017 15:01:23 +0200 Subject: [PATCH 01/17] enable playlist export and creation --- lib/actions/playlist.js | 28 ++++++++++++++++++++++++++++ package.json | 4 ++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index 8e6cc81b..deb7de40 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -1,4 +1,16 @@ 'use strict'; +const util = require('util'); + + +function createPlaylist(player, values) { + const playlistName = decodeURIComponent(values[0]); + return player.coordinator + .createPlaylist(playlistName) + .then((res) => { + return res; + } + ); +} function playlist(player, values) { const playlistName = decodeURIComponent(values[0]); @@ -7,6 +19,22 @@ function playlist(player, values) { .then(() => player.coordinator.play()); } +function playlistExport(player, values) { + var sqid = decodeURIComponent(values[0]); + console.log("playlistExport sqid:" + sqid); +return player.coordinator + .exportPlaylist(sqid) + .then((res) => { + var dump = res.map((x) => util.inspect(x, false, null) ); + console.log("dump xxxxxxxxxx:", dump); + return { + export : res + }; + }); +} + module.exports = function (api) { api.registerAction('playlist', playlist); + api.registerAction('playlistexport', playlistExport); + api.registerAction('createplaylist', createPlaylist); }; diff --git a/package.json b/package.json index 19907b34..34eb527d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sonos-http-api", - "version": "1.4.1", + "version": "1.4.2", "description": "A simple node app for controlling a Sonos system with basic HTTP requests", "scripts": { "start": "node server.js" @@ -18,7 +18,7 @@ "json5": "^0.5.1", "node-static": "~0.7.0", "request-promise": "~1.0.2", - "sonos-discovery": "https://github.com/jishi/node-sonos-discovery/archive/v1.4.1.tar.gz" + "sonos-discovery": "file:../node-sonos-discovery" }, "engines": { "node": ">=4.0.0", From 3cd7231cffbb62bf0921aa9ad7b945b502c4cc41 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 21 May 2017 18:38:18 +0200 Subject: [PATCH 02/17] enable playlist import --- lib/actions/playlist.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index deb7de40..b4677494 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -1,6 +1,6 @@ 'use strict'; const util = require('util'); - +const os = require('os'); function createPlaylist(player, values) { const playlistName = decodeURIComponent(values[0]); @@ -12,6 +12,25 @@ function createPlaylist(player, values) { ); } +function importPlaylist(player, values) { + const sqid = decodeURIComponent(values[0]); + const title = decodeURIComponent(values[1]); + let uri = values.slice(2).map(x => "/" + x).join().replace(/,/g,'').replace(/\//,''); + const platform = os.platform(); + let hostname = os.hostname(); + if(platform === 'darwin') { + hostname = hostname.replace(/.local$/gi,''); + } + uri = hostname + uri; + console.log("importPlaylistx:" + sqid + ',uri:' + uri + ',title:' + title); + return player.coordinator + .importPlaylist(sqid, uri, title) + .then((res) => { + return res; + } + ); +} + function playlist(player, values) { const playlistName = decodeURIComponent(values[0]); return player.coordinator @@ -36,5 +55,6 @@ return player.coordinator module.exports = function (api) { api.registerAction('playlist', playlist); api.registerAction('playlistexport', playlistExport); - api.registerAction('createplaylist', createPlaylist); + api.registerAction('playlistcreate', createPlaylist); + api.registerAction('playlistimport', importPlaylist); }; From 07bfff49a88a3fa01173ccd5d750acff76999ad2 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 21 May 2017 19:55:58 +0200 Subject: [PATCH 03/17] gardeing --- lib/actions/playlist.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index b4677494..74ae4c0a 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -38,14 +38,11 @@ function playlist(player, values) { .then(() => player.coordinator.play()); } -function playlistExport(player, values) { - var sqid = decodeURIComponent(values[0]); - console.log("playlistExport sqid:" + sqid); -return player.coordinator +function exportPlaylist(player, values) { + var sqid = decodeURIComponent(values[0]); + return player.coordinator .exportPlaylist(sqid) .then((res) => { - var dump = res.map((x) => util.inspect(x, false, null) ); - console.log("dump xxxxxxxxxx:", dump); return { export : res }; @@ -54,7 +51,7 @@ return player.coordinator module.exports = function (api) { api.registerAction('playlist', playlist); - api.registerAction('playlistexport', playlistExport); + api.registerAction('playlistexport', exportPlaylist); api.registerAction('playlistcreate', createPlaylist); api.registerAction('playlistimport', importPlaylist); }; From 18be6675a8d5bf36a799d22635ff900cfd5172ab Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 21 May 2017 21:00:21 +0200 Subject: [PATCH 04/17] documentation --- static/index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/index.html b/static/index.html index e2ef1a1b..65e9f7c0 100644 --- a/static/index.html +++ b/static/index.html @@ -63,6 +63,9 @@

Actions

  • state (will return a json-representation of the current state of player)
  • favorite
  • playlist
  • +
  • playlistcreate (parameter is playlist name)
  • +
  • playlistexport (will return a json-representation of all playlists. because playlists can share the same name, each playlist unique identifier is the sqid key. optional parameter is this sqid key, will return the desired playlist)
  • +
  • playlistimport (parameters : sqid of the playlist / track title / absolute path to the track file on your SONOS Controller computer. Example : /1/track title//Music/iTunes/iTunes Media/Music/artist/album/track file.mp3). Note : invalid file paths will not trigger an error, the device currently does not return an UPnP error code for unmatched remote files)
  • lockvolumes / unlockvolumes (experimental, will enforce the volume that was selected when locking!)
  • repeat (on/off)
  • shuffle (on/off)
  • From 121addf374044c117ef6ac7b83d2fdda5254f648 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sat, 27 May 2017 15:39:29 +0200 Subject: [PATCH 05/17] playlist delete/doc --- lib/actions/playlist.js | 11 +++++++++++ static/index.html | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index 74ae4c0a..6402adc8 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -12,6 +12,16 @@ function createPlaylist(player, values) { ); } +function deletePlaylist(player, values) { + const sqid = decodeURIComponent(values[0]); + return player.coordinator + .deletePlaylist(sqid) + .then((res) => { + return res; + } + ); +} + function importPlaylist(player, values) { const sqid = decodeURIComponent(values[0]); const title = decodeURIComponent(values[1]); @@ -53,5 +63,6 @@ module.exports = function (api) { api.registerAction('playlist', playlist); api.registerAction('playlistexport', exportPlaylist); api.registerAction('playlistcreate', createPlaylist); + api.registerAction('playlistdelete', deletePlaylist); api.registerAction('playlistimport', importPlaylist); }; diff --git a/static/index.html b/static/index.html index 65e9f7c0..9cfd3518 100644 --- a/static/index.html +++ b/static/index.html @@ -65,7 +65,7 @@

    Actions

  • playlist
  • playlistcreate (parameter is playlist name)
  • playlistexport (will return a json-representation of all playlists. because playlists can share the same name, each playlist unique identifier is the sqid key. optional parameter is this sqid key, will return the desired playlist)
  • -
  • playlistimport (parameters : sqid of the playlist / track title / absolute path to the track file on your SONOS Controller computer. Example : /1/track title//Music/iTunes/iTunes Media/Music/artist/album/track file.mp3). Note : invalid file paths will not trigger an error, the device currently does not return an UPnP error code for unmatched remote files)
  • +
  • playlistimport (parameters : sqid of the playlist / track title / absolute path to the track file on your SONOS Controller computer. Example : /1/track title//Music/iTunes/iTunes Media/Music/artist/album/track file.mp3). Note : invalid file paths will not trigger an error, the device currently does not return an UPnP error code for unmatched remote files.)
  • lockvolumes / unlockvolumes (experimental, will enforce the volume that was selected when locking!)
  • repeat (on/off)
  • shuffle (on/off)
  • From 9f6b45ef5068d3b4f07dbd64ba2b1dfbc6dd1ced Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 28 May 2017 11:56:04 +0200 Subject: [PATCH 06/17] enable playlist multitrack import --- lib/actions/playlist.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index 6402adc8..8cee7156 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -25,6 +25,7 @@ function deletePlaylist(player, values) { function importPlaylist(player, values) { const sqid = decodeURIComponent(values[0]); const title = decodeURIComponent(values[1]); + console.log('values:', util.inspect(values.slice(2), false, null)) let uri = values.slice(2).map(x => "/" + x).join().replace(/,/g,'').replace(/\//,''); const platform = os.platform(); let hostname = os.hostname(); From a67960aad19f19a4d2e047af187015e97ef56afb Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 28 May 2017 12:34:26 +0200 Subject: [PATCH 07/17] fix import uri and documentation --- lib/actions/playlist.js | 8 +------- static/index.html | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index 8cee7156..f3c254ec 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -25,14 +25,8 @@ function deletePlaylist(player, values) { function importPlaylist(player, values) { const sqid = decodeURIComponent(values[0]); const title = decodeURIComponent(values[1]); - console.log('values:', util.inspect(values.slice(2), false, null)) + console.log('values:', util.inspect(values.slice(2), false, null)); let uri = values.slice(2).map(x => "/" + x).join().replace(/,/g,'').replace(/\//,''); - const platform = os.platform(); - let hostname = os.hostname(); - if(platform === 'darwin') { - hostname = hostname.replace(/.local$/gi,''); - } - uri = hostname + uri; console.log("importPlaylistx:" + sqid + ',uri:' + uri + ',title:' + title); return player.coordinator .importPlaylist(sqid, uri, title) diff --git a/static/index.html b/static/index.html index 9cfd3518..84aeb8af 100644 --- a/static/index.html +++ b/static/index.html @@ -65,7 +65,7 @@

    Actions

  • playlist
  • playlistcreate (parameter is playlist name)
  • playlistexport (will return a json-representation of all playlists. because playlists can share the same name, each playlist unique identifier is the sqid key. optional parameter is this sqid key, will return the desired playlist)
  • -
  • playlistimport (parameters : sqid of the playlist / track title / absolute path to the track file on your SONOS Controller computer. Example : /1/track title//Music/iTunes/iTunes Media/Music/artist/album/track file.mp3). Note : invalid file paths will not trigger an error, the device currently does not return an UPnP error code for unmatched remote files.)
  • +
  • playlistimport (parameters : sqid of the playlist / track or playlist title / absolute path to the track file on your SONOS Controller computer or web path or playlist jffs path on your SONOS device. Example : /1/track title/x-file-cifs://MacBook-Air-de-laurent-2/Music/iTunes/iTunes Media/Music/artist/album/track file.mp3 or /1/playlist title/file:///jffs/settings/savedqueues.rsq#1). Note : invalid track file paths will not trigger an error, the device currently does not return an UPnP error code for unmatched Controller files.)
  • lockvolumes / unlockvolumes (experimental, will enforce the volume that was selected when locking!)
  • repeat (on/off)
  • shuffle (on/off)
  • From 9c8f12981483100f81be25121cc99d21718bee8d Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sat, 10 Jun 2017 17:34:12 +0200 Subject: [PATCH 08/17] enable POST --- server.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index a5700431..54dbdcdb 100644 --- a/server.js +++ b/server.js @@ -14,6 +14,19 @@ const discovery = new SonosSystem(settings); const api = new SonosHttpAPI(discovery, settings); var requestHandler = function (req, res) { + let body = ''; + + if (req.method === 'POST') { + req.on('data', function (data) { + body += data; + + // Too much POST data, kill the connection! + // 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB + if (body.length > 1e6) { + req.connection.destroy(); + } + }); + } req.addListener('end', function () { fileServer.serve(req, res, function (err) { @@ -46,7 +59,8 @@ var requestHandler = function (req, res) { return; } - if (req.method === 'GET') { + req.body = JSON.parse(body); + if (req.method === 'GET' || req.method === 'POST') { api.requestHandler(req, res); } }); From 2c7167d2482c8967e02d1d316f7f5f5b42696c6f Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sat, 10 Jun 2017 17:47:11 +0200 Subject: [PATCH 09/17] fixed massive POST bug --- server.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 54dbdcdb..fb781a92 100644 --- a/server.js +++ b/server.js @@ -58,8 +58,13 @@ var requestHandler = function (req, res) { res.end(); return; } - - req.body = JSON.parse(body); + if (req.method === 'POST') { + try { + req.body = JSON.parse(body); + } catch(e) { + logger.error("Invalid JSON body", e); + } + } if (req.method === 'GET' || req.method === 'POST') { api.requestHandler(req, res); } From 1f158666835b9f9baa2459c493e3e76127302e23 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sat, 10 Jun 2017 19:41:35 +0200 Subject: [PATCH 10/17] POST body can be non json --- server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server.js b/server.js index fb781a92..77acd53b 100644 --- a/server.js +++ b/server.js @@ -59,6 +59,7 @@ var requestHandler = function (req, res) { return; } if (req.method === 'POST') { + req.body = body; try { req.body = JSON.parse(body); } catch(e) { From 6c9c9dd90653d5e52e79d50b6bba1768f7698fb4 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sat, 10 Jun 2017 20:27:51 +0200 Subject: [PATCH 11/17] playlist import finally works. cleanup later --- lib/actions/playlist.js | 41 +++++++++++++++++++++++++++++------------ lib/sonos-http-api.js | 2 ++ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index f3c254ec..14a91e95 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -1,6 +1,5 @@ 'use strict'; -const util = require('util'); -const os = require('os'); +const sleep = require('system-sleep'); function createPlaylist(player, values) { const playlistName = decodeURIComponent(values[0]); @@ -24,16 +23,34 @@ function deletePlaylist(player, values) { function importPlaylist(player, values) { const sqid = decodeURIComponent(values[0]); - const title = decodeURIComponent(values[1]); - console.log('values:', util.inspect(values.slice(2), false, null)); - let uri = values.slice(2).map(x => "/" + x).join().replace(/,/g,'').replace(/\//,''); - console.log("importPlaylistx:" + sqid + ',uri:' + uri + ',title:' + title); - return player.coordinator - .importPlaylist(sqid, uri, title) - .then((res) => { - return res; - } - ); + if (values.body !== undefined) { + const items = values.body.export; + const p = Promise.resolve() + return p.then(_ => { + items.map(item => { + const title = item.title; + const uri = item.uri; + // there is no way to batch import in Controller UI, we must import one by one + // and since adding requires a synchronous browse for index update, we block + // 1000 should be long enough, tested on WiFi + play:5 + sleep(1000); + return player.coordinator.importPlaylist(sqid, uri, title); + }); + }).then((res) => { + return res; + }); + // TODO fail (ex: wrong sqid), timeout, document batch post + } + else { + const title = decodeURIComponent(values[1]); + const uri = values.slice(2).map(x => "/" + x).join().replace(/,/g,'').replace(/\//,''); + return player.coordinator + .importPlaylist(sqid, uri, title) + .then((res) => { + return res; + } + ); + } } function playlist(player, values) { diff --git a/lib/sonos-http-api.js b/lib/sonos-http-api.js index 6f776965..dce33f13 100644 --- a/lib/sonos-http-api.js +++ b/lib/sonos-http-api.js @@ -47,6 +47,7 @@ function HttpAPI(discovery, settings) { }); this.requestHandler = function (req, res) { + if (req.url === '/favicon.ico') { res.end(); return; @@ -73,6 +74,7 @@ function HttpAPI(discovery, settings) { opt.action = (params[0] || '').toLowerCase(); opt.values = params.splice(1); } + opt.values.body = req.body; function sendResponse(code, body) { var jsonResponse = JSON.stringify(body); From 342e4cdad417770207d132daa2514cb4c258eff0 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 11 Jun 2017 00:14:39 +0200 Subject: [PATCH 12/17] block i/o on import --- lib/actions/playlist.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index 14a91e95..146f9455 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -31,15 +31,17 @@ function importPlaylist(player, values) { const title = item.title; const uri = item.uri; // there is no way to batch import in Controller UI, we must import one by one - // and since adding requires a synchronous browse for index update, we block - // 1000 should be long enough, tested on WiFi + play:5 - sleep(1000); + // since adding requires a synchronous browse for index update, we block + // block should be long enough, tested on WiFi + play:5 + sleep(500); return player.coordinator.importPlaylist(sqid, uri, title); }); }).then((res) => { return res; + }).catch((err) => { + throw new Error(err); }); - // TODO fail (ex: wrong sqid), timeout, document batch post + // TODO timeout, document batch post postman/curl } else { const title = decodeURIComponent(values[1]); From 3bc036d94e9ce8b3d7ff3e8155fb8f16d0c67119 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 11 Jun 2017 12:31:21 +0200 Subject: [PATCH 13/17] can export/import by playlistname --- lib/actions/playlist.js | 17 +++++++++-------- static/index.html | 11 +++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index 146f9455..5edf06ec 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -24,16 +24,16 @@ function deletePlaylist(player, values) { function importPlaylist(player, values) { const sqid = decodeURIComponent(values[0]); if (values.body !== undefined) { - const items = values.body.export; - const p = Promise.resolve() - return p.then(_ => { + // multi uri + const items = values.body.export.items; + return Promise.resolve().then(_ => { items.map(item => { const title = item.title; const uri = item.uri; // there is no way to batch import in Controller UI, we must import one by one - // since adding requires a synchronous browse for index update, we block - // block should be long enough, tested on WiFi + play:5 - sleep(500); + // since adduri requires a synchronous browse for index updateID, we block + // block should be low enough, tested on WiFi + play:5 + sleep(600); return player.coordinator.importPlaylist(sqid, uri, title); }); }).then((res) => { @@ -44,6 +44,7 @@ function importPlaylist(player, values) { // TODO timeout, document batch post postman/curl } else { + // single uri or internal rsq jffs to jffs merge const title = decodeURIComponent(values[1]); const uri = values.slice(2).map(x => "/" + x).join().replace(/,/g,'').replace(/\//,''); return player.coordinator @@ -63,9 +64,9 @@ function playlist(player, values) { } function exportPlaylist(player, values) { - var sqid = decodeURIComponent(values[0]); + var id = decodeURIComponent(values[0]); return player.coordinator - .exportPlaylist(sqid) + .exportPlaylist(id) .then((res) => { return { export : res diff --git a/static/index.html b/static/index.html index 84aeb8af..d525d952 100644 --- a/static/index.html +++ b/static/index.html @@ -62,10 +62,13 @@

    Actions

  • previous
  • state (will return a json-representation of the current state of player)
  • favorite
  • -
  • playlist
  • -
  • playlistcreate (parameter is playlist name)
  • -
  • playlistexport (will return a json-representation of all playlists. because playlists can share the same name, each playlist unique identifier is the sqid key. optional parameter is this sqid key, will return the desired playlist)
  • -
  • playlistimport (parameters : sqid of the playlist / track or playlist title / absolute path to the track file on your SONOS Controller computer or web path or playlist jffs path on your SONOS device. Example : /1/track title/x-file-cifs://MacBook-Air-de-laurent-2/Music/iTunes/iTunes Media/Music/artist/album/track file.mp3 or /1/playlist title/file:///jffs/settings/savedqueues.rsq#1). Note : invalid track file paths will not trigger an error, the device currently does not return an UPnP error code for unmatched Controller files.)
  • +
  • playlist (parameter is playlist name, will play)
  • +
  • playlistcreate (parameter is playlist name, will return a suitable sqid used for export, import and delete, see below)
  • +
  • playlistdelete (parameter is sqid of the playlist)
  • +
  • playlistexport for backup purposes in case of factory reset or hardware failure (will return a json-representation of all playlists (optional parameter is name, will return the desired playlist contents)
  • +
  • playlistimport (parameters : sqid of the playlist / optional: track or playlist title / optional: track or playlist uri obtained by playlistexport. Example : /1/track title/x-file-cifs://MacBook-Air-de-laurent-2/Music/iTunes/iTunes Media/Music/artist/album/track file.mp3 or for appending a playlist to another one : /1/playlist title/file:///jffs/settings/savedqueues.rsq#2. Note : invalid local uri track paths will not trigger an error, the device currently does not return an UPnP error code for unmatched Controller files. +
    + to import a whole playlist : save the response generated by playlistexport/name as a file, and POST the file contents to playlistimport/sqid. Example: TODO. Be patient, large playlists can take time.
  • lockvolumes / unlockvolumes (experimental, will enforce the volume that was selected when locking!)
  • repeat (on/off)
  • shuffle (on/off)
  • From 4ee7d38b047fdd37aa74adc32d8ebcda7f31093f Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 11 Jun 2017 12:45:31 +0200 Subject: [PATCH 14/17] create by name --- static/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/index.html b/static/index.html index d525d952..17affe1e 100644 --- a/static/index.html +++ b/static/index.html @@ -63,8 +63,8 @@

    Actions

  • state (will return a json-representation of the current state of player)
  • favorite
  • playlist (parameter is playlist name, will play)
  • -
  • playlistcreate (parameter is playlist name, will return a suitable sqid used for export, import and delete, see below)
  • -
  • playlistdelete (parameter is sqid of the playlist)
  • +
  • playlistcreate (parameter is playlist name, will return a suitable name used for export, import and delete, see below)
  • +
  • playlistdelete (parameter is name of the playlist)
  • playlistexport for backup purposes in case of factory reset or hardware failure (will return a json-representation of all playlists (optional parameter is name, will return the desired playlist contents)
  • playlistimport (parameters : sqid of the playlist / optional: track or playlist title / optional: track or playlist uri obtained by playlistexport. Example : /1/track title/x-file-cifs://MacBook-Air-de-laurent-2/Music/iTunes/iTunes Media/Music/artist/album/track file.mp3 or for appending a playlist to another one : /1/playlist title/file:///jffs/settings/savedqueues.rsq#2. Note : invalid local uri track paths will not trigger an error, the device currently does not return an UPnP error code for unmatched Controller files.
    From 50ed5ec01ae2440bfcaac489f5f022485ff41c14 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 11 Jun 2017 18:19:51 +0200 Subject: [PATCH 15/17] documentation of import --- README.md | 4 ++++ lib/actions/playlist.js | 6 +++--- static/index.html | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 78eb5d0c..09763eb0 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,10 @@ The actions supported as of today: * favorite * favorites (with optional "detailed" parameter) * playlist +* playlistcreate +* playlistdelete +* playlistexport +* playlistimport * lockvolumes / unlockvolumes (experimental, will enforce the volume that was selected when locking!) * repeat (on/off) * shuffle (on/off) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index 5edf06ec..af25e6fa 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -31,8 +31,8 @@ function importPlaylist(player, values) { const title = item.title; const uri = item.uri; // there is no way to batch import in Controller UI, we must import one by one - // since adduri requires a synchronous browse for index updateID, we block - // block should be low enough, tested on WiFi + play:5 + // since adduri requires a synchronous browse for index updateID, we pause. + // pause should be low enough for the HTTP roundtrips, tested on WiFi + play:5 sleep(600); return player.coordinator.importPlaylist(sqid, uri, title); }); @@ -44,7 +44,7 @@ function importPlaylist(player, values) { // TODO timeout, document batch post postman/curl } else { - // single uri or internal rsq jffs to jffs merge + // single uri or internal rsq jffs to jffs appending const title = decodeURIComponent(values[1]); const uri = values.slice(2).map(x => "/" + x).join().replace(/,/g,'').replace(/\//,''); return player.coordinator diff --git a/static/index.html b/static/index.html index 17affe1e..d8a1c019 100644 --- a/static/index.html +++ b/static/index.html @@ -66,9 +66,9 @@

    Actions

  • playlistcreate (parameter is playlist name, will return a suitable name used for export, import and delete, see below)
  • playlistdelete (parameter is name of the playlist)
  • playlistexport for backup purposes in case of factory reset or hardware failure (will return a json-representation of all playlists (optional parameter is name, will return the desired playlist contents)
  • -
  • playlistimport (parameters : sqid of the playlist / optional: track or playlist title / optional: track or playlist uri obtained by playlistexport. Example : /1/track title/x-file-cifs://MacBook-Air-de-laurent-2/Music/iTunes/iTunes Media/Music/artist/album/track file.mp3 or for appending a playlist to another one : /1/playlist title/file:///jffs/settings/savedqueues.rsq#2. Note : invalid local uri track paths will not trigger an error, the device currently does not return an UPnP error code for unmatched Controller files. +
  • playlistimport (parameters : name of the playlist / optional: track or playlist name / optional: track or playlist uri obtained by playlistexport. Example : /target playlist name/track title/x-file-cifs://MacBook-Air-de-laurent-2/Music/iTunes/iTunes Media/Music/artist/album/track file.mp3 or for appending a playlist to another one : /target playlist name/source playlist name/file:///jffs/settings/savedqueues.rsq#2. Note : invalid local uri track paths will not trigger an error, the device currently does not return an UPnP error code for unmatched Controller files.
    - to import a whole playlist : save the response generated by playlistexport/name as a file, and POST the file contents to playlistimport/sqid. Example: TODO. Be patient, large playlists can take time.
  • + to import a whole playlist : save the response generated by playlistexport/name as a file, and POST the file contents to playlistimport/name. Example: curl -v POST http://localhost:5005/playlistimport/playlist name/places.json -d @export.json --header "Content-Type: application/json". Be patient, large playlists can take time.
  • lockvolumes / unlockvolumes (experimental, will enforce the volume that was selected when locking!)
  • repeat (on/off)
  • shuffle (on/off)
  • From 771fd9707b08e7fce39d0416f84c07ad18d1ee04 Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 11 Jun 2017 18:46:49 +0200 Subject: [PATCH 16/17] import sleep and use temporary discovery link til https://github.com/jishi/node-sonos-discovery/pull/105 is merged --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 34eb527d..6ab8e2c0 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "json5": "^0.5.1", "node-static": "~0.7.0", "request-promise": "~1.0.2", - "sonos-discovery": "file:../node-sonos-discovery" + "sonos-discovery": "file:../node-sonos-discovery", + "system-sleep": "^1.3.5" }, "engines": { "node": ">=4.0.0", From 429cd6cfbbda418d1ed770e1d7728caaa1af525b Mon Sep 17 00:00:00 2001 From: laurentperez Date: Sun, 11 Jun 2017 18:48:12 +0200 Subject: [PATCH 17/17] polish --- lib/actions/playlist.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/actions/playlist.js b/lib/actions/playlist.js index af25e6fa..a55179e8 100644 --- a/lib/actions/playlist.js +++ b/lib/actions/playlist.js @@ -41,7 +41,6 @@ function importPlaylist(player, values) { }).catch((err) => { throw new Error(err); }); - // TODO timeout, document batch post postman/curl } else { // single uri or internal rsq jffs to jffs appending