123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- 'use strict';
- const exec = require('child_process');
- const gm = require('gm');
- const async = require('async');
- const fs = require('fs');
- const Models = require('../models/db');
- const uploader = require('../helpers/uploader');
- const path = require('path');
- const os = require('os');
- const db = require('../models/db');
- const Sequelize = require('sequelize');
- const Op = Sequelize.Op;
- const mime = require('mime-types');
- const fileType = require('file-type');
- const readChunk = require('read-chunk');
- const convertableImageTypes = [
- "image/png",
- "image/jpeg",
- "application/pdf",
- "image/jpg",
- "image/gif",
- "image/tiff",
- "image/vnd.adobe.photoshop"];
- const convertableVideoTypes = [
- "video/quicktime",
- "video/3gpp",
- "video/mpeg",
- "video/mp4",
- "video/ogg"];
- const convertableAudioTypes = [
- "application/ogg",
- "audio/amr",
- "audio/3ga",
- "audio/wave",
- "audio/3gpp",
- "audio/x-wav",
- "audio/aiff",
- "audio/x-aiff",
- "audio/ogg",
- "audio/mp4",
- "audio/x-m4a",
- "audio/mpeg",
- "audio/mp3",
- "audio/x-hx-aac-adts",
- "audio/aac"];
- function getDuration(localFilePath, callback){
- exec.execFile("ffprobe", ["-show_format", "-of", "json", localFilePath], function(error, stdout, stderr) {
- var test = JSON.parse(stdout);
- callback(parseFloat(test.format.duration));
- });
- }
- function createWaveform(fileName, localFilePath, callback){
- var filePathImage = localFilePath + "-" + (new Date().getTime()) + ".png";
- getDuration(localFilePath, function(duration){
- var totalTime = duration || 1.0;
- var pixelsPerSecond = 256.0;
- do {
- var targetWidth = parseInt(pixelsPerSecond*totalTime, 10);
- if (targetWidth>2048) pixelsPerSecond/=2.0;
- } while (targetWidth>2048 && pixelsPerSecond>1);
- exec.execFile("audiowaveform",
- [
- "-w",
- ""+targetWidth,
- "--pixels-per-second",
- ""+parseInt(pixelsPerSecond),
- "--background-color", "ffffff00",
- "--border-color", "ffffff",
- "--waveform-color", "3498db",
- "--no-axis-labels",
- "-i", localFilePath, "-o", filePathImage
- ],
- {}, function(error, stdout, stderr) {
- if(!error) {
- callback(null, filePathImage);
- } else {
- console.log("error:", stdout, stderr);
- callback(error, null);
- }
- });
- });
- }
- function convertVideo(fileName, filePath, codec, callback, progressCallback) {
- var ext = path.extname(fileName);
- var presetMime = mime.lookup(fileName);
- var newExt = codec == "mp4" ? "mp4" : "ogv";
- var convertedPath = filePath + "." + newExt;
- console.log("converting", filePath, "to", convertedPath);
- var convertArgs = (codec == "mp4") ? [
- "-i", filePath,
- "-threads", "4",
- "-vf", "scale=1280:trunc(ow/a/2)*2", // scale to width of 1280, truncating height to an even value
- "-b:v", "2000k",
- "-acodec", "libvo_aacenc",
- "-b:a", "96k",
- "-vcodec", "libx264",
- "-y", convertedPath ]
- : [
- "-i", filePath,
- "-threads", "4",
- "-vf", "scale=1280:trunc(ow/a/2)*2", // scale to width of 1280, truncating height to an even value
- "-b:v", "2000k",
- "-acodec", "libvorbis",
- "-b:a", "96k",
- "-vcodec", "libtheora",
- "-y", convertedPath];
- var ff = exec.spawn('ffmpeg', convertArgs, {
- stdio: [
- 'pipe', // use parents stdin for child
- 'pipe', // pipe child's stdout to parent
- 'pipe'
- ]
- });
- ff.stdout.on('data', function (data) {
- console.log('[ffmpeg-video] stdout: ' + data);
- });
- ff.stderr.on('data', function (data) {
- console.log('[ffmpeg-video] stderr: ' + data);
- if (progressCallback) {
- progressCallback(data);
- }
- });
- ff.on('close', function (code) {
- console.log('[ffmpeg-video] child process exited with code ' + code);
- if (!code) {
- console.log("converted", filePath, "to", convertedPath);
- callback(null, convertedPath);
- } else {
- callback(code, null);
- }
- });
- }
- function convertAudio(fileName, filePath, codec, callback) {
- var ext = path.extname(fileName);
- var presetMime = mime.lookup(fileName);
- var newExt = codec == "mp3" ? "mp3" : "ogg";
- var convertedPath = filePath + "." + newExt;
- console.log("converting audio", filePath, "to", convertedPath);
- var convertArgs = (ext == ".aac") ? [ "-i", filePath, "-y", convertedPath ]
- : [ "-i", filePath,
- "-b:a", "128k",
- "-y", convertedPath];
- exec.execFile("ffmpeg", convertArgs , {}, function(error, stdout, stderr) {
- if(!error){
- console.log("converted", filePath, "to", convertedPath);
- callback(null, convertedPath);
- }else{
- console.log(error,stdout, stderr);
- callback(error, null);
- }
- });
- }
- function createThumbnailForVideo(fileName, filePath, callback) {
- var filePathImage = filePath + ".jpg";
- exec.execFile("ffmpeg", ["-y", "-i", filePath, "-ss", "00:00:01.00", "-vcodec", "mjpeg", "-vframes", "1", "-f", "image2", filePathImage], {}, function(error, stdout, stderr){
- if(!error){
- callback(null, filePathImage);
- }else{
- console.log("error:", stdout, stderr);
- callback(error, null);
- }
- });
- }
- function getMime(fileName, filePath, callback) {
- var ext = path.extname(fileName);
- var presetMime = mime.lookup(fileName);
-
- if (presetMime) {
- callback(null, presetMime);
- } else {
- const buffer = readChunk.sync(filePath, 0, 4100);
- var mimeType = fileType(buffer);
- callback(null, mimeType);
- }
- }
- function resizeAndUpload(a, size, max, fileName, localFilePath, callback) {
- if (max>320 || size.width > max || size.height > max) {
- var resizedFileName = max + "_"+fileName;
- var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + resizedFileName;
- var localResizedFilePath = os.tmpdir()+"/"+resizedFileName;
- gm(localFilePath).resize(max, max).autoOrient().write(localResizedFilePath, function (err) {
- if(!err) {
- uploader.uploadFile(s3Key, "image/jpeg", localResizedFilePath, function(err, url) {
- if (err) callback(err);
- else{
- fs.unlink(localResizedFilePath, function (err) {
- if (err) {
- console.error(err);
- callback(null, url);
- }
- else callback(null, url);
- });
- }
- });
- } else {
- console.error(err);
- callback(err);
- }
- });
- } else {
- callback(null, "");
- }
- }
- var resizeAndUploadImage = function(a, mimeType, size, fileName, fileNameOrg, imageFilePath, originalFilePath, payloadCallback) {
- async.parallel({
- small: function(callback){
- resizeAndUpload(a, size, 320, fileName, imageFilePath, callback);
- },
- medium: function(callback){
- resizeAndUpload(a, size, 800, fileName, imageFilePath, callback);
- },
- big: function(callback){
- resizeAndUpload(a, size, 1920, fileName, imageFilePath, callback);
- },
- original: function(callback){
- var s3Key = "s"+ a.space_id.toString() + "/a" + a._id + "/" + fileNameOrg;
- uploader.uploadFile(s3Key, mimeType, originalFilePath, function(err, url){
- callback(null, url);
- });
- }
- }, function(err, results) {
- a.state = "idle";
- a.mime = mimeType;
- var stats = fs.statSync(originalFilePath);
- a.payload_size = stats["size"];
- a.payload_thumbnail_web_uri = results.small;
- a.payload_thumbnail_medium_uri = results.medium;
- a.payload_thumbnail_big_uri = results.big;
- a.payload_uri = results.original;
- var factor = 320/size.width;
- a.w = Math.round(size.width*factor);
- a.h = Math.round(size.height*factor);
- a.updated_at = new Date();
- db.packArtifact(a);
- a.save().then(function() {
- fs.unlink(originalFilePath, function (err) {
- if (err){
- console.error(err);
- payloadCallback(err, null);
- } else {
- console.log('successfully deleted ' + originalFilePath);
- payloadCallback(null, a);
- }
- });
- });
- });
- };
- module.exports = {
- convert: function(a, fileName, localFilePath, payloadCallback, progressCallback) {
- getMime(fileName, localFilePath, function(err, mimeType){
- console.log("[convert] fn: "+fileName+" local: "+localFilePath+" mimeType:", mimeType);
- if (!err) {
- if (convertableImageTypes.indexOf(mimeType) > -1) {
-
- gm(localFilePath).size(function (err, size) {
- console.log("[convert] gm:", err, size);
- if (!err) {
- if(mimeType == "application/pdf") {
- var firstImagePath = localFilePath + ".jpeg";
- exec.execFile("gs", ["-sDEVICE=jpeg","-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-dFirstPage=1", "-dLastPage=1", "-sOutputFile=" + firstImagePath, "-r90", "-f", localFilePath], {}, function(error, stdout, stderr) {
- if(error === null) {
- resizeAndUploadImage(a, mimeType, size, fileName + ".jpeg", fileName, firstImagePath, localFilePath, function(err, a) {
- fs.unlink(firstImagePath, function (err) {
- payloadCallback(err, a);
- });
- });
- } else {
- payloadCallback(error, null);
- }
- });
- } else if(mimeType == "image/gif") {
- //gifs are buggy after convertion, so we should not convert them
- var s3Key = "s"+ a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
- uploader.uploadFile(s3Key, "image/gif", localFilePath, function(err, url) {
- if (err) payloadCallback(err);
- else {
- console.log(localFilePath);
- var stats = fs.statSync(localFilePath);
- a.state = "idle";
- a.mime = mimeType;
- a.payload_size = stats["size"];
- a.payload_thumbnail_web_uri = url;
- a.payload_thumbnail_medium_uri = url;
- a.payload_thumbnail_big_uri = url;
- a.payload_uri = url;
- var factor = 320/size.width;
- a.w = Math.round(size.width*factor);
- a.h = Math.round(size.height*factor);
- a.updated_at = new Date();
- db.packArtifact(a);
- a.save().then(function() {
- fs.unlink(localFilePath, function (err) {
- if (err){
- console.error(err);
- payloadCallback(err, null);
- } else {
- console.log('successfully deleted ' + localFilePath);
- payloadCallback(null, a);
- }
- });
- });
- }
- });
- } else {
- resizeAndUploadImage(a, mimeType, size, fileName, fileName, localFilePath, localFilePath, payloadCallback);
- }
- } else payloadCallback(err);
- });
-
- } else if (convertableVideoTypes.indexOf(mimeType) > -1) {
- async.parallel({
- thumbnail: function(callback) {
- createThumbnailForVideo(fileName, localFilePath, function(err, created){
- console.log("thumbnail created: ", err, created);
- if (err) callback(err);
- else {
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".jpg" ;
- uploader.uploadFile(keyName, "image/jpeg", created, function(err, url){
- if (err) callback(err);
- else callback(null, url);
- });
- }
- });
- },
- ogg: function(callback) {
- if (mimeType == "video/ogg") {
- callback(null, "org");
- } else {
- convertVideo(fileName, localFilePath, "ogg", function(err, file) {
- if(err) callback(err);
- else {
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".ogv" ;
- uploader.uploadFile(keyName, "video/ogg", file, function(err, url){
- if (err) callback(err);
- else callback(null, url);
- });
- }
- }, progressCallback);
- }
- },
- mp4: function(callback) {
- if (mimeType == "video/mp4") {
- callback(null, "org");
- } else {
- convertVideo(fileName, localFilePath, "mp4", function(err, file) {
- if (err) callback(err);
- else {
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".mp4";
- uploader.uploadFile(keyName, "video/mp4" ,file, function(err, url) {
- if (err) callback(err);
- else callback(null, url);
- });
- }
- }, progressCallback);
- }
- },
- original: function(callback){
- uploader.uploadFile(fileName, mimeType, localFilePath, function(err, url){
- callback(null, url);
- });
- }
- }, function(err, results) {
- console.log(err, results);
- if (err) payloadCallback(err, a);
- else {
- a.state = "idle";
- a.mime = mimeType;
- var stats = fs.statSync(localFilePath);
- a.payload_size = stats["size"];
- a.payload_thumbnail_web_uri = results.thumbnail;
- a.payload_thumbnail_medium_uri = results.thumbnail;
- a.payload_thumbnail_big_uri = results.thumbnail;
- a.payload_uri = results.original;
- if (mimeType == "video/mp4") {
- a.payload_alternatives = [
- {
- mime: "video/ogg",
- payload_uri: results.ogg
- }
- ];
- } else {
- a.payload_alternatives = [
- {
- mime: "video/mp4",
- payload_uri: results.mp4
- }
- ];
- }
- db.packArtifact(a);
- a.updated_at = new Date();
- a.save().then(function() {
- fs.unlink(localFilePath, function (err) {
- if (err) {
- console.error(err);
- payloadCallback(err, null);
- } else {
- console.log('successfully deleted ' + localFilePath);
- payloadCallback(null, a);
- }
- });
- });
- }
- });
- } else if (convertableAudioTypes.indexOf(mimeType) > -1) {
- async.parallel({
- ogg: function(callback) {
- convertAudio(fileName, localFilePath, "ogg", function(err, file) {
- if(err) callback(err);
- else {
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".ogg" ;
- uploader.uploadFile(keyName, "audio/ogg", file, function(err, url){
- if (err) callback(err);
- else callback(null, url);
- });
- }
- });
- },
- mp3_waveform: function(callback) {
- convertAudio(fileName, localFilePath, "mp3", function(err, file) {
- if(err) callback(err);
- else {
- createWaveform(fileName, file, function(err, filePath){
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + "-" + (new Date().getTime()) + ".png";
- uploader.uploadFile(keyName, "image/png", filePath, function(err, pngUrl){
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName + ".mp3" ;
- uploader.uploadFile(keyName, "audio/mp3", file, function(err, mp3Url){
- if (err) callback(err);
- else callback(null, {waveform: pngUrl, mp3: mp3Url});
- });
- });
- });
- }
- });
- },
- original: function(callback) {
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
- uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url){
- callback(null, url);
- });
- }
- }, function(err, results) {
- console.log(err, results);
- if (err) payloadCallback(err, a);
- else {
- a.state = "idle";
- a.mime = mimeType;
- var stats = fs.statSync(localFilePath);
- a.payload_size = stats["size"];
- a.payload_thumbnail_web_uri = results.mp3_waveform.waveform;
- a.payload_thumbnail_medium_uri = results.mp3_waveform.waveform;
- a.payload_thumbnail_big_uri = results.mp3_waveform.waveform;
- a.payload_uri = results.original;
- a.payload_alternatives = [
- {payload_uri:results.ogg, mime:"audio/ogg"},
- {payload_uri:results.mp3_waveform.mp3, mime:"audio/mpeg"}
- ];
- a.updated_at = new Date();
- db.packArtifact(a);
-
- a.save().then(function(){
- fs.unlink(localFilePath, function (err) {
- if (err){
- console.error(err);
- payloadCallback(err, null);
- } else {
- console.log('successfully deleted ' + localFilePath);
- payloadCallback(null, a);
- }
- });
- });
- }
- });
- } else {
- console.log("mimeType not matched for conversion, storing file");
- var keyName = "s" + a.space_id.toString() + "/a" + a._id.toString() + "/" + fileName;
- uploader.uploadFile(keyName, mimeType, localFilePath, function(err, url) {
-
- a.state = "idle";
- a.mime = mimeType;
- var stats = fs.statSync(localFilePath);
- a.payload_size = stats["size"];
- a.payload_uri = url;
-
- a.updated_at = new Date();
- a.save().then(function() {
- fs.unlink(localFilePath, function (err) {
- payloadCallback(null, a);
- });
- });
- });
- }
- } else {
- //there was an error getting mime
- payloadCallback(err);
- }
- });
- }
- };
|