",
"repository": {
@@ -11,17 +12,28 @@
"url": "https://github.com/jishi/node-sonos-http-api.git"
},
"dependencies": {
- "basic-auth": "~1.0.3",
- "fuse.js": "^2.2.0",
"anesidora": "^1.2.0",
- "node-static": "~0.7.0",
+ "aws-sdk": "^2.1299.0",
+ "basic-auth": "~1.1.0",
+ "fuse.js": "^6.4.1",
+ "html-entities": "^1.2.1",
+ "json5": "^0.5.1",
+ "mime": "^1.4.1",
+ "music-metadata": "^1.1.0",
+ "serve-static": "^1.15.0",
"request-promise": "~1.0.2",
- "sonos-discovery": "https://github.com/jishi/node-sonos-discovery/archive/v1.2.1.tar.gz"
+ "sonos-discovery": "https://github.com/jishi/node-sonos-discovery/archive/v1.8.0.tar.gz",
+ "wav-file-info": "0.0.8",
+ "elevenlabs-node": "2.0.1"
},
"engines": {
- "node": ">=4.0.0",
- "npm": "^2.0.0"
+ "node": ">=4 <23"
},
"main": "lib/sonos-http-api.js",
- "license": "MIT"
+ "license": "MIT",
+ "devDependencies": {
+ "eslint": "^4.8.0",
+ "eslint-config-airbnb-base": "^12.0.1",
+ "eslint-plugin-import": "^2.7.0"
+ }
}
diff --git a/presets/example.json b/presets/example.json
index ec7afcde..420c2540 100644
--- a/presets/example.json
+++ b/presets/example.json
@@ -27,5 +27,5 @@
"crossfade": false
},
"pauseOthers": false,
- "favorite": "My example favorite"
+
}
diff --git a/server.js b/server.js
index ca427e00..f59d6abd 100644
--- a/server.js
+++ b/server.js
@@ -3,25 +3,25 @@ const http = require('http');
const https = require('https');
const fs = require('fs');
const auth = require('basic-auth');
-const SonosDiscovery = require('sonos-discovery');
+const SonosSystem = require('sonos-discovery');
const logger = require('sonos-discovery/lib/helpers/logger');
const SonosHttpAPI = require('./lib/sonos-http-api.js');
-const nodeStatic = require('node-static');
+const serveStatic = require('serve-static');
const settings = require('./settings');
-const fileServer = new nodeStatic.Server(settings.webroot);
-const discovery = new SonosDiscovery(settings);
+const serve = new serveStatic(settings.webroot);
+const discovery = new SonosSystem(settings);
const api = new SonosHttpAPI(discovery, settings);
var requestHandler = function (req, res) {
req.addListener('end', function () {
- fileServer.serve(req, res, function (err) {
+ serve(req, res, function (err) {
// If error, route it.
// This bypasses authentication on static files!
- if (!err) {
- return;
- }
+ //if (!err) {
+ // return;
+ //}
if (settings.auth) {
var credentials = auth(req);
@@ -80,8 +80,9 @@ process.on('unhandledRejection', (err) => {
logger.error(err);
});
-server.listen(settings.port, function () {
- logger.info('http server listening on port', settings.port);
+let host = settings.ip;
+server.listen(settings.port, host, function () {
+ logger.info('http server listening on', host, 'port', settings.port);
});
server.on('error', (err) => {
diff --git a/settings.js b/settings.js
index 422072b5..f2531e2d 100644
--- a/settings.js
+++ b/settings.js
@@ -2,6 +2,7 @@
const fs = require('fs');
const path = require('path');
const logger = require('sonos-discovery/lib/helpers/logger');
+const tryLoadJson = require('./lib/helpers/try-load-json');
function merge(target, source) {
Object.keys(source).forEach((key) => {
@@ -15,6 +16,7 @@ function merge(target, source) {
var settings = {
port: 5005,
+ ip: "0.0.0.0",
securePort: 5006,
cacheDir: path.resolve(__dirname, 'cache'),
webroot: path.resolve(__dirname, 'static'),
@@ -23,12 +25,9 @@ var settings = {
};
// load user settings
-try {
- const userSettings = require(path.resolve(__dirname, 'settings.json'));
- merge(settings, userSettings);
-} catch (e) {
- logger.info('no settings file found, will only use default settings');
-}
+const settingsFileFullPath = path.resolve(__dirname, 'settings.json');
+const userSettings = tryLoadJson(settingsFileFullPath);
+merge(settings, userSettings);
logger.debug(settings);
diff --git a/static/index.html b/static/index.html
index fb2b2846..7bfc06db 100644
--- a/static/index.html
+++ b/static/index.html
@@ -11,10 +11,14 @@
.logo { display: block; margin: auto }
.content { margin: 20px 25%; }
.docs { background-color: #f4f4f4; border-radius: 4px; padding: 10px; margin-bottom: 20px;}
- .docs p { padding: 0; margin: 0; line-height: 28px; font-size: 14px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; }
+ .docs p, .docs li { padding: 0; margin: 0; line-height: 28px; font-size: 14px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; }
.docs .method { background-color: #dbdbdb; padding: 4px; border-radius: 2px; margin-right: 4px;}
.docs h2 { margin-bottom: 8px;}
.docs h2:first-child { margin-top: 0;}
+ .docs h4 { margin-left: 10px; }
+ .docs ul { list-style-type: none; padding-left: 20px; }
+ .docs .explanation { margin-left: 20px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 14px; }
+ .docs .experimental { background-color: red; border-radius: 3px; color: white; font-size: 10px; line-height: 12px; padding: 2px 6px; font-weight: bold; }
.footer { text-align: center; font-size: 12px; margin: 10px 0 50px 0;}
@@ -34,8 +38,8 @@ Info
Global Control
GET /lockvolumes
GET /unlockvolumes
- GET /pauseall
- GET /resumeall
+ GET /pauseall/{timeout in minutes (optional)}
+ GET /resumeall/{timeout in minutes (optional)}
GET /reindex
GET /sleep/{timeout in seconds or timestamp HH:MM:SS or off}
GET /preset/{JSON preset}
@@ -47,30 +51,65 @@ Zone Control
GET /{zone name}/{action}[/{parameter}]
Actions
+
+ Playback
+
+ - play
+ - pause
+ - playpause toggles playing state
+ - trackseek/{seconds into song, i.e. 60 for 1:00, 120 for 2:00 etc.}
+ - next
+ - previous
+
+
+ Volume
+
+ - volume/{absolute volume}
+ - volume/{+ or -}{relative volume}
+ - groupVolume/{absolute volume}
+ - groupVolume/{+ or -}{relative volume}
+ - mute
+ - unmute
+ - groupMute
+ - groupUnmute
+ - togglemute
+ - lockvolumes
+ - unlockvolumes experimental enforce the volume that was selected when locking!
+
+
+ Playback Settings
+
+ - favorite
+ - playlist
+ - repeat/{on | off}
+ - shuffle/{on | off}
+ - crossfade/{on | off}
+
+
+ Queue
+
+ - queue
+ - clearqueue
+ - seek/{queue index}
+
+
+ Room Grouping
+
+ - add/{player name from existing zone, prefix with player you want to join}
+ - join/{player player to join in, prefix with player from current zone} (this is just the inverse of add)
+ - isolate
+ - ungroup (alias of isolate)
+ - leave (alias of isolate)
+
+
+ Other
+
+
+ Internals
- - play
- - pause
- - playpause (toggles playing state)
- - volume (parameter is absolute or relative volume. Prefix +/- indicates relative volume)
- - groupVolume (parameter is absolute or relative volume. Prefix +/- indicates relative volume)
- - mute / unmute
- - groupMute / groupUnmute
- - seek (parameter is queue index)
- - trackseek (parameter is in seconds, 60 for 1:00, 120 for 2:00 etc)
- - next
- - previous
- - state (will return a json-representation of the current state of player)
- - favorite
- - playlist
- - lockvolumes / unlockvolumes (experimental, will enforce the volume that was selected when locking!)
- - repeat (on/off)
- - shuffle (on/off)
- - crossfade (on/off)
- - pauseall (with optional timeout in minutes)
- - resumeall (will resume the ones that was pause on the pauseall call. Useful for doorbell, phone calls, etc. Optional timeout)
- - say
- - queue
- - clearqueue
+ - state returns a json-representation of the current state of player
diff --git a/test_endpoint.js b/test_endpoint.js
index 2fd72545..eedab434 100644
--- a/test_endpoint.js
+++ b/test_endpoint.js
@@ -2,6 +2,7 @@
const http = require('http');
let server = http.createServer((req, res) => {
+ console.log(req.method, req.url);
for (let header in req.headers) {
console.log(header + ':', req.headers[header]);
}
@@ -14,9 +15,11 @@ let server = http.createServer((req, res) => {
req.on('end', () => {
res.end();
- const json = JSON.parse(buffer.join(''));
- console.dir(json, {depth: 10});
- console.log('');
+ try {
+ const json = JSON.parse(buffer.join(''));
+ console.dir(json, { depth: 10 });
+ console.log('');
+ } catch (e) {}
});
});