gifski.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import { spawn } from 'child_process';
  2. import { closeSync, existsSync, readFileSync, statSync, writeSync } from 'fs';
  3. import * as temp from 'temp';
  4. import { getLogger } from './loggers';
  5. const logger = getLogger('gifski');
  6. const sizeLimit = 10 * 2 ** 20;
  7. const roundToEven = (n: number) => Math.ceil(n / 2) * 2;
  8. export default async (data: ArrayBuffer, targetWidth?: number) => {
  9. const outputFilePath = temp.path({suffix: '.gif'});
  10. temp.track();
  11. try {
  12. const inputFile = temp.openSync();
  13. writeSync(inputFile.fd, Buffer.from(data));
  14. closeSync(inputFile.fd);
  15. logger.info(`saved video file to ${inputFile.path}, starting gif conversion...`);
  16. const args = [
  17. inputFile.path,
  18. '-o',
  19. outputFilePath,
  20. '--fps',
  21. '12.5',
  22. '--quiet',
  23. '--quality',
  24. '90',
  25. ];
  26. if (typeof(targetWidth) === 'number') {
  27. args.push('--width', roundToEven(targetWidth).toString());
  28. }
  29. logger.info(` gifski ${args.join(' ')}`);
  30. const gifskiSpawn = spawn('gifski', args);
  31. const gifskiResult = new Promise<ArrayBufferLike>((resolve, reject) => {
  32. const sizeChecker = setInterval(() => {
  33. if (existsSync(outputFilePath) && statSync(outputFilePath).size > sizeLimit) gifskiSpawn.kill();
  34. }, 5000);
  35. gifskiSpawn.on('exit', () => {
  36. clearInterval(sizeChecker);
  37. if (!existsSync(outputFilePath) || statSync(outputFilePath).size === 0) return reject('no file was created on exit');
  38. logger.info(`gif conversion succeeded, file path: ${outputFilePath}`);
  39. resolve(readFileSync(outputFilePath).buffer);
  40. });
  41. });
  42. const stderr = [];
  43. gifskiSpawn.stderr.on('data', errdata => stderr.push(errdata));
  44. gifskiSpawn.stderr.on('end', () => {
  45. if (stderr.length !== 0) {
  46. if (!gifskiSpawn.killed) gifskiSpawn.kill();
  47. throw Error(Buffer.concat(stderr).toString());
  48. }
  49. });
  50. return await gifskiResult;
  51. } catch (error) {
  52. logger.error(`error converting video to gif ${error ? `message: ${error}` : ''}`);
  53. throw Error('error converting video to gif');
  54. } finally {
  55. temp.cleanup();
  56. }
  57. };