├── README.md ├── sv.js ├── package.json ├── .gitignore └── lib └── index.js /README.md: -------------------------------------------------------------------------------- 1 | A NodeJS APP to download youtube videos 2 | -------------------------------------------------------------------------------- /sv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | let Yt = require('./lib/index'), 3 | teste = new Yt(); 4 | teste.playlistDownload('PLnaSPks1YwbBzxhos17Hg758Y_EB8Nzbk', './musics/'); 5 | 6 | 7 | teste 8 | .on('download', (title) => console.log('Download: ' + title)) 9 | .on('conversion', (title) => console.log('Conversion: ' + title)) 10 | .on('error',(err)=> console.log(err)) 11 | .on('finished', () => console.log('Everything is over now')); 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "youtube-to-mp3", 3 | "version": "0.1.1", 4 | "description": "A NodeJS APP to download youtube videos and convert them to mp3", 5 | "homepage": "igorsantana.co", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/opsigor/ops" 9 | }, 10 | "main": "lib/index.js", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [ 15 | "nodejs", 16 | "youtube", 17 | "mp3" 18 | ], 19 | "author": "Igor Santana", 20 | "license": "MIT", 21 | "dependencies": { 22 | "cheerio": "^0.19.0", 23 | "fluent-ffmpeg": "^2.0.1", 24 | "request": "^2.64.0", 25 | "ytdl-core": "^0.6.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/bower,node 2 | 3 | ### Bower ### 4 | bower_components 5 | .bower-cache 6 | .bower-registry 7 | .bower-tmp 8 | 9 | 10 | ### Node ### 11 | # Logs 12 | logs 13 | *.log 14 | npm-debug.log* 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directory 37 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 38 | node_modules 39 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | let fs = require('fs'), 3 | ytdl = require('ytdl-core'), 4 | ffmpeg = require('fluent-ffmpeg'), 5 | cheerio = require('cheerio'), 6 | request = require('request'), 7 | events = require('events'), 8 | numVideos = 0; 9 | 10 | function checkTypeAndPopulate(id) { 11 | let self = this; 12 | if (!id) { 13 | self.emit('error', 'You must pass a id to YTTOMP3'); 14 | return; 15 | } 16 | let variable = [], 17 | youtubeString = 'https://www.youtube.com/watch?v='; 18 | if (id.constructor === Array) { 19 | variable = id.map((val) => youtubeString + val); 20 | } else if (id.constructor === Object) { 21 | for (var key in id) 22 | if (id.hasOwnProperty(key)) { 23 | variable.push(youtubeString + id[key]); 24 | } 25 | } else { 26 | variable.push(youtubeString + id); 27 | } 28 | return variable; 29 | } 30 | 31 | function createSong(path, title, stream, bitrate) { 32 | let self = this; 33 | let x = new ffmpeg(stream) 34 | .audioBitrate(bitrate) 35 | .saveToFile(path + title + '.mp3') 36 | .on('error', (err) => self.emit('error', err)) 37 | .on('end', function() { 38 | numVideos--; 39 | self.emit('conversion', title); 40 | if (!numVideos) { 41 | self.emit('finished'); 42 | return; 43 | } 44 | }) 45 | } 46 | 47 | function playlistDownload(plID, path) { 48 | let url = 'http://www.youtube.com/playlist?list=' + plID, 49 | self = this; 50 | request(url, (err, res, html) => { 51 | let $ = cheerio.load(html), 52 | videos = $('tr[data-video-id]'), 53 | idVideos = []; 54 | for (let vid in videos) 55 | if (videos.hasOwnProperty(vid) && videos[vid].attribs) { 56 | idVideos.push(videos[vid].attribs['data-video-id']); 57 | } 58 | self.videoDownload(idVideos, path); 59 | }) 60 | } 61 | 62 | function videoDownload(ytID, path) { 63 | let _vid, 64 | options = { 65 | quality: 'highest', 66 | downloadURL: true, 67 | filter: 'audioonly' 68 | }, 69 | self = this; 70 | _vid = checkTypeAndPopulate(ytID); 71 | let streams = _vid.forEach((val) => { 72 | numVideos++; 73 | let _stream = ytdl(val, options); 74 | _stream 75 | .on('info', (info, format) => { 76 | fs.stat(path, (err, stats) => { 77 | if (err) { 78 | fs.mkdirSync(path); 79 | } 80 | self.emit('download', info.title); 81 | self.createSong(path, info.title, _stream, 192); 82 | }); 83 | }) 84 | .on('error', (err) => { 85 | self.emit('error', err); 86 | numVideos--; 87 | }); 88 | }) 89 | } 90 | 91 | function YoutubeToMp3() {}; 92 | YoutubeToMp3.prototype = new events.EventEmitter 93 | YoutubeToMp3.prototype.playlistDownload = playlistDownload; 94 | YoutubeToMp3.prototype.videoDownload = videoDownload; 95 | YoutubeToMp3.prototype.checkTypeAndPopulate = checkTypeAndPopulate; 96 | YoutubeToMp3.prototype.createSong = createSong; 97 | module.exports = YoutubeToMp3; 98 | --------------------------------------------------------------------------------