UNPKG

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
#!/usr/bin/env node /** * Optimize MP4 Video Uploads for web * * Logs at https://console.cloud.google.com/logs/viewer?project=videooptimizer-216820&authuser=4&minLogLevel=0&expandAll=false&timestamp=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