const ffmpeg = require('ffmpeg-static'); const Jimp = require('jimp'); const shell = require('any-shell-escape'); const { exec } = require('child_process'); const { path } = require('./utils'); const { diskFilePath } = require('./config.json'); // Thumbnail parameters const THUMBNAIL_QUALITY = 50; const THUMBNAIL_SIZE = 512; /** * Builds a safe escaped ffmpeg command * @param {String} src Path to the input file * @param {String} dest Path of the output file * @returns {String} The command to execute */ function getCommand(src, dest) { return shell([ ffmpeg, '-y', '-v', (process.env.NODE_ENV === 'production' ? 'error' : 'debug'), // Log level '-i', src, // Input file '-ss', '00:00:01.000', // Timestamp of frame to grab '-frames:v', '1', // Number of frames to grab '-s', `${THUMBNAIL_SIZE}x${THUMBNAIL_SIZE}`, // Dimensions of output file dest // Output file ]); } /** * Builds a thumbnail filename * @param {String} oldName The original filename * @returns {String} The filename for the thumbnail */ function getNewName(oldName) { return oldName.concat('.thumbnail.jpg'); } /** * Builds a path to the thumbnails * @param {String} oldName The original filename * @returns {String} The path to the thumbnail */ function getNewNamePath(oldName) { return path(diskFilePath, 'thumbnails/', getNewName(oldName)); } /** * Extracts an image from a video file to use as a thumbnail, using ffmpeg * @param {*} file The video file to pull a frame from */ function getVideoThumbnail(file) { return new Promise((resolve, reject) => exec( getCommand(file.path, getNewNamePath(file.originalname)), (err) => (err ? reject(err) : resolve()) )); } /** * Generates a thumbnail for the provided image * @param {*} file The file to generate a thumbnail for */ function getImageThumbnail(file) { return new Promise((resolve, reject) => Jimp.read(file.path) .then((image) => image .quality(THUMBNAIL_QUALITY) .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE, Jimp.RESIZE_BICUBIC) .write(getNewNamePath(file.originalname))) .then(resolve) .catch(reject)); } /** * Generates a thumbnail * @param {*} file The file to generate a thumbnail for * @returns The thumbnail filename (NOT the path) */ module.exports = (file) => new Promise((resolve, reject) => (file.mimetype.includes('video') ? getVideoThumbnail : getImageThumbnail)(file) .then(() => resolve(getNewName(file.originalname))) .catch(reject));