import { FileData } from './types/definitions'; import { Config } from 'ass-json'; import fs from 'fs-extra'; import ffmpeg from 'ffmpeg-static'; import sharp from 'sharp'; // @ts-ignore import shell from 'any-shell-escape'; import { exec } from 'child_process'; import { isProd, path } from './utils'; const { diskFilePath }: Config = fs.readJsonSync(path('config.json')); // Thumbnail parameters const THUMBNAIL = { QUALITY: 75, WIDTH: 200 * 2, HEIGHT: 140 * 2, } /** * Builds a safe escaped ffmpeg command */ function getCommand(src: String, dest: String) { return shell([ ffmpeg, '-y', '-v', (isProd ? 'error' : 'debug'), // Log level '-i', src, // Input file '-ss', '00:00:01.000', // Timestamp of frame to grab '-vf', `scale=${THUMBNAIL.WIDTH}:${THUMBNAIL.HEIGHT}:force_original_aspect_ratio=increase,crop=${THUMBNAIL.WIDTH}:${THUMBNAIL.HEIGHT}`, // Dimensions of output file '-frames:v', '1', // Number of frames to grab dest // Output file ]); } /** * Builds a thumbnail filename */ function getNewName(oldName: String) { return oldName.concat('.thumbnail.jpg'); } /** * Builds a path to the thumbnails */ function getNewNamePath(oldName: String) { return path(diskFilePath, 'thumbnails/', getNewName(oldName)); } /** * Extracts an image from a video file to use as a thumbnail, using ffmpeg */ function getVideoThumbnail(file: FileData) { return new Promise((resolve: Function, reject: Function) => exec( getCommand(file.path, getNewNamePath(file.randomId)), // @ts-ignore (err: Error) => (err ? reject(err) : resolve()) )); } /** * Generates a thumbnail for the provided image */ function getImageThumbnail(file: FileData) { return new Promise((resolve, reject) => sharp(file.path) .resize(THUMBNAIL.WIDTH, THUMBNAIL.HEIGHT, { kernel: 'cubic' }) .jpeg({ quality: THUMBNAIL.QUALITY }) .toFile(getNewNamePath(file.randomId)) .then(resolve) .catch(reject)); } /** * Generates a thumbnail */ export default (file: FileData): Promise => new Promise((resolve, reject) => (file.is.video ? getVideoThumbnail : (file.is.image && !file.mimetype.includes('webp')) ? getImageThumbnail : () => Promise.resolve())(file) .then(() => resolve((file.is.video || file.is.image) ? getNewName(file.randomId) : file.is.audio ? 'views/ass-audio-icon.png' : 'views/ass-file-icon.png')) .catch(reject));