Browse Source

Implement youtube-dl: check that URL is valid

Josh Bicking 7 years ago
commit
6157bf3a78
14 changed files with 326 additions and 0 deletions
  1. 33 0
      .gitignore
  2. 1 0
      README.md
  3. 60 0
      app.js
  4. 90 0
      bin/www
  5. 3 0
      downloads/.gitignore
  6. 18 0
      package.json
  7. 8 0
      public/stylesheets/style.css
  8. 58 0
      routes/index.js
  9. 9 0
      routes/users.js
  10. 3 0
      views/error.hbs
  11. 15 0
      views/file.hbs
  12. 4 0
      views/index.hbs
  13. 10 0
      views/layout.hbs
  14. 14 0
      views/youtube.hbs

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+# Logs
+logs
+*.log
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
+# https://docs.npmjs.com/cli/shrinkwrap#caveats
+node_modules
+
+# Debug log from npm
+npm-debug.log
+
+# Temp files
+.#

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# lan-jukebox

+ 60 - 0
app.js

@@ -0,0 +1,60 @@
+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();
+
+// view engine setup
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', 'hbs');
+
+// 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(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) {
+  var err = new Error('Not Found');
+  err.status = 404;
+  next(err);
+});
+
+// error handlers
+
+// development error handler
+// will print stacktrace
+if (app.get('env') === 'development') {
+  app.use(function(err, req, res, next) {
+    res.status(err.status || 500);
+    res.render('error', {
+      message: err.message,
+      error: err
+    });
+  });
+}
+
+// production error handler
+// no stacktraces leaked to user
+app.use(function(err, req, res, next) {
+  res.status(err.status || 500);
+  res.render('error', {
+    message: err.message,
+    error: {}
+  });
+});
+
+
+module.exports = app;

+ 90 - 0
bin/www

@@ -0,0 +1,90 @@
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var app = require('../app');
+var debug = require('debug')('lan-jukebox:server');
+var http = require('http');
+
+/**
+ * Get port from environment and store in Express.
+ */
+
+var port = normalizePort(process.env.PORT || '3000');
+app.set('port', port);
+
+/**
+ * Create HTTP server.
+ */
+
+var server = http.createServer(app);
+
+/**
+ * Listen on provided port, on all network interfaces.
+ */
+
+server.listen(port);
+server.on('error', onError);
+server.on('listening', onListening);
+
+/**
+ * Normalize a port into a number, string, or false.
+ */
+
+function normalizePort(val) {
+  var port = parseInt(val, 10);
+
+  if (isNaN(port)) {
+    // named pipe
+    return val;
+  }
+
+  if (port >= 0) {
+    // port number
+    return port;
+  }
+
+  return false;
+}
+
+/**
+ * Event listener for HTTP server "error" event.
+ */
+
+function onError(error) {
+  if (error.syscall !== 'listen') {
+    throw error;
+  }
+
+  var bind = typeof port === 'string'
+    ? 'Pipe ' + port
+    : 'Port ' + port;
+
+  // handle specific listen errors with friendly messages
+  switch (error.code) {
+    case 'EACCES':
+      console.error(bind + ' requires elevated privileges');
+      process.exit(1);
+      break;
+    case 'EADDRINUSE':
+      console.error(bind + ' is already in use');
+      process.exit(1);
+      break;
+    default:
+      throw error;
+  }
+}
+
+/**
+ * Event listener for HTTP server "listening" event.
+ */
+
+function onListening() {
+  var addr = server.address();
+  var bind = typeof addr === 'string'
+    ? 'pipe ' + addr
+    : 'port ' + addr.port;
+  debug('Listening on ' + bind);
+}

+ 3 - 0
downloads/.gitignore

@@ -0,0 +1,3 @@
+# Upload/YT Music
+*
+!.gitignore

+ 18 - 0
package.json

@@ -0,0 +1,18 @@
+{
+  "name": "lan-jukebox",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "start": "node ./bin/www"
+  },
+  "dependencies": {
+    "body-parser": "~1.15.1",
+    "cookie-parser": "~1.4.3",
+    "debug": "~2.2.0",
+    "express": "~4.13.4",
+    "express-validator": "^3.1.2",
+    "hbs": "~4.0.0",
+    "morgan": "~1.7.0",
+    "serve-favicon": "~2.3.0"
+  }
+}

