youtube-mp3-downloader
Advanced tools
Comparing version 0.6.8 to 0.7.0
@@ -1,177 +0,161 @@ | ||
"use strict"; | ||
var os = require("os"); | ||
var util = require("util"); | ||
var EventEmitter = require("events").EventEmitter; | ||
var ffmpeg = require("fluent-ffmpeg"); | ||
var ytdl = require("ytdl-core"); | ||
var async = require("async"); | ||
var progress = require("progress-stream"); | ||
var sanitize = require("sanitize-filename"); | ||
'use strict'; | ||
var os = require('os'); | ||
var util = require('util'); | ||
var EventEmitter = require('events').EventEmitter; | ||
var ffmpeg = require('fluent-ffmpeg'); | ||
var ytdl = require('ytdl-core'); | ||
var async = require('async'); | ||
var progress = require('progress-stream'); | ||
var sanitize = require('sanitize-filename'); | ||
function YoutubeMp3Downloader(options) { | ||
class YoutubeMp3Downloader extends EventEmitter { | ||
var self = this; | ||
constructor(options) { | ||
super(); | ||
this.youtubeBaseUrl = 'http://www.youtube.com/watch?v='; | ||
this.youtubeVideoQuality = (options && options.youtubeVideoQuality ? options.youtubeVideoQuality : 'highestaudio'); | ||
this.outputPath = options && options.outputPath ? options.outputPath : os.homedir(); | ||
this.queueParallelism = (options && options.queueParallelism ? options.queueParallelism : 1); | ||
this.progressTimeout = (options && options.progressTimeout ? options.progressTimeout : 1000); | ||
this.fileNameReplacements = [[/'/g, ''], [/\|/g, ''], [/'/g, ''], [/\//g, ''], [/\?/g, ''], [/:/g, ''], [/;/g, '']]; | ||
this.requestOptions = (options && options.requestOptions ? options.requestOptions : { maxRedirects: 5 }); | ||
this.outputOptions = (options && options.outputOptions ? options.outputOptions : []); | ||
self.youtubeBaseUrl = "http://www.youtube.com/watch?v="; | ||
self.youtubeVideoQuality = (options && options.youtubeVideoQuality ? options.youtubeVideoQuality : "highest"); | ||
self.outputPath = (options && options.outputPath ? options.outputPath : (os.platform() === "win32" ? "C:/Windows/Temp" : "/tmp")); | ||
self.queueParallelism = (options && options.queueParallelism ? options.queueParallelism : 1); | ||
self.progressTimeout = (options && options.progressTimeout ? options.progressTimeout : 1000); | ||
self.fileNameReplacements = [[/"/g, ""], [/\|/g, ""], [/'/g, ""], [/\//g, ""], [/\?/g, ""], [/:/g, ""], [/;/g, ""]]; | ||
self.requestOptions = (options && options.requestOptions ? options.requestOptions : { maxRedirects: 5 }); | ||
self.outputOptions = (options && options.outputOptions ? options.outputOptions : []); | ||
if (options && options.ffmpegPath) { | ||
ffmpeg.setFfmpegPath(options.ffmpegPath); | ||
} | ||
if (options && options.ffmpegPath) { | ||
ffmpeg.setFfmpegPath(options.ffmpegPath); | ||
this.setupQueue(); | ||
} | ||
//Async download/transcode queue | ||
self.downloadQueue = async.queue(function (task, callback) { | ||
setupQueue() { | ||
let self = this; | ||
//Async download/transcode queue | ||
this.downloadQueue = async.queue(function (task, callback) { | ||
self.emit("queueSize", self.downloadQueue.running() + self.downloadQueue.length()); | ||
self.emit('queueSize', self.downloadQueue.running() + self.downloadQueue.length()); | ||
self.performDownload(task, function(err, result) { | ||
callback(err, result); | ||
}); | ||
self.performDownload(task, function(err, result) { | ||
callback(err, result); | ||
}); | ||
}, self.queueParallelism); | ||
}, self.queueParallelism); | ||
} | ||
} | ||
cleanFileName (fileName) { | ||
this.fileNameReplacements.forEach(function(replacement) { | ||
fileName = fileName.replace(replacement[0], replacement[1]); | ||
}); | ||
return fileName; | ||
}; | ||
util.inherits(YoutubeMp3Downloader, EventEmitter); | ||
YoutubeMp3Downloader.prototype.cleanFileName = function(fileName) { | ||
var self = this; | ||
self.fileNameReplacements.forEach(function(replacement) { | ||
fileName = fileName.replace(replacement[0], replacement[1]); | ||
}); | ||
return fileName; | ||
}; | ||
YoutubeMp3Downloader.prototype.download = function(videoId, fileName) { | ||
var self = this; | ||
var task = { | ||
videoId: videoId, | ||
fileName: fileName | ||
download = function(videoId, fileName) { | ||
let self = this; | ||
const task = { | ||
videoId: videoId, | ||
fileName: fileName | ||
}; | ||
this.downloadQueue.push(task, function (err, data) { | ||
self.emit('queueSize', self.downloadQueue.running() + self.downloadQueue.length()); | ||
if (err) { | ||
self.emit('error', err, data); | ||
} else { | ||
self.emit('finished', err, data); | ||
} | ||
}); | ||
}; | ||
self.downloadQueue.push(task, function (err, data) { | ||
self.emit("queueSize", self.downloadQueue.running() + self.downloadQueue.length()); | ||
if (err) { | ||
self.emit("error", err, data); | ||
async performDownload(task, callback) { | ||
let self = this; | ||
const videoUrl = this.youtubeBaseUrl+task.videoId; | ||
let resultObj = { | ||
videoId: task.videoId | ||
}; | ||
const info = await ytdl.getInfo(videoUrl, { quality: this.youtubeVideoQuality }) | ||
var videoTitle = this.cleanFileName(info.videoDetails.title); | ||
var artist = 'Unknown'; | ||
var title = 'Unknown'; | ||
var thumbnail = info.videoDetails.thumbnail.thumbnails[0].url || null; | ||
if (videoTitle.indexOf('-') > -1) { | ||
var temp = videoTitle.split('-'); | ||
if (temp.length >= 2) { | ||
artist = temp[0].trim(); | ||
title = temp[1].trim(); | ||
} | ||
} else { | ||
self.emit("finished", err, data); | ||
title = videoTitle; | ||
} | ||
}); | ||
}; | ||
//Derive file name, if given, use it, if not, from video title | ||
const fileName = (task.fileName ? self.outputPath + '/' + task.fileName : self.outputPath + '/' + (sanitize(videoTitle) || info.videoId) + '.mp3'); | ||
YoutubeMp3Downloader.prototype.performDownload = function(task, callback) { | ||
//Stream setup | ||
const stream = ytdl.downloadFromInfo(info, { | ||
quality: self.youtubeVideoQuality, | ||
requestOptions: self.requestOptions | ||
}); | ||
var self = this; | ||
var videoUrl = self.youtubeBaseUrl+task.videoId; | ||
var resultObj = { | ||
videoId: task.videoId | ||
}; | ||
stream.on('response', function(httpResponse) { | ||
ytdl.getInfo(videoUrl, function(err, info){ | ||
//Setup of progress module | ||
const str = progress({ | ||
length: parseInt(httpResponse.headers['content-length']), | ||
time: self.progressTimeout | ||
}); | ||
if (err) { | ||
callback(err.message, resultObj); | ||
} else { | ||
// Map new structure to old one | ||
info = info.player_response.videoDetails; | ||
var videoTitle = self.cleanFileName(info.title); | ||
var artist = "Unknown"; | ||
var title = "Unknown"; | ||
var thumbnail = info.thumbnail.thumbnails[0].url || null; | ||
if (videoTitle.indexOf("-") > -1) { | ||
var temp = videoTitle.split("-"); | ||
if (temp.length >= 2) { | ||
artist = temp[0].trim(); | ||
title = temp[1].trim(); | ||
//Add progress event listener | ||
str.on('progress', function(progress) { | ||
if (progress.percentage === 100) { | ||
resultObj.stats= { | ||
transferredBytes: progress.transferred, | ||
runtime: progress.runtime, | ||
averageSpeed: parseFloat(progress.speed.toFixed(2)) | ||
} | ||
} | ||
} else { | ||
title = videoTitle; | ||
self.emit('progress', {videoId: task.videoId, progress: progress}) | ||
}); | ||
let outputOptions = [ | ||
'-id3v2_version', '4', | ||
'-metadata', 'title=' + title, | ||
'-metadata', 'artist=' + artist | ||
]; | ||
if (self.outputOptions) { | ||
outputOptions = outputOptions.concat(self.outputOptions); | ||
} | ||
//Start encoding | ||
const proc = new ffmpeg({ | ||
source: stream.pipe(str) | ||
}) | ||
.audioBitrate(info.formats[0].audioBitrate) | ||
.withAudioCodec('libmp3lame') | ||
.toFormat('mp3') | ||
.outputOptions(...outputOptions) | ||
.on('error', function(err) { | ||
callback(err.message, null); | ||
}) | ||
.on('end', function() { | ||
resultObj.file = fileName; | ||
resultObj.youtubeUrl = videoUrl; | ||
resultObj.videoTitle = videoTitle; | ||
resultObj.artist = artist; | ||
resultObj.title = title; | ||
resultObj.thumbnail = thumbnail; | ||
callback(null, resultObj); | ||
}) | ||
.saveToFile(fileName); | ||
//Derive file name, if given, use it, if not, from video title | ||
var fileName = (task.fileName ? self.outputPath + "/" + task.fileName : self.outputPath + "/" + (sanitize(videoTitle) || info.videoId) + ".mp3"); | ||
}); | ||
}; | ||
ytdl.getInfo(videoUrl, { quality: self.youtubeVideoQuality }, function(err, info) { | ||
} | ||
if (err) callback(err, null); | ||
//Stream setup | ||
var stream = ytdl.downloadFromInfo(info, { | ||
quality: self.youtubeVideoQuality, | ||
requestOptions: self.requestOptions | ||
}); | ||
stream.on("response", function(httpResponse) { | ||
//Setup of progress module | ||
var str = progress({ | ||
length: parseInt(httpResponse.headers["content-length"]), | ||
time: self.progressTimeout | ||
}); | ||
//Add progress event listener | ||
str.on("progress", function(progress) { | ||
if (progress.percentage === 100) { | ||
resultObj.stats= { | ||
transferredBytes: progress.transferred, | ||
runtime: progress.runtime, | ||
averageSpeed: parseFloat(progress.speed.toFixed(2)) | ||
} | ||
} | ||
self.emit("progress", {videoId: task.videoId, progress: progress}) | ||
}); | ||
var outputOptions = [ | ||
"-id3v2_version", "4", | ||
"-metadata", "title=" + title, | ||
"-metadata", "artist=" + artist | ||
]; | ||
if (self.outputOptions) { | ||
outputOptions = outputOptions.concat(self.outputOptions); | ||
} | ||
//Start encoding | ||
var proc = new ffmpeg({ | ||
source: stream.pipe(str) | ||
}) | ||
.audioBitrate(info.formats[0].audioBitrate) | ||
.withAudioCodec("libmp3lame") | ||
.toFormat("mp3") | ||
.outputOptions(...outputOptions) | ||
.on("error", function(err) { | ||
callback(err.message, null); | ||
}) | ||
.on("end", function() { | ||
resultObj.file = fileName; | ||
resultObj.youtubeUrl = videoUrl; | ||
resultObj.videoTitle = videoTitle; | ||
resultObj.artist = artist; | ||
resultObj.title = title; | ||
resultObj.thumbnail = thumbnail; | ||
callback(null, resultObj); | ||
}) | ||
.saveToFile(fileName); | ||
}); | ||
}); | ||
} | ||
}); | ||
}; | ||
module.exports = YoutubeMp3Downloader; |
{ | ||
"name": "youtube-mp3-downloader", | ||
"version": "0.6.8", | ||
"version": "0.7.0", | ||
"description": "Downloads Youtube videos (in parallel, as streams), encodes the audio data as mp3 and stores them in a defineable location", | ||
@@ -25,3 +25,3 @@ "keywords": [ | ||
"sanitize-filename": "^1.6.3", | ||
"ytdl-core": "^2.1.7" | ||
"ytdl-core": "^3.1.2" | ||
}, | ||
@@ -28,0 +28,0 @@ "author": { |
@@ -19,3 +19,3 @@ # Youtube MP3 Downloader | ||
`git clone https://github.com/tobilg/youtube-mp3-downloader.git` | ||
`git clone https://github.com/ytb2mp3/youtube-mp3-downloader.git` | ||
@@ -37,7 +37,7 @@ #### Install module dependencies | ||
var YD = new YoutubeMp3Downloader({ | ||
"ffmpegPath": "/path/to/ffmpeg", // Where is the FFmpeg binary located? | ||
"outputPath": "/path/to/mp3/folder", // Where should the downloaded and encoded files be stored? | ||
"youtubeVideoQuality": "highest", // What video quality should be used? | ||
"queueParallelism": 2, // How many parallel downloads/encodes should be started? | ||
"progressTimeout": 2000 // How long should be the interval of the progress reports | ||
"ffmpegPath": "/path/to/ffmpeg", // FFmpeg binary location | ||
"outputPath": "/path/to/mp3/folder", // Output file location (default: the home directory) | ||
"youtubeVideoQuality": "highestaudio", // Desired video quality (default: highestaudio) | ||
"queueParallelism": 2, // Download parallelism (default: 1) | ||
"progressTimeout": 2000 // Interval in ms for the progress reports (default: 1000) | ||
}); | ||
@@ -125,7 +125,7 @@ | ||
self.YD = new YoutubeMp3Downloader({ | ||
"ffmpegPath": "/path/to/ffmpeg", // Where is the FFmpeg binary located? | ||
"outputPath": "/path/to/mp3/folder", // Where should the downloaded and encoded files be stored? | ||
"youtubeVideoQuality": "highest", // What video quality should be used? | ||
"queueParallelism": 2, // How many parallel downloads/encodes should be started? | ||
"progressTimeout": 2000, // How long should be the interval of the progress reports | ||
"ffmpegPath": "/path/to/ffmpeg", // FFmpeg binary location | ||
"outputPath": "/path/to/mp3/folder", // Output file location (default: the home directory) | ||
"youtubeVideoQuality": "highestaudio", // Desired video quality (default: highestaudio) | ||
"queueParallelism": 2, // Download parallelism (default: 1) | ||
"progressTimeout": 2000 // Interval in ms for the progress reports (default: 1000) | ||
"outputOptions" : ["-af", "silenceremove=1:0:-50dB"] // Additional output options passend to ffmpeg | ||
@@ -132,0 +132,0 @@ }); |
15155
171
+ Addedminiget@2.1.0(transitive)
+ Addedytdl-core@3.4.2(transitive)
- Removedytdl-core@2.1.7(transitive)
Updatedytdl-core@^3.1.2