gstorage-video-optimizer
Version:
This cli uses ffmpeg to optimize videos in google cloud storage for web. It generates and uploads a .mp4 and .webm file to the same bucket.
163 lines (132 loc) ⢠5.35 kB
JavaScript
/**
* Optimize MP4 Video Uploads for web
*
* Logs at https://console.cloud.google.com/logs/viewer?project=videooptimizer-216820&authuser=4&minLogLevel=0&expandAll=false×tamp=2018-09-18T21:08:04.097000000Z&customFacets=&limitCustomFacetWidth=true&interval=NO_LIMIT&resource=gae_app&logName=projects%2Fvideooptimizer-216820%2Flogs%2Fstdout&logName=projects%2Fvideooptimizer-216820%2Flogs%2Fstderr&logName=projects%2Fvideooptimizer-216820%2Flogs%2Fappengine.googleapis.com%252Frequest_log&scrollTimestamp=2018-09-18T21:08:59.364904000Z
*
* Refs:
* 0. https://github.com/firebase/functions-samples/blob/master/ffmpeg-convert-audio/functions/index.js
* 1. https://stackoverflow.com/questions/42773497/can-you-call-out-to-ffmpeg-in-a-firebase-cloud-function
* 2. https://stackoverflow.com/questions/44469537/cloud-functions-for-firebase-completing-long-processes-without-touching-maximum/44472980#44472980
* 3. https://cloud.google.com/functions/docs/concepts/exec
* 4. https://cloud.google.com/functions/docs/concepts/nodejs-8-runtime
* 5. https://github.com/Scew5145/GCSConvertDemo
*/
const path = require('path');
const os = require('os');
const fs = require('fs');
const exec = require("child-process-promise").exec;
const meow = require('meow');
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');
const gcs = require('@google-cloud/storage')();
var pjson = require('./package.json');
const DEBUG=false;
/**
* CLI Handling
*/
const cli = meow(`
Usage
$ gstorage-video-optimizer bucket path [options]
Flags
--shutdown, -s shutdown on finish. Useful for auto-scaling
Examples
$ full-page-cache-warmer gs://get-candeo.appspot.com video_files/wbbpyjgTudQY9QNLCAl7
...
`, {
flags: {
shutdown: {
type: 'boolean',
alias: 's',
default: false,
},
}
});
if (cli.input.length !== 2) {
cli.showHelp(1);
}
const shutdown = cli.flags.shutdown;
const gbucket = cli.input[0];
const gpath = cli.input[1];
/**
* Timer
*/
var scriptDuration=0;
setInterval(() => { scriptDuration++; }, 1000);
/** Helpers **/
function promisifyCommand(command) {
return new Promise((resolve, reject) => {
command
.on('end', () => {
resolve();
})
.on('error', (error) => {
reject(error);
})
.run();
});
}
const ffmpegLogger = {
debug: function(arg) { if (DEBUG) console.log('[DEBUG] ' + arg); },
info: function(arg) { if (DEBUG) console.log('[INFO] ' + arg); },
warn: function(arg) { if (DEBUG) console.log('[WARN] ' + arg); },
error: function(arg) { console.log('[ERROR] ' + arg); }
};
async function encode(gbucket,gpath) {
const bucket = gcs.bucket(gbucket); // The Storage bucket that contains the file.
const srcBucketPath = gpath; // File path in the bucket.
const fileName = path.basename(srcBucketPath);
const srcLocalPath = path.join(os.tmpdir(), fileName);
const outLocalPath = path.join(os.tmpdir(), fileName + '.mp4');
const outBucketPath = path.join(path.dirname(srcBucketPath), fileName + '.mp4');
console.log("Encoding " + bucket + srcBucketPath);
console.log('Downloading ' + srcBucketPath + ' to', srcLocalPath);
await bucket.file(srcBucketPath).download({destination: srcLocalPath});
console.log('Creating ' + outLocalPath);
const ffmpegCmdMp4 = ffmpeg({ source: srcLocalPath, logger: ffmpegLogger, timeout: 1200 })
.setFfmpegPath(ffmpeg_static.path)
.on('progress', function(progress) {
console.log('Processing: ' + progress.percent + '% done'); // not always available
console.dir(progress);
})
// .on('stderr', function(stderrLine) {
// console.log('Stderr output: ' + stderrLine);
// })
// Audio Settings: https://trac.ffmpeg.org/wiki/Encode/MP3
.audioCodec('libmp3lame')
.audioChannels(1)
// .audioBitrate(64)
.audioQuality(7)
// Mp4 settings
.videoCodec('libx264')
.videoBitrate(1024)
.size('1920x?')
.format('mp4')
.output(outLocalPath);
await promisifyCommand(ffmpegCmdMp4);
console.log('Uploading '+ outLocalPath + ' to ' + outBucketPath);
await bucket.upload(outLocalPath, {destination: outBucketPath});
console.log('Deleting tmp files');
fs.unlinkSync(srcLocalPath);
fs.unlinkSync(outLocalPath);
return Promise.resolve();
}
async function main() {
console.log("\n\n***********************************************************************\n");
console.log("gstorage-video-optimizer " + pjson.version);
var currentdate = new Date();
console.log("Current Time: " + currentdate.getFullYear() + "." + (currentdate.getMonth()+1) + "." + currentdate.getDate() + " @ " + currentdate.getHours() + ":" + currentdate.getMinutes() + ":" + currentdate.getSeconds());
console.log("\n***********************************************************************\n");
await encode(gbucket,gpath);
console.log("\nSTATS\n")
console.log("Duration: " + scriptDuration + "s");
console.log("\nš Done! š\n");
if (shutdownOnFinish) exec('sudo shutdown -t 1');
process.exit(); // The timers will keep the process from exiting, so force exit.
}
main();
setTimeout(() => {
console.log("\nTimeout Reached: Exiting\n");
if (shutdownOnFinish) exec('sudo shutdown -t 1');
process.exit(1);
}, 1260 * 1000); // 21 minutes