Browse Source

Clean up a bit, add crude playlist implementation, start sockets

Josh Bicking 6 years ago
parent
commit
3ef28f3413
6 changed files with 1287 additions and 41 deletions
  1. 19 14
      app.js
  2. 1123 0
      package-lock.json
  3. 4 3
      package.json
  4. 116 23
      routes/index.js
  5. 18 0
      routes/player-demo.js
  6. 7 1
      views/index.hbs

+ 19 - 14
app.js

@@ -1,29 +1,34 @@
+// express and http
 var express = require('express');
-var path = require('path');
-var favicon = require('serve-favicon');
-var logger = require('morgan');
-var cookieParser = require('cookie-parser');
-var bodyParser = require('body-parser');
-
-var routes = require('./routes/index');
-var users = require('./routes/users');
-
 var app = express();
+var http = require('http').Server(app);
 
 // view engine setup
+var path = require('path');
 app.set('views', path.join(__dirname, 'views'));
 app.set('view engine', 'hbs');
 
+// logging
+var logger = require('morgan');
+app.use(logger('dev'));
+
+// Sockets
+var io = require('socket.io').listen(http);
+io.on('connection', function(socket){
+  console.log('a user connected');
+});
+
+var routes = require('./routes/index');
+
+// Unused (for now) cookies and favicons
+//var favicon = require('serve-favicon');
+//var cookieParser = require('cookie-parser');
 // uncomment after placing your favicon in /public
 //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
-app.use(logger('dev'));
-app.use(bodyParser.json());
-app.use(bodyParser.urlencoded({ extended: false }));
-app.use(cookieParser());
+//app.use(cookieParser());
 app.use(express.static(path.join(__dirname, 'public')));
 
 app.use('/', routes);
