import { spawn } from 'child_process'; import { existsSync, statSync } from 'fs'; import { getLogger } from './loggers'; const logger = getLogger('gifski'); const sizeLimit = 10 * 2 ** 20; const roundToEven = (n: number) => Math.ceil(n / 2) * 2; export default async (inputFilePath: string, targetWidth?: number) => { const outputFilePath = inputFilePath.replace(/(?:\.[^.]*)?$/, '.gif') try { const args = [ inputFilePath, '-o', outputFilePath, '--fps', '12.5', '--quiet', '--quality', '90', ]; if (typeof(targetWidth) === 'number') { args.push('--width', roundToEven(targetWidth).toString()); } logger.info(` gifski ${args.join(' ')}`); const gifskiSpawn = spawn('gifski', args); const gifskiResult = new Promise((resolve, reject) => { const sizeChecker = setInterval(() => { if (existsSync(outputFilePath) && statSync(outputFilePath).size > sizeLimit) gifskiSpawn.kill(); }, 5000); gifskiSpawn.on('exit', () => { clearInterval(sizeChecker); if (!existsSync(outputFilePath) || statSync(outputFilePath).size === 0) return reject('no file was created on exit'); logger.info(`gif conversion succeeded, file path: ${outputFilePath}`); resolve(outputFilePath); }); }); const stderr = []; gifskiSpawn.stderr.on('data', errdata => stderr.push(errdata)); gifskiSpawn.stderr.on('end', () => { if (stderr.length !== 0) { if (!gifskiSpawn.killed) gifskiSpawn.kill(); throw Error(Buffer.concat(stderr).toString()); } }); return await gifskiResult; } catch (error) { logger.error(`error converting video to gif ${error ? `message: ${error}` : ''}`); throw Error('error converting video to gif'); } };