+ 8 - 0
public/stylesheets/style.css

@@ -0,0 +1,8 @@
+body {
+  padding: 50px;
+  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
+}
+
+a {
+  color: #00B7FF;
+}

+ 58 - 0
routes/index.js

@@ -0,0 +1,58 @@
+var express = require('express');
+var router = express.Router();
+var bodyParser = require('body-parser');
+var expressValidator = require('express-validator');
+router.use(bodyParser.urlencoded({extended: true}));
+router.use(bodyParser.json());
+router.use(expressValidator());
+
+var child_process = require('child_process');
+
+/* GET home page. */
+router.get('/', function(req, res, next) {
+  res.render('index', { title: 'LAN Jukebox' });
+});
+
+router.get('/youtube', function(req, res, next) {
+  res.render('youtube', { title: 'LAN Jukebox - Add a Youtube Video' });
+});
+
+router.use(expressValidator({
+    customValidators: {
+	dlSuccess: function(video) {
+	    // Test if the requested video is available.
+	    var youtube_dl = child_process.spawnSync('/usr/bin/youtube-dl', ['-s', video]);
+	    console.log(youtube_dl.stderr.toString());
+	    if (youtube_dl.stderr.toString()) {
+		return false;
+	    }
+	    return true;
+	}
+    }
+ }
+));
+
+router.post('/youtube', function(req, res) {
+    var video = req.body.video;
+    
+    req.checkBody('video', 'URL is not valid.').dlSuccess();
+    
+    var errors = req.validationErrors();
+    
+    if(errors){
+	res.render('youtube', {
+	    title: 'LAN Jukebox - Add a Youtube Video',
+	    errors:errors
+	});
+    } else {
+	var youtube_dl = child_process.spawn('/usr/bin/youtube-dl', ['-x', '-o', 'downloads/%(title)s.%(ext)s', video]);
+	// TODO eval video
+	res.render('index', { title: 'LAN Jukebox' });
+    }
+});
+
+router.get('/file', function(req, res, next) {
+  res.render('file', { title: 'LAN Jukebox - Add a Music File' });
+});
+
+module.exports = router;

+ 9 - 0
routes/users.js

@@ -0,0 +1,9 @@
+var express = require('express');
+var router = express.Router();
+
+/* GET users listing. */
+router.get('/', function(req, res, next) {
+  res.send('respond with a resource');
+});
+
+module.exports = router;

+ 3 - 0
views/error.hbs

@@ -0,0 +1,3 @@
+<h1>{{message}}</h1>
+<h2>{{error.status}}</h2>
+<pre>{{error.stack}}</pre>

+ 15 - 0
views/file.hbs

@@ -0,0 +1,15 @@
+<a href="/">Back</a>
+<h1>{{title}}</h1>
+{{#if errors}}
+      {{#each errors}}
+        <div class="alert alert-danger">{{msg}}</div>
+      {{/each}}
+{{/if}}
+<label>Upload a file:</label><br/>
+<form method="post" class="upload">
+  <div class="form-group">
+    <input type="text" class="form-control" placeholder="TODO file upload" name="file">
+    <button type="submit" class="btn btn-default file-button">Submit</button>
+    <!-- TODO file size limit -->
+  </div>
+</form> 

+ 4 - 0
views/index.hbs

@@ -0,0 +1,4 @@
+<h1>{{title}}</h1>
+<a href="/youtube">Add a Youtube video</a><br/>
+<a href="/file">Add a music file</a><br/>
+<p>Allan put music player here</p>

+ 10 - 0
views/layout.hbs

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>{{title}}</title>
+    <link rel='stylesheet' href='/stylesheets/style.css' />
+  </head>
+  <body>
+    {{{body}}}
+  </body>
+</html>

+ 14 - 0
views/youtube.hbs

@@ -0,0 +1,14 @@
+<a href="/">Back</a>
+<h1>{{title}}</h1>
+{{#if errors}}
+      {{#each errors}}
+        <div class="alert alert-danger">{{msg}}</div>
+      {{/each}}
+{{/if}}
+<form method="post">
+  <div class="form-group">
+    <label>Add a video:</label><br/>
+    <input type="text" class="form-control" placeholder="Youtube URL" name="video">
+    <button type="submit" class="btn btn-default">Submit</button>
+  </div>
+</form>