-app.use('/users', users);
 
 // catch 404 and forward to error handler
 app.use(function(req, res, next) {

+ 1123 - 0
package-lock.json

@@ -0,0 +1,1123 @@
+{
+  "name": "lan-jukebox",
+  "version": "0.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@types/bluebird": {
+      "version": "3.5.8",
+      "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.8.tgz",
+      "integrity": "sha512-rBfrD56OxaqVjghtVqp2EEX0ieHkRk6IefDVrQXIVGvlhDOEBTvZff4Q02uo84ukVkH4k5eB1cPKGDM2NlFL8A=="
+    },
+    "@types/express": {
+      "version": "4.0.36",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.0.36.tgz",
+      "integrity": "sha512-bT9q2eqH/E72AGBQKT50dh6AXzheTqigGZ1GwDiwmx7vfHff0bZOrvUWjvGpNWPNkRmX1vDF6wonG6rlpBHb1A==",
+      "requires": {
+        "@types/express-serve-static-core": "4.0.49",
+        "@types/serve-static": "1.7.31"
+      }
+    },
+    "@types/express-serve-static-core": {
+      "version": "4.0.49",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.0.49.tgz",
+      "integrity": "sha512-b7mVHoURu1xaP/V6xw1sYwyv9V0EZ7euyi+sdnbnTZxEkAh4/hzPsI6Eflq+ZzHQ/Tgl7l16Jz+0oz8F46MLnA==",
+      "requires": {
+        "@types/node": "8.0.15"
+      }
+    },
+    "@types/mime": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.1.tgz",
+      "integrity": "sha512-rek8twk9C58gHYqIrUlJsx8NQMhlxqHzln9Z9ODqiNgv3/s+ZwIrfr+djqzsnVM12xe9hL98iJ20lj2RvCBv6A=="
+    },
+    "@types/node": {
+      "version": "8.0.15",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.15.tgz",
+      "integrity": "sha512-ASl21hskyMlYJkx11SqyWksFHZ0Eb8hiQK+VLZYRCpm99KcPj+JuO1rfVCM2kNKhKKpdR1DVlFrrOYiSeUeiIg=="
+    },
+    "@types/serve-static": {
+      "version": "1.7.31",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.7.31.tgz",
+      "integrity": "sha1-FUVt6NmNa0z/Mb5savdJKuY/Uho=",
+      "requires": {
+        "@types/express-serve-static-core": "4.0.49",
+        "@types/mime": "1.3.1"
+      }
+    },
+    "accepts": {
+      "version": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz",
+      "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=",
+      "requires": {
+        "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz",
+        "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz"
+      }
+    },
+    "after": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+      "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+    },
+    "align-text": {
+      "version": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+      "requires": {
+        "kind-of": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz",
+        "longest": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+        "repeat-string": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz"
+      }
+    },
+    "amdefine": {
+      "version": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
+    },
+    "array-flatten": {
+      "version": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "arraybuffer.slice": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz",
+      "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco="
+    },
+    "async": {
+      "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+    },
+    "backo2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+    },
+    "base64-arraybuffer": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+      "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
+    },
+    "base64id": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
+      "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY="
+    },
+    "basic-auth": {
+      "version": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.0.4.tgz",
+      "integrity": "sha1-Awk1sB3nyblKgksp8/zLdQ06UpA="
+    },
+    "better-assert": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+      "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+      "requires": {
+        "callsite": "1.0.0"
+      }
+    },
+    "bindings": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz",
+      "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE="
+    },
+    "blob": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
+      "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE="
+    },
+    "bluebird": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
+      "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw="
+    },
+    "body-parser": {
+      "version": "https://registry.npmjs.org/body-parser/-/body-parser-1.15.2.tgz",
+      "integrity": "sha1-11eM9PHRHV9uqATO813Hp/9trmc=",
+      "requires": {
+        "bytes": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
+        "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
+        "debug": "2.2.0",
+        "depd": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
+        "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz",
+        "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz",
+        "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+        "qs": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz",
+        "raw-body": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
+        "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.14.tgz"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+          "requires": {
+            "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+          }
+        }
+      }
+    },
+    "bytes": {
+      "version": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
+      "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk="
+    },
+    "callsite": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+      "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
+    },
+    "camelcase": {
+      "version": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+      "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+      "optional": true
+    },
+    "center-align": {
+      "version": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+      "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+      "optional": true,
+      "requires": {
+        "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+        "lazy-cache": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz"
+      }
+    },
+    "cliui": {
+      "version": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+      "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+      "optional": true,
+      "requires": {
+        "center-align": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+        "right-align": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+        "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
+      },
+      "dependencies": {
+        "wordwrap": {
+          "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+          "optional": true
+        }
+      }
+    },
+    "component-bind": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+      "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+    },
+    "component-emitter": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+    },
+    "component-inherit": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+      "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+    },
+    "content-disposition": {
+      "version": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.1.tgz",
+      "integrity": "sha1-h0dsamfI2qh+Muh2Ft+IO6f7Bxs="
+    },
+    "content-type": {
+      "version": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
+      "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0="
+    },
+    "cookie": {
+      "version": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+    },
+    "cookie-parser": {
+      "version": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz",
+      "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
+      "requires": {
+        "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+        "cookie-signature": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz"
+      }
+    },
+    "cookie-signature": {
+      "version": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "debug": {
+      "version": "2.6.8",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
+      "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+      "requires": {
+        "ms": "2.0.0"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "decamelize": {
+      "version": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "optional": true
+    },
+    "depd": {
+      "version": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
+      "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM="
+    },
+    "destroy": {
+      "version": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "ee-first": {
+      "version": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "engine.io": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.0.tgz",
+      "integrity": "sha1-XKQ4486f28kVxKIcjdnhJmcG5X4=",
+      "requires": {
+        "accepts": "1.3.3",
+        "base64id": "1.0.0",
+        "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+        "debug": "2.6.8",
+        "engine.io-parser": "2.1.1",
+        "uws": "0.14.5",
+        "ws": "2.3.1"
+      },
+      "dependencies": {
+        "accepts": {
+          "version": "1.3.3",
+          "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
+          "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
+          "requires": {
+            "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz",
+            "negotiator": "0.6.1"
+          }
+        },
+        "negotiator": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+          "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
+        }
+      }
+    },
+    "engine.io-client": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.1.tgz",
+      "integrity": "sha1-QVqYUrrbFPoAj6PvHjFgjbZ2EyU=",
+      "requires": {
+        "component-emitter": "1.2.1",
+        "component-inherit": "0.0.3",
+        "debug": "2.6.8",
+        "engine.io-parser": "2.1.1",
+        "has-cors": "1.1.0",
+        "indexof": "0.0.1",
+        "parsejson": "0.0.3",
+        "parseqs": "0.0.5",
+        "parseuri": "0.0.5",
+        "ws": "2.3.1",
+        "xmlhttprequest-ssl": "1.5.3",
+        "yeast": "0.1.2"
+      }
+    },
+    "engine.io-parser": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.1.tgz",
+      "integrity": "sha1-4Ps/DgRi9/WLt3waUun1p+JuRmg=",
+      "requires": {
+        "after": "0.8.2",
+        "arraybuffer.slice": "0.0.6",
+        "base64-arraybuffer": "0.1.5",
+        "blob": "0.0.4",
+        "has-binary2": "1.0.2"
+      }
+    },
+    "escape-html": {
+      "version": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "etag": {
+      "version": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
+      "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg="
+    },
+    "express": {
+      "version": "https://registry.npmjs.org/express/-/express-4.13.4.tgz",
+      "integrity": "sha1-PAt288d1kMg0VzkGHsC9O6Bn7CQ=",
+      "requires": {
+        "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz",
+        "array-flatten": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+        "content-disposition": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.1.tgz",
+        "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
+        "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.1.5.tgz",
+        "cookie-signature": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+        "debug": "2.2.0",
+        "depd": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
+        "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+        "etag": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
+        "finalhandler": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz",
+        "fresh": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz",
+        "merge-descriptors": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+        "methods": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+        "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+        "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
+        "path-to-regexp": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+        "proxy-addr": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz",
+        "qs": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz",
+        "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz",
+        "send": "https://registry.npmjs.org/send/-/send-0.13.1.tgz",
+        "serve-static": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.3.tgz",
+        "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.14.tgz",
+        "utils-merge": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
+        "vary": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz"
+      },
+      "dependencies": {
+        "cookie": {
+          "version": "https://registry.npmjs.org/cookie/-/cookie-0.1.5.tgz",
+          "integrity": "sha1-armUiksa4hlSzSWIUwpHItQETXw="
+        },
+        "debug": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+          "requires": {
+            "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+          }
+        },
+        "qs": {
+          "version": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz",
+          "integrity": "sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc="
+        }
+      }
+    },
+    "express-validator": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-3.2.1.tgz",
+      "integrity": "sha1-RWA+fu5pMYXCGY+969QUkl/9NSQ=",
+      "requires": {
+        "@types/bluebird": "3.5.8",
+        "@types/express": "4.0.36",
+        "bluebird": "3.5.0",
+        "lodash": "4.17.4",
+        "validator": "6.2.1"
+      }
+    },
+    "finalhandler": {
+      "version": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz",
+      "integrity": "sha1-haF8bFmpRxfSYtYSMNSw6+PUoU0=",
+      "requires": {
+        "debug": "2.2.0",
+        "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+        "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+        "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+          "requires": {
+            "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+          }
+        }
+      }
+    },
+    "follow-redirects": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz",
+      "integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=",
+      "requires": {
+        "debug": "2.6.8",
+        "stream-consume": "0.1.0"
+      }
+    },
+    "foreachasync": {
+      "version": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
+      "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY="
+    },
+    "forwarded": {
+      "version": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz",
+      "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M="
+    },
+    "fresh": {
+      "version": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz",
+      "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8="
+    },
+    "handlebars": {
+      "version": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.5.tgz",
+      "integrity": "sha1-ksbta7FkEQxQ1NjQ+93HCAbG+Oc=",
+      "requires": {
+        "async": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+        "optimist": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+        "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+        "uglify-js": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz"
+      }
+    },
+    "has-binary2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz",
+      "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=",
+      "requires": {
+        "isarray": "2.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+        }
+      }
+    },
+    "has-cors": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+      "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+    },
+    "hbs": {
+      "version": "https://registry.npmjs.org/hbs/-/hbs-4.0.1.tgz",
+      "integrity": "sha1-S/2YZQ3IydrESzyprfnAmOi8M7Y=",
+      "requires": {
+        "handlebars": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.5.tgz",
+        "walk": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz"
+      }
+    },
+    "home": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/home/-/home-0.1.8.tgz",
+      "integrity": "sha1-SHjoYJ1XnNQfhwX9QLejDu0U3FQ="
+    },
+    "http-errors": {
+      "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz",
+      "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=",
+      "requires": {
+        "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+        "setprototypeof": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz",
+        "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz"
+      }
+    },
+    "iconv-lite": {
+      "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz",
+      "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI="
+    },
+    "indexof": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+    },
+    "inherits": {
+      "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "ipaddr.js": {
+      "version": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz",
+      "integrity": "sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c="
+    },
+    "is-buffer": {
+      "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz",
+      "integrity": "sha1-z8hszV3FpS+oBIkRHGkgxFfi2Ys="
+    },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+    },
+    "keypress": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz",
+      "integrity": "sha1-HoBFQlABjbrUw/6USX1uZ7YmnHc="
+    },
+    "kind-of": {
+      "version": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz",
+      "integrity": "sha1-R11pil5J/15T0U4+cyQp3Iv0z0c=",
+      "requires": {
+        "is-buffer": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz"
+      }
+    },
+    "lame": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lame/-/lame-1.2.4.tgz",
+      "integrity": "sha1-VGgBUU4t6MoMie7yIogMjcZxJIU=",
+      "requires": {
+        "bindings": "1.2.1",
+        "debug": "2.6.8",
+        "nan": "2.6.2",
+        "readable-stream": "1.1.14"
+      }
+    },
+    "lazy-cache": {
+      "version": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+      "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+      "optional": true
+    },
+    "lodash": {
+      "version": "4.17.4",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
+      "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
+    },
+    "longest": {
+      "version": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+      "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
+    },
+    "media-typer": {
+      "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "merge-descriptors": {
+      "version": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "mime": {
+      "version": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
+      "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM="
+    },
+    "mime-db": {
+      "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz",
+      "integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I="
+    },
+    "mime-types": {
+      "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz",
+      "integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=",
+      "requires": {
+        "mime-db": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz"
+      }
+    },
+    "minimist": {
+      "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+      "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
+    },
+    "morgan": {
+      "version": "https://registry.npmjs.org/morgan/-/morgan-1.7.0.tgz",
+      "integrity": "sha1-6xDKjlDRq+D409rVwCAdBS2YHGI=",
+      "requires": {
+        "basic-auth": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.0.4.tgz",
+        "debug": "2.2.0",
+        "depd": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
+        "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+        "on-headers": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+          "requires": {
+            "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+          }
+        }
+      }
+    },
+    "ms": {
+      "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+      "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+    },
+    "nan": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz",
+      "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U="
+    },
+    "negotiator": {
+      "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz",
+      "integrity": "sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g="
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "object-component": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+      "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
+    },
+    "on-finished": {
+      "version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
+      }
+    },
+    "on-headers": {
+      "version": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
+      "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
+    },
+    "optimist": {
+      "version": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+      "requires": {
+        "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+        "wordwrap": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz"
+      }
+    },
+    "parsejson": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz",
+      "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=",
+      "requires": {
+        "better-assert": "1.0.2"
+      }
+    },
+    "parseqs": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+      "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+      "requires": {
+        "better-assert": "1.0.2"
+      }
+    },
+    "parseuri": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+      "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+      "requires": {
+        "better-assert": "1.0.2"
+      }
+    },
+    "parseurl": {
+      "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
+      "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY="
+    },
+    "path-to-regexp": {
+      "version": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "pcm-volume": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/pcm-volume/-/pcm-volume-1.0.0.tgz",
+      "integrity": "sha1-6qhRyUloq24ULOLhKVNwi+Sn8MA=",
+      "requires": {
+        "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
+      }
+    },
+    "player": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/player/-/player-0.6.1.tgz",
+      "integrity": "sha1-hHFkvdhl6yqOOGNuFNEoMI0nrEQ=",
+      "requires": {
+        "async": "0.9.2",
+        "follow-redirects": "0.0.7",
+        "home": "0.1.8",
+        "keypress": "0.2.1",
+        "lame": "1.2.4",
+        "pcm-volume": "1.0.0",
+        "pool_stream": "0.0.2",
+        "speaker": "0.2.6",
+        "underscore": "1.6.0"
+      },
+      "dependencies": {
+        "async": {
+          "version": "0.9.2",
+          "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+          "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0="
+        }
+      }
+    },
+    "pool_stream": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/pool_stream/-/pool_stream-0.0.2.tgz",
+      "integrity": "sha1-OB5wB2WkpSNgwwzdi+I3AqGnj94="
+    },
+    "proxy-addr": {
+      "version": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.10.tgz",
+      "integrity": "sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU=",
+      "requires": {
+        "forwarded": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz",
+        "ipaddr.js": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz"
+      }
+    },
+    "qs": {
+      "version": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz",
+      "integrity": "sha1-O3hIwDwt7OaalSKw+ujEEm10Xzs="
+    },
+    "range-parser": {
+      "version": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz",
+      "integrity": "sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU="
+    },
+    "raw-body": {
+      "version": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz",
+      "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=",
+      "requires": {
+        "bytes": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
+        "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz",
+        "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
+      }
+    },
+    "readable-stream": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+      "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+      "requires": {
+        "core-util-is": "1.0.2",
+        "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+        "isarray": "0.0.1",
+        "string_decoder": "0.10.31"
+      }
+    },
+    "repeat-string": {
+      "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+    },
+    "right-align": {
+      "version": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+      "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+      "optional": true,
+      "requires": {
+        "align-text": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
+      "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c="
+    },
+    "send": {
+      "version": "https://registry.npmjs.org/send/-/send-0.13.1.tgz",
+      "integrity": "sha1-ow1fTILIqbrprQCh2bG9vm8Zntc=",
+      "requires": {
+        "debug": "2.2.0",
+        "depd": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
+        "destroy": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+        "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+        "etag": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
+        "fresh": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz",
+        "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz",
+        "mime": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
+        "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+        "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+        "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz",
+        "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+          "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+          "requires": {
+            "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+          }
+        },
+        "http-errors": {
+          "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz",
+          "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=",
+          "requires": {
+            "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+            "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz"
+          }
+        },
+        "statuses": {
+          "version": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz",
+          "integrity": "sha1-3e1FzBglbVHtQK7BQkidXGECbSg="
+        }
+      }
+    },
+    "serve-favicon": {
+      "version": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.3.2.tgz",
+      "integrity": "sha1-3UGeJo3gEqtysxnTN/IQUBP5OB8=",
+      "requires": {
+        "etag": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
+        "fresh": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz",
+        "ms": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+        "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz"
+      },
+      "dependencies": {
+        "ms": {
+          "version": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U="
+        }
+      }
+    },
+    "serve-static": {
+      "version": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.3.tgz",
+      "integrity": "sha1-zlpuzTEB/tXsCYJ9rCKpwpv7BTU=",
+      "requires": {
+        "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+        "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
+        "send": "https://registry.npmjs.org/send/-/send-0.13.2.tgz"
+      },
+      "dependencies": {
+        "http-errors": {
+          "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz",
+          "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=",
+          "requires": {
+            "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+            "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz"
+          }
+        },
+        "send": {
+          "version": "https://registry.npmjs.org/send/-/send-0.13.2.tgz",
+          "integrity": "sha1-dl52B8gFVFK7pvCwUllTUJhgNt4=",
+          "requires": {
+            "debug": "2.2.0",
+            "depd": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
+            "destroy": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+            "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+            "etag": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
+            "fresh": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz",
+            "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz",
+            "mime": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
+            "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+            "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+            "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz",
+            "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz"
+          },
+          "dependencies": {
+            "debug": {
+              "version": "2.2.0",
+              "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+              "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+              "requires": {
+                "ms": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
+              }
+            }
+          }
+        },
+        "statuses": {
+          "version": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz",
+          "integrity": "sha1-3e1FzBglbVHtQK7BQkidXGECbSg="
+        }
+      }
+    },
+    "setprototypeof": {
+      "version": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz",
+      "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg="
+    },
+    "socket.io": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.3.tgz",
+      "integrity": "sha1-Q1nwaiSTOua9CHeYr3jGgOrjReM=",
+      "requires": {
+        "debug": "2.6.8",
+        "engine.io": "3.1.0",
+        "object-assign": "4.1.1",
+        "socket.io-adapter": "1.1.0",
+        "socket.io-client": "2.0.3",
+        "socket.io-parser": "3.1.2"
+      }
+    },
+    "socket.io-adapter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.0.tgz",
+      "integrity": "sha1-x6pGUB3VVsLLiiivj/lcC14dqkw=",
+      "requires": {
+        "debug": "2.3.3"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz",
+          "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=",
+          "requires": {
+            "ms": "0.7.2"
+          }
+        },
+        "ms": {
+          "version": "0.7.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+          "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U="
+        }
+      }
+    },
+    "socket.io-client": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.3.tgz",
+      "integrity": "sha1-bK9K/5+FsZ/ZG2zhPWmttWT4hzs=",
+      "requires": {
+        "backo2": "1.0.2",
+        "base64-arraybuffer": "0.1.5",
+        "component-bind": "1.0.0",
+        "component-emitter": "1.2.1",
+        "debug": "2.6.8",
+        "engine.io-client": "3.1.1",
+        "has-cors": "1.1.0",
+        "indexof": "0.0.1",
+        "object-component": "0.0.3",
+        "parseqs": "0.0.5",
+        "parseuri": "0.0.5",
+        "socket.io-parser": "3.1.2",
+        "to-array": "0.1.4"
+      }
+    },
+    "socket.io-parser": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz",
+      "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=",
+      "requires": {
+        "component-emitter": "1.2.1",
+        "debug": "2.6.8",
+        "has-binary2": "1.0.2",
+        "isarray": "2.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+        }
+      }
+    },
+    "source-map": {
+      "version": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+      "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+      "requires": {
+        "amdefine": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz"
+      }
+    },
+    "speaker": {
+      "version": "0.2.6",
+      "resolved": "https://registry.npmjs.org/speaker/-/speaker-0.2.6.tgz",
+      "integrity": "sha1-0p6Qt94bkmpHcYeW2hHyup68GZo=",
+      "requires": {
+        "bindings": "1.2.1",
+        "debug": "2.6.8",
+        "nan": "2.6.2",
+        "readable-stream": "1.0.34"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "requires": {
+            "core-util-is": "1.0.2",
+            "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+            "isarray": "0.0.1",
+            "string_decoder": "0.10.31"
+          }
+        }
+      }
+    },
+    "statuses": {
+      "version": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+      "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
+    },
+    "stream-consume": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz",
+      "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8="
+    },
+    "string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+    },
+    "to-array": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+      "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+    },
+    "type-is": {
+      "version": "https://registry.npmjs.org/type-is/-/type-is-1.6.14.tgz",
+      "integrity": "sha1-4hljnBfe0coHiQkt1UoDgmuBfLI=",
+      "requires": {
+        "media-typer": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+        "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz"
+      }
+    },
+    "uglify-js": {
+      "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz",
+      "integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=",
+      "optional": true,
+      "requires": {
+        "async": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+        "source-map": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+        "uglify-to-browserify": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+        "yargs": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz"
+      },
+      "dependencies": {
+        "async": {
+          "version": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+          "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
+          "optional": true
+        },
+        "source-map": {
+          "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+          "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+          "optional": true
+        }
+      }
+    },
+    "uglify-to-browserify": {
+      "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+      "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+      "optional": true
+    },
+    "ultron": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz",
+      "integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ="
+    },
+    "underscore": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+      "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+    },
+    "unpipe": {
+      "version": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "utils-merge": {
+      "version": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
+      "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg="
+    },
+    "uws": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz",
+      "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=",
+      "optional": true
+    },
+    "validator": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/validator/-/validator-6.2.1.tgz",
+      "integrity": "sha1-vFdbeNFb6y4zimZbqVMMf0Ce9mc="
+    },
+    "vary": {
+      "version": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz",
+      "integrity": "sha1-meSYFWaihhGN+yuBc1ffeZM3bRA="
+    },
+    "walk": {
+      "version": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz",
+      "integrity": "sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins=",
+      "requires": {
+        "foreachasync": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz"
+      }
+    },
+    "window-size": {
+      "version": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+      "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+      "optional": true
+    },
+    "wordwrap": {
+      "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+    },
+    "ws": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
+      "integrity": "sha1-a5Sz5EfLajY/eF6vlK9jWejoHIA=",
+      "requires": {
+        "safe-buffer": "5.0.1",
+        "ultron": "1.1.0"
+      }
+    },
+    "xmlhttprequest-ssl": {
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz",
+      "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0="
+    },
+    "yargs": {
+      "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+      "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+      "optional": true,
+      "requires": {
+        "camelcase": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+        "cliui": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+        "decamelize": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+        "window-size": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
+      }
+    },
+    "yeast": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+      "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+    }
+  }
+}

+ 4 - 3
package.json

@@ -8,12 +8,13 @@
   "dependencies": {
     "body-parser": "~1.15.1",
     "cookie-parser": "~1.4.3",
-    "debug": "~2.2.0",
+    "debug": "^2.6.8",
     "express": "~4.13.4",
-    "express-validator": "^3.1.2",
+    "express-validator": "^3.2.1",
     "hbs": "~4.0.0",
     "morgan": "~1.7.0",
     "player": "^0.6.1",
-    "serve-favicon": "~2.3.0"
+    "serve-favicon": "~2.3.0",
+    "socket.io": "^2.0.3"
   }
 }

+ 116 - 23
routes/index.js

@@ -1,45 +1,133 @@
-var express = require('express');
-var router = express.Router();
+var app = require('express')();
+
+// Body parsing
 var bodyParser = require('body-parser');
+app.use(bodyParser.urlencoded({extended: true}));
+app.use(bodyParser.json());
+
+// Validation
 var expressValidator = require('express-validator');
-router.use(bodyParser.urlencoded({extended: true}));
-router.use(bodyParser.json());
-router.use(expressValidator());
+app.use(expressValidator());
 
+// Child process, for spawning youtube-dl
 var child_process = require('child_process');
 
+// The location of the youtube-dl executable
+var YOUTUBE_DL_LOC = '/usr/bin/youtube-dl'
+
+// Keep track of music to play, playing, and played
+var playlist = [];
+var playing = 'None';
+var played = [];
+
 var Player = require('player');
-var player = new Player();
 
-var YOUTUBE_DL_LOC = '/usr/local/bin/youtube-dl'
+// Create a player with the proper error handling.
+function playerCreator(song) {
+    if (song)
+	return new Player(song)
+	.on('error', function(err) {playerHandleError(playlist, played)})
+    return new Player()
+	.on('error', function(err) {playerHandleError(playlist, played)})
+}
+
+player = playerCreator();
+
+// Player functionality
+
+// Songs move from playlist (if nothing is playing presently), to playing, to played.
+
+// TODO check if song already exists in played.
+
+// event: on error (No next song was found).
+function playerHandleError(playlist, played) {
+
+    // TODO Workaround for a bug in player: wait a couple seconds so the
+    // songs don't overlap.
+    
+    // Move the recently playing song to played.
+    played.push(playing);
+
+    if (playlist.length != 0) {
+	// Play the next song.
+	player = playerCreator(playlist[0]);
+	player.play();
+
+	// Remove the added song.
+	playing = playlist[0];
+	playlist.shift();
+    } else {
+	// Reset the object, resetting its playlist.
+	player = playerCreator();
+	playing = 'None';
+    }
+}
+
+// Add a song and start the playlist, if it's empty.
+function playerAddSong(song) {
+    if (player.list.length == 0) {
+	// If this is the player's first song, start it.
+	player = playerCreator(song);
+	player.play();
+	playing = song;
+    } else { 
+	// Otherwise add it to the queue
+	playlist.push(song);
+    }
+
+}
+
 
 /* GET home page. */
-router.get('/', function(req, res, next) {
-    var playlist;
-    if(player.list.length) {
-	playlist = '<ul>';
-	for(var i=0; i < player.list.length; i++) {
-	    playlist +=
+app.get('/', function(req, res, next) {
+    var playlist_view;
+    if(playlist.length) {
+	playlist_view = '<p>Playlist:</p><ul>';
+	for(var i=0; i < playlist.length; i++) {
+	    playlist_view +=
+		'<li>' +
+		playlist[i].replace(/^\.\/downloads\//, '') .replace(/\.mp3$/, '') +
+		'</li>';
+	}
+	playlist_view += '</ul>';
+    } else {
+	playlist_view = '<p>No songs in playlist.</p>';
+    }
+    var playing_view = '<p>Now playing: ' + playing.replace(/^\.\/downloads\//, '') .replace(/\.mp3$/, '') + '</p>';
+    var played_view;
+    if(played.length) {
+	played_view = '<p>Played:</p><ul>';
+	for(var i=0; i < played.length; i++) {
+	    played_view +=
 		'<li>' +
-		player.list[i].replace(/^\.\/downloads\//, '') .replace(/\.mp3$/, '') +
+		played[i].replace(/^\.\/downloads\//, '') .replace(/\.mp3$/, '') +
 		'</li>';
 	}
-	playlist += '</ul>';
+	played_view += '</ul>';
     } else {
-	playlist = '<p>No songs in playlist.</p>';
+	played_view = '<p>No played songs.</p>';
     }
-    res.render('index', { playlist: playlist });
+    res.render('index', { playlist_view: playlist_view,
+			  playing_view: playing_view,
+			  played_view: played_view });
 });
 
-router.get('/youtube', function(req, res, next) {
+app.get('/youtube', function(req, res, next) {
   res.render('youtube');
 });
 
-router.use(expressValidator({
+app.use(expressValidator({
     customValidators: {
 	dlSuccess: function(video) {
 	    // Test if the requested video is available.
+	    // TODO make this non-blocking so music doesn't stop
 	    var youtube_dl = child_process.spawnSync(YOUTUBE_DL_LOC, ['-s', video]);
+
+	    // DEBUG: for when I forget to change the var
+	    if(youtube_dl.stderr == null) {
+		console.log("Your youtube-dl location is probably invalid.");
+	    }
+
 	    if (youtube_dl.stderr.toString()) {
 		return false;
 	    }
@@ -49,7 +137,7 @@ router.use(expressValidator({
  }
 ));
 
-router.post('/youtube', function(req, res) {
+app.post('/youtube', function(req, res) {
     var video = req.body.video;
     
     req.checkBody('video', 'URL is not valid.').dlSuccess();
@@ -73,15 +161,20 @@ router.post('/youtube', function(req, res) {
 
             if(!error) {
                 var youtube_dl_get_title = child_process.spawnSync(YOUTUBE_DL_LOC, ['--get-title', video]);
-                player.add('./downloads/' + youtube_dl_get_title.stdout.toString().replace('\n', '') + ".mp3");
+		console.log(youtube_dl_get_title.stdout.toString());
+                playerAddSong('./downloads/' + youtube_dl_get_title.stdout.toString()
+			      .replace('\n', '')
+			      .replace(new RegExp('"', 'g'), '\'')
+			      + ".mp3");
             }
             res.redirect('/');
         });
     }
 });
 
-router.get('/file', function(req, res, next) {
+app.get('/file', function(req, res, next) {
   res.render('file');
 });
 
-module.exports = router;
+
+module.exports = app;

+ 18 - 0
routes/player-demo.js

@@ -0,0 +1,18 @@
+var Player = require('player');
+var player = new Player();
+
+playerAddSong('./downloads/\'IS THAT A MAN RIDING A SHRIMP\'.mp3');
+
+// event: on error (No next song was found)
+player.on('error', function(err){
+  // Reset the object, resetting the playlist.
+  player = new Player();
+});
+
+// Add a song and start the playlist, if it's empty
+function playerAddSong(song) {
+    player.add(song);
+  if (player.list.length == 1) {
+    player.play();
+  }
+}

+ 7 - 1
views/index.hbs

@@ -1,5 +1,11 @@
 <h1>LAN Jukebox</h1>
 <a href="/youtube">Add a Youtube video</a><br/>
 <a href="/file">Add a music file</a><br/>
-{{{playlist}}}
+{{{playlist_view}}}
+{{{playing_view}}}
+{{{played_view}}}
 <p>Allan put music player here</p>
+<script src="/socket.io/socket.io.js"></script>
+<script>
+  var socket = io();
+</script>