├── views ├── error.hbs ├── layout.hbs ├── index.hbs └── listvideo.hbs ├── routes ├── users.js └── index.js ├── package.json ├── readme.md ├── .gitignore ├── public └── stylesheets │ └── style.css ├── LICENSE ├── app.js └── bin └── www /views/error.hbs: -------------------------------------------------------------------------------- 1 |

{{message}}

2 |

{{error.status}}

3 |
{{error.stack}}
4 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "youtube-downloader-app", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "start": "node ./bin/www" 6 | }, 7 | "dependencies": { 8 | "body-parser": "~1.15.1", 9 | "cookie-parser": "~1.4.3", 10 | "debug": "~2.2.0", 11 | "express": "~4.13.4", 12 | "hbs": "~4.0.0", 13 | "morgan": "~1.7.0", 14 | "request": "^2.79.0", 15 | "serve-favicon": "~2.3.0", 16 | "youtube-dl": "^1.11.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Youtube Downloader Web App 2 | ----------------------------- 3 | Working youtube downloader app. Uses [youtube-dl](https://rg3.github.io/youtube-dl/) and [node-youtube-dl](https://github.com/fent/node-youtube-dl) wrapper. 4 | 5 | #### How to run 6 | 1. Make sure you've have nodejs installed on your system. 7 | 2. Then do `npm install` and `npm start`. It will automatically download *youtube-dl* if it's not exist on your system. 8 | 9 | Tutorial found at: https://hackprogramming.com/how-to-create-your-own-youtube-downloader-web-app-using-youtube-dl-in-expressjs/ 10 | -------------------------------------------------------------------------------- /views/layout.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{title}} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{{body}}} 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /views/index.hbs: -------------------------------------------------------------------------------- 1 |
2 |

Youtube Downloader

3 |

Download any youtube video in multiple formats and quality with fast download

4 |
5 | 6 |
7 | 8 |
9 | 10 |
11 | 12 |
13 | 14 | 15 |
16 | 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: 'Open Sans', 'Lucida Grande', sans-serif; 4 | } 5 | 6 | ::-webkit-input-placeholder { /* Chrome/Opera/Safari */ 7 | color: rgba(0,0,0,.4)!important; 8 | } 9 | ::-moz-placeholder { /* Firefox 19+ */ 10 | color: rgba(0,0,0,.4)!important; 11 | } 12 | :-ms-input-placeholder { /* IE 10+ */ 13 | color: rgba(0,0,0,.4)!important; 14 | } 15 | 16 | .site-head { 17 | padding: 1.4rem 2rem; 18 | text-align: center; 19 | margin-top: 6rem; 20 | } 21 | .site-head .title { 22 | position: relative; 23 | display: inline-block; 24 | font-family: 'Bungee Shade', cursive; 25 | } 26 | .site-head .title:before { 27 | content: ""; 28 | position: absolute; 29 | width: 70px; height: 6px; 30 | background: rgba(144, 232, 24, 1); 31 | bottom: -10px; 32 | left: 6px; 33 | } 34 | 35 | .content-wrap { 36 | padding: 1rem; 37 | } 38 | .down-form input{ 39 | border: 2px solid #efefef; 40 | } 41 | .down-form input:focus { 42 | border-color: rgba(104, 202, 24, .4); 43 | } 44 | -------------------------------------------------------------------------------- /views/listvideo.hbs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | ← Back To Homepage 5 |
6 | 7 | {{#if error}} 8 |

{{error}}

9 | {{else}} 10 |
11 | 12 |
13 | 14 |
15 |

Download Links (multiple sizes and formats)

16 | 25 |
26 | {{/if}} 27 |
28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ashish Rawat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index'); 9 | var users = require('./routes/users'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'hbs'); 16 | 17 | // uncomment after placing your favicon in /public 18 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 19 | app.use(logger('dev')); 20 | app.use(bodyParser.json()); 21 | app.use(bodyParser.urlencoded({ extended: false })); 22 | app.use(cookieParser()); 23 | app.use(express.static(path.join(__dirname, 'public'))); 24 | 25 | app.use('/', routes); 26 | app.use('/users', users); 27 | 28 | // catch 404 and forward to error handler 29 | app.use(function(req, res, next) { 30 | var err = new Error('Not Found'); 31 | err.status = 404; 32 | next(err); 33 | }); 34 | 35 | // error handlers 36 | 37 | // development error handler 38 | // will print stacktrace 39 | if (app.get('env') === 'development') { 40 | app.use(function(err, req, res, next) { 41 | res.status(err.status || 500); 42 | res.render('error', { 43 | message: err.message, 44 | error: err 45 | }); 46 | }); 47 | } 48 | 49 | // production error handler 50 | // no stacktraces leaked to user 51 | app.use(function(err, req, res, next) { 52 | res.status(err.status || 500); 53 | res.render('error', { 54 | message: err.message, 55 | error: {} 56 | }); 57 | }); 58 | 59 | 60 | module.exports = app; 61 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('ytdown:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var ytdl = require('youtube-dl'); 4 | var request = require('request'); 5 | 6 | /* GET home page. */ 7 | router.get('/', function(req, res, next) { 8 | res.render('index', { title: 'Youtube Downloader Web App' }); 9 | }); 10 | 11 | // convert to human readable format 12 | function bytesToSize(bytes) { 13 | var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 14 | if (bytes == 0) return '0 Byte'; 15 | var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); 16 | return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; 17 | }; 18 | 19 | 20 | router.post('/video', function(req, res, next) { 21 | var url = req.body.url, 22 | formats = [], 23 | pattern = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; 24 | 25 | request.get(url, function (err, resp, body) { 26 | // check if it is valid url 27 | if(pattern.test(resp.request.uri.href)) { 28 | ytdl.getInfo(url, ['--youtube-skip-dash-manifest'], function(err, info) { 29 | if(err) return res.render('listvideo', {error: 'The link you provided either not a valid url or it is not acceptable'}); 30 | 31 | // push all video formats for download (skipping audio) 32 | info.formats.forEach(function(item) { 33 | if(item.format_note !== 'DASH audio' && item.filesize) { 34 | item.filesize = item.filesize ? bytesToSize(item.filesize): 'unknown'; 35 | formats.push(item); 36 | } 37 | }); 38 | res.render('listvideo', {meta: {id: info.id, formats: formats}}); 39 | }) 40 | } 41 | else { 42 | res.render('listvideo', {error: 'The link you provided either not a valid url or it is not acceptable'}); 43 | } 44 | }); 45 | 46 | 47 | 48 | }) 49 | 50 | 51 | module.exports = router; 52 | --------------------------------------------------------------------------------