added extract images and other changes

This commit is contained in:
Sami Salkosuo 2020-04-21 12:15:01 +03:00
parent 4ac2c5c515
commit 7f5090a13e
7 changed files with 208 additions and 20 deletions

View File

@ -7,7 +7,6 @@ const logger = require('./utils/logger.js');
const constants = require('./constants.js');
fileSizeLimit = constants.fileSizeLimit;
port = 3000;
timeout = 3600000;
// catch SIGINT and SIGTERM and exit
@ -45,7 +44,7 @@ require('express-readme')(app, {
});
const server = app.listen(port, function() {
const server = app.listen(constants.serverPort, function() {
let host = server.address().address;
let port = server.address().port;
logger.info('listening http://'+host+':'+port)

View File

@ -1,2 +1,4 @@
exports.fileSizeLimit = parseInt(process.env.FILE_SIZE_LIMIT_BYTES || "536870912") //536870912 = 512MB
exports.fileSizeLimit = parseInt(process.env.FILE_SIZE_LIMIT_BYTES || "536870912"); //536870912 = 512MB
exports.defaultFFMPEGProcessPriority=10;
exports.serverPort = 3000;//port to listen, NOTE: if using Docker/Kubernetes this port may not be the one clients are using

View File

@ -18,7 +18,8 @@
"express-list-endpoints": "4.0.1",
"fs": "0.0.1-security",
"unique-filename": "^1.1.1",
"winston": "^3.2.1"
"winston": "^3.2.1",
"archiver": "^4.0.1"
},
"devDependencies": {
"eslint": "^6.8.0",

View File

@ -2,6 +2,8 @@ var express = require('express')
const fs = require('fs');
const ffmpeg = require('fluent-ffmpeg');
const constants = require('../constants.js');
var router = express.Router()
const logger = require('../utils/logger.js')
@ -76,25 +78,24 @@ function convert(req,res,next) {
//ffmpeg processing... converting file...
let ffmpegConvertCommand = ffmpeg(savedFile);
ffmpegConvertCommand
.renice(15)
.renice(constants.defaultFFMPEGProcessPriority)
.outputOptions(ffmpegParams.outputOptions)
.on('error', function(err) {
logger.error(`${err}`);
fs.unlinkSync(savedFile);
utils.deleteFile(savedFile);
res.writeHead(500, {'Connection': 'close'});
res.end(JSON.stringify({error: `${err}`}));
})
.on('end', function() {
fs.unlinkSync(savedFile);
utils.deleteFile(savedFile);
logger.debug(`starting download to client ${savedFile}`);
res.download(outputFile, null, function(err) {
if (err) {
logger.error(`download ${err}`);
}
logger.debug(`deleting ${outputFile}`);
if (fs.unlinkSync(outputFile)) {
logger.debug(`deleted ${outputFile}`);
else {
utils.deleteFile(`${outputFile}`);
}
});
})

View File

@ -1,9 +1,14 @@
var express = require('express')
const fs = require('fs');
const ffmpeg = require('fluent-ffmpeg');
const uniqueFilename = require('unique-filename');
var archiver = require('archiver');
const constants = require('../constants.js');
var router = express.Router()
const logger = require('../utils/logger.js')
const utils = require('../utils/utils.js')
//routes for /video/extract
@ -11,25 +16,193 @@ const logger = require('../utils/logger.js')
//extracts images from vide
router.post('/audio', function (req, res,next) {
res.locals.conversion="audio"
res.locals.format="wav"
res.locals.extract="audio"
return extract(req,res,next);
});
router.post('/images', function (req, res,next) {
res.locals.conversion="images"
res.locals.format="png"
res.locals.extract="images"
return extract(req,res,next);
});
router.get('/download/:filename', function (req, res,next) {
//download extracted image
let filename = req.params.filename;
let deleteFile = req.query.delete || "true";
let file = `/tmp/${filename}`
logger.debug(`starting download to client ${file}`);
res.download(file, filename, function(err) {
if (err) {
logger.error(`download ${err}`);
}
else
{
//delete file if no delete=no query parameter
if (deleteFile === "true" || deleteFile === "yes")
{
utils.deleteFile(file);
}
}
});
});
// extract audio or images from video
function extract(req,res,next) {
let msg="Not yet implemented.";
logger.error(msg);
let err = new Error(msg);
err.statusCode = 500;
next(err);
let extract = res.locals.extract;
logger.debug(`extract ${extract}`);
let fps = req.query.fps || 1;
//compress = zip or gzip
let compress = req.query.compress || "none";
let ffmpegParams ={};
var format = "png";
if (extract === "images"){
format = "png"
ffmpegParams.outputOptions=[
`-vf fps=${fps}`
];
}
if (extract === "audio"){
format = "wav"
ffmpegParams.outputOptions=[
`-ac 1` ,
`-f ${format}`
];
}
ffmpegParams.extension = format;
let savedFile = res.locals.savedFile;
var outputFile = uniqueFilename('/tmp/') ;
logger.debug(`outputFile ${outputFile}`);
var uniqueFileNamePrefix = outputFile.replace("/tmp/","");
logger.debug(`uniqueFileNamePrefix ${uniqueFileNamePrefix}`);
//ffmpeg processing... converting file...
var ffmpegCommand = ffmpeg(savedFile);
ffmpegCommand = ffmpegCommand
.renice(constants.defaultFFMPEGProcessPriority)
.outputOptions(ffmpegParams.outputOptions)
.on('error', function(err) {
logger.error(`${err}`);
utils.deleteFile(savedFile);
res.writeHead(500, {'Connection': 'close'});
res.end(JSON.stringify({error: `${err}`}));
})
if (extract === "images"){
ffmpegCommand
.output(`${outputFile}-%04d.png`)
.on('end', function() {
logger.debug(`ffmpeg process ended`);
utils.deleteFile(savedFile)
//read extracted files
var files = fs.readdirSync('/tmp/').filter(fn => fn.startsWith(uniqueFileNamePrefix));
if (compress === "zip" || compress === "gzip")
{
//do zip or tar&gzip of all images and download file
var archive = null;
var extension = "";
if (compress === "gzip") {
archive = archiver('tar', {
gzip: true,
zlib: { level: 9 } // Sets the compression level.
});
extension = "tar.gz";
}
else {
archive = archiver('zip', {
zlib: { level: 9 } // Sets the compression level.
});
extension = "zip";
}
let compressFileName = `${uniqueFileNamePrefix}.${extension}`
let compressFilePath = `/tmp/${compressFileName}`
logger.debug(`starting ${compress} process ${compressFilePath}`);
var compressFile = fs.createWriteStream(compressFilePath);
archive.on('error', function(err) {
return next(err);
});
// pipe archive data to the output file
archive.pipe(compressFile);
// add files to archive
for (var i=0; i < files.length; i++) {
var file = `/tmp/${files[i]}`;
archive.file(file, {name: files[i]});
}
// listen for all archive data to be written
// 'close' event is fired only when a file descriptor is involved
compressFile.on('close', function() {
logger.debug(`${compressFileName}: ${archive.pointer()} total bytes`);
logger.debug('archiver has been finalized and the output file descriptor has closed.');
// delete all images
for (var i=0; i < files.length; i++) {
var file = `/tmp/${files[i]}`;
utils.deleteFile(file);
}
//return tar.gz
logger.debug(`starting download to client ${compressFilePath}`);
res.download(compressFilePath, compressFileName, function(err) {
if (err) {
logger.error(`download gzip error: ${err}`);
return next(err);
}
else
{
logger.debug(`download complete ${compressFilePath}`);
utils.deleteFile(compressFilePath);
}
});
});
// Wait for streams to complete
archive.finalize();
}
else
{
//return JSON list of extracted images
logger.debug(`output files in /tmp`);
var responseJson = {};
responseJson["totalfiles"] = files.length;
responseJson["description"] = "Extracted image files and URLs to download them. By default, downloading image also deletes the image from server. Note port in the URL may be different if server is running on Docker/Kubernetes.";
var filesArray=[];
for (var i=0; i < files.length; i++) {
var file = files[i];
logger.debug("file: " + file);
var fileJson={};
fileJson["name"] = file;
fileJson[`url`] = `${req.protocol}://${req.hostname}:${constants.serverPort}${req.baseUrl}/download/${file}`;
filesArray.push(fileJson);
}
responseJson["files"] = filesArray;
res.status(200).send(responseJson);
}
})
.run();
// .save(outputFile);
}
}

View File

@ -74,7 +74,7 @@ router.use(function (req, res,next) {
});
busboy.on('finish', function() {
if (hitLimit) {
fs.unlinkSync(savedFile);
utils.deleteFile(savedFile);
return;
}
logger.debug(`upload complete. file: ${fileName}`)

12
src/utils/utils.js Normal file
View File

@ -0,0 +1,12 @@
const fs = require('fs');
const logger = require('./logger.js')
function deleteFile (filepath) {
fs.unlinkSync(filepath);
logger.debug(`deleted ${filepath}`);
}
module.exports = {
deleteFile
}