fluent-ffmpeg
Advanced tools
Comparing version 0.0.19 to 0.1.0
@@ -0,0 +0,0 @@ |
@@ -0,0 +0,0 @@ |
@@ -0,0 +0,0 @@ Number.prototype.gcd = function(b) { |
@@ -45,2 +45,7 @@ var fs = require('fs'), | ||
var modname = file.substring(0, file.length - 3); | ||
if (file.substring(0, 1) == '.') { | ||
return true; | ||
} | ||
var preset = require('./presets/' + modname); | ||
@@ -47,0 +52,0 @@ if (typeof preset.load == 'function') { |
var exec = require('child_process').exec; | ||
module.exports = { | ||
var meta = module.exports = { | ||
escapedPath: function(path) { | ||
if(/http/.exec(path)) { | ||
path = path.replace(' ', '%20'); | ||
} else { | ||
path = '"' + path + '"'; | ||
} | ||
return path; | ||
}, | ||
get: function(inputfile, callback) { | ||
try | ||
{ | ||
exec('ffmpeg -i ' + inputfile, function(err, stdout, stderr) { | ||
inputfile = meta.escapedPath(inputfile); | ||
exec(process.env.FFMPEG_PATH || 'ffmpeg' + ' -i ' + inputfile, function(err, stdout, stderr) { | ||
// parse data from stderr | ||
var aspect = /(4|3|16):(3|2|9|10)/.exec(stderr); | ||
var bitrate = /bitrate: ([0-9]+) kb\/s/.exec(stderr); | ||
var aspect = /DAR ([0-9\:]+)/.exec(stderr); | ||
var video_bitrate = /bitrate: ([0-9]+) kb\/s/.exec(stderr); | ||
var fps = /([0-9\.]+) (fps|tb\(r\))/.exec(stderr); | ||
var container = /Input #0, ([a-zA-Z0-9]+),/.exec(stderr); | ||
var title = /title *: ([^\n]+)/.exec(stderr); | ||
var video_stream = /Stream #([0-9\.]+)([a-z0-9\(\)\[\]]*)[:] Video/.exec(stderr); | ||
var video_codec = /Video: ([\w]+)/.exec(stderr); | ||
var duration = /Duration: (([0-9]+):([0-9]{2}):([0-9]{2}).([0-9]+))/.exec(stderr); | ||
var resolution = /(([0-9]{2,5})x([0-9]{2,5}))/.exec(stderr) | ||
var audio_bitrate = /Audio: [\w, ]+, ([0-9]+) kb\/s/.exec(stderr); | ||
var sample_rate = /([0-9]+) Hz/i.exec(stderr); | ||
var audio_codec = /Audio: ([\w]+)/.exec(stderr); | ||
var channels = /Audio: [\w]+, [0-9]+ Hz, ([a-z0-9:]+)[a-z0-9\/,]*/.exec(stderr); | ||
var audio_stream = /Stream #([0-9\.]+)([a-z0-9\(\)\[\]]*)[:] Audio/.exec(stderr); | ||
var is_synched = (/start: 0.000000/.exec(stderr) != null); | ||
// build return object | ||
var ret = { | ||
aspect: (aspect && aspect.length > 0) ? aspect[0] : '', | ||
durationraw: (duration && duration.length > 1) ? duration[1] : '', | ||
bitrate: (bitrate && bitrate.length > 1) ? bitrate[1] : '', | ||
durationraw: (duration && duration.length > 1) ? duration[1] : '', | ||
title: (title && title.length > 1) ? title[1] : null, | ||
synched: is_synched, | ||
video: { | ||
container: (container && container.length > 0) ? container[1] : '', | ||
bitrate: (video_bitrate && video_bitrate.length > 1) ? parseInt(video_bitrate[1]) : 0, | ||
codec: (video_codec && video_codec.length > 1) ? video_codec[1] : '', | ||
resolution: { | ||
w: (resolution && resolution.length > 2) ? resolution[2] : 0, | ||
h: (resolution && resolution.length > 3) ? resolution[3] : 0 | ||
} | ||
w: (resolution && resolution.length > 2) ? parseInt(resolution[2]) : 0, | ||
h: (resolution && resolution.length > 3) ? parseInt(resolution[3]) : 0 | ||
}, | ||
fps: (fps && fps.length > 1) ? parseFloat(fps[1]) : 0.0, | ||
stream: (video_stream && video_stream.length > 1) ? parseFloat(video_stream[1]) : 0.0 | ||
}, | ||
audio: { | ||
codec: (audio_codec && audio_codec.length > 1) ? audio_codec[1] : '', | ||
bitrate: (audio_bitrate && audio_bitrate.length > 1) ? parseInt(audio_bitrate[1]) : 0, | ||
sample_rate: (sample_rate && sample_rate.length > 1) ? parseInt(sample_rate[1]) : 0, | ||
stream: (audio_stream && audio_stream.length > 1) ? parseFloat(audio_stream[1]) : 0.0 | ||
} | ||
}; | ||
// calculate duration in seconds | ||
@@ -37,3 +70,20 @@ if (duration && duration.length > 1) { | ||
} | ||
if (channels && channels.length > 1) { | ||
if (channels[1] == "stereo") ret.audio.channels = 2; | ||
else if (channels[1] == "mono") ret.audio.channels = 1; | ||
else ret.audio.channels = 0; | ||
} | ||
if (aspect && aspect.length > 0) { | ||
var n = aspect[1].split(":"); | ||
ret.video.aspect = parseFloat((parseInt(n[0]) / parseInt(n[1])).toFixed(2)); | ||
} else { | ||
if(ret.video.resolution.w != 0) { | ||
ret.video.aspect = parseFloat((ret.video.resolution.w / ret.video.resolution.h).toFixed(2)); | ||
} else { | ||
ret.video.aspect = 0.0; | ||
} | ||
} | ||
callback(ret); | ||
@@ -40,0 +90,0 @@ }); |
@@ -5,12 +5,16 @@ var fs = require('fs'), | ||
exec = require('child_process').exec, | ||
spawn = require('child_process').spawn; | ||
spawn = require('child_process').spawn, | ||
Meta = require('./metadata.js'); | ||
exports = module.exports = function Processor(command) { | ||
// constant for timeout checks | ||
this.E_PROCESSTIMEOUT = -99; | ||
this.saveToFile = function(targetfile, callback) { | ||
this.options.outputfile = targetfile; | ||
this.options.outputfile = targetfile; | ||
var self = this; | ||
// parse options to command | ||
this._prepare(function(err, meta) { | ||
this._prepare(function(err, meta) { | ||
if (err) { | ||
@@ -20,10 +24,9 @@ callback(null, null, err); | ||
var args = self.buildFfmpegArgs(false); | ||
// start conversion of file using spawn | ||
var ffmpegProc; | ||
var ffmpegProc = self._spawnProcess(args); | ||
if (self.options.inputstream) { | ||
// pump input stream to stdin | ||
ffmpegProc = self._spawnProcess(args, { customFds: [self.options.inputstream.fd, -1, -1] }); | ||
} else { | ||
ffmpegProc = self._spawnProcess(args); | ||
// pump input stream to stdin | ||
self.options.inputstream.resume(); | ||
self.options.inputstream.pipe(ffmpegProc.stdin); | ||
} | ||
@@ -35,11 +38,12 @@ | ||
processTimer = setTimeout(function() { | ||
ffmpegProc.removeAllListeners('exit'); | ||
ffmpegProc.kill('SIGKILL'); | ||
callback(-1, 'timeout'); | ||
callback(self.E_PROCESSTIMEOUT, 'timeout'); | ||
}, self.options.timeout); | ||
} | ||
var stdout = ''; | ||
var stderr = ''; | ||
ffmpegProc.on('exit', function(code) { | ||
if (processTimer) clearTimeout(processTimer); | ||
if (processTimer) clearTimeout(processTimer); | ||
// check if we have to run flvtool2 to update flash video meta data | ||
@@ -62,9 +66,9 @@ if (self.options._updateFlvMetadata === true) { | ||
}); | ||
ffmpegProc.stdout.on('data', function (data) { | ||
stdout += data; | ||
}); | ||
ffmpegProc.stderr.on('data', function (data) { | ||
stderr += data; | ||
stderr += data; | ||
}); | ||
@@ -74,7 +78,7 @@ } | ||
}; | ||
this.writeToStream = function(stream, callback) { | ||
this.writeToStream = function(stream, callback) { | ||
if (!this.options._isStreamable) { | ||
callback(null, new Error('selected output format is not streamable')); | ||
} else { | ||
} else { | ||
var self = this; | ||
@@ -85,15 +89,13 @@ // parse options to command | ||
callback(null, err); | ||
} else { | ||
} else { | ||
var args = self.buildFfmpegArgs(true); | ||
// write data to stdout | ||
args.push('pipe:1'); | ||
// start conversion of file using spawn | ||
var ffmpegProc; | ||
var ffmpegProc = self._spawnProcess(args, { customFds: [-1, stream.fd, -1] }); | ||
if (self.options.inputstream) { | ||
// pump input stream to stdin | ||
self.options.inputstream.resume(); | ||
ffmpegProc = self._spawnProcess(args, { customFds: [self.options.inputstream.fd, stream.fd, -1] }); | ||
} else { | ||
ffmpegProc = self._spawnProcess(args, { customFds: [-1, stream.fd, -1] }); | ||
self.options.inputstream.pipe(ffmpegProc.stdin); | ||
} | ||
@@ -105,20 +107,41 @@ | ||
processTimer = setTimeout(function() { | ||
ffmpegProc.removeAllListeners('exit'); | ||
ffmpegProc.kill('SIGKILL'); | ||
callback(-1, 'timeout'); | ||
callback(self.E_PROCESSTIMEOUT, 'timeout'); | ||
}, self.options.timeout); | ||
} | ||
var stderr = ''; | ||
ffmpegProc.stderr.on('data', function(data) { | ||
stderr += data; | ||
}); | ||
ffmpegProc.stdout.on('data', function(chunk) { | ||
stream.write(chunk); | ||
}); | ||
ffmpegProc.on('exit', function(code, signal) { | ||
if (processTimer) clearTimeout(processTimer); | ||
// close file descriptor on outstream | ||
fs.closeSync(stream.fd); | ||
if (self.options.inputstream) | ||
fs.closeSync(self.options.inputstream.fd); | ||
callback(code, stderr); | ||
if(/http/.exec(self.options.inputfile)) { | ||
callback(code, stderr); | ||
} else { | ||
var cb_ = function() { | ||
if (self.options.inputstream) { | ||
fs.close(self.options.inputstream.fd, function() { | ||
callback(code, stderr); | ||
}); | ||
} | ||
else { | ||
callback(code, stderr); | ||
} | ||
}; | ||
if ( !stream.fd ) { | ||
stream.end ? stream.end() : callback(code, "stream will not be closed"); | ||
cb_(); | ||
} else { | ||
fs.close(stream.fd, cb_); | ||
} | ||
} | ||
}); | ||
@@ -129,3 +152,28 @@ } | ||
}; | ||
this.takeScreenshot = function(config, callback) { | ||
if (!this.options.video.size) { | ||
callback(new Error("set size of thumbnails using 'withSize' method")); | ||
} | ||
var self = this; | ||
var input = Meta.escapedPath(this.options.inputfile); | ||
var output = Meta.escapedPath(config.outputfile); | ||
// build screenshot command | ||
var tnArgs = [ | ||
'-ss', config.offset, | ||
'-i', input, | ||
'-vcodec', 'mjpeg', | ||
'-vframes', '1', | ||
'-an', | ||
'-f', 'rawvideo', | ||
'-s', self.options.video.size, | ||
'-y', output | ||
]; | ||
exec(process.env.FFMPEG_PATH || 'ffmpeg' + ' ' + tnArgs.join(' '), callback); | ||
} | ||
this.takeScreenshots = function(config, folder, callback) { | ||
@@ -147,5 +195,5 @@ var timemarks, screenshotcount; | ||
} | ||
var self = this; | ||
// check target folder | ||
// check target folder | ||
if (!path.existsSync(folder)) { | ||
@@ -162,3 +210,3 @@ fs.mkdir(folder, '0755', function(err) { | ||
} | ||
// read metadata from file | ||
@@ -186,5 +234,5 @@ function _screenShotInternal(callback) { | ||
var series = []; | ||
var i = 1; | ||
var i = 1; | ||
// use async helper function to generate all screenshots and | ||
@@ -201,11 +249,13 @@ // fire callback just once after work is done | ||
offset = timemarks[(i - 1)]; | ||
} else { | ||
} else { | ||
offset = secondOffset * i; | ||
} | ||
var target = folder + '/tn_' + offset + 's.jpg'; | ||
var target = Meta.escapedPath(folder + '/tn_' + offset + 's.jpg'); | ||
var input = Meta.escapedPath(this.options.inputfile); | ||
// build screenshot command | ||
var tnArgs = [ | ||
'-i', self.options.inputfile, | ||
'-ss', offset, | ||
'-i', input, | ||
'-vcodec', 'mjpeg', | ||
@@ -218,10 +268,10 @@ '-vframes', '1', | ||
]; | ||
i++; | ||
if (self.options._nice.level) { | ||
// execute ffmpeg through nice | ||
exec('nice --adjustment="' + self.options._nice.level + '" ffmpeg ' + tnArgs.join(' '), taskcallback); | ||
} else { | ||
exec('ffmpeg ' + tnArgs.join(' '), taskcallback); | ||
} else { | ||
exec(process.env.FFMPEG_PATH || 'ffmpeg' + ' ' + tnArgs.join(' '), taskcallback); | ||
} | ||
@@ -239,5 +289,5 @@ }, | ||
}; | ||
this._spawnProcess = function(args, options) { | ||
var retProc = spawn('ffmpeg', args, options); | ||
var retProc = spawn(process.env.FFMPEG_PATH || 'ffmpeg', args, options); | ||
if (this.options._nice.level) { | ||
@@ -252,6 +302,11 @@ var niceLvl = (this.options._nice.level > 0 ? '+' + this.options._nice.level : this.options._nice.level); | ||
}; | ||
this.buildFfmpegArgs = function(overrideOutputCheck) { | ||
var args = []; | ||
// add startoffset and duration | ||
if (this.options.starttime) { | ||
args.push('-ss', this.options.starttime); | ||
} | ||
// add input file (if using fs mode) | ||
@@ -261,8 +316,12 @@ if (this.options.inputfile && !this.options.inputstream) { | ||
{ | ||
var fstats = fs.statSync(this.options.inputfile); | ||
if (fstats.isFile()) { | ||
// fix for spawn call with path containing spaces | ||
args.push('-i', this.options.inputfile.replace(' ', '\ ')); | ||
if(/http/.exec(this.options.inputfile)) { | ||
args.push('-i', this.options.inputfile.replace(' ', '%20')); | ||
} else { | ||
throw new Error('input file is not readable'); | ||
var fstats = fs.statSync(this.options.inputfile); | ||
if (fstats.isFile()) { | ||
// fix for spawn call with path containing spaces | ||
args.push('-i', this.options.inputfile.replace(' ', '\ ')); | ||
} else { | ||
throw new Error('input file is not readable'); | ||
} | ||
} | ||
@@ -278,11 +337,7 @@ } catch (err) { | ||
} | ||
// add startoffset and duration | ||
if (this.options.starttime) { | ||
args.push('-ss', this.options.starttime); | ||
} | ||
if (this.options.duration) { | ||
args.push('-t', this.options.duration); | ||
} | ||
// add format | ||
@@ -292,3 +347,3 @@ if (this.options.format) { | ||
} | ||
// add video options | ||
@@ -316,3 +371,3 @@ if (this.options.video.bitrate) { | ||
} | ||
// add video options | ||
@@ -331,3 +386,3 @@ if (this.options.audio.bitrate) { | ||
} | ||
// add additional options | ||
@@ -350,3 +405,3 @@ if (this.options.additional) { | ||
} | ||
// add size and output file | ||
@@ -356,4 +411,4 @@ if (this.options.video.size) { | ||
} | ||
if (this.options.outputfile) { | ||
@@ -366,5 +421,5 @@ args.push('-y', this.options.outputfile.replace(' ', '\ ')); | ||
} | ||
return args; | ||
}; | ||
}; | ||
}; |
{ | ||
"name": "fluent-ffmpeg", | ||
"version": "0.0.19", | ||
"version": "0.1.0", | ||
"description": "A fluent API to FFMPEG (http://www.ffmpeg.org)", | ||
@@ -5,0 +5,0 @@ "keywords": [ "ffmpeg" ], |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
41294
905
0
13
8