Skip to content

mekwall/ffmpeggy

Repository files navigation

ffmpeggy

license npm npm package size node TypeScript FFmpeg dependencies types coverage GitHub last commit

A minimal yet powerful wrapper for FFmpeg and FFprobe. Has built-in support for Node.js streams and events that can provide you with a detailed progress report.

This is a hybrid package built in TypeScript that provides both CommonJS and ES modules with only a couple of dependencies.

✨ Features

  • πŸš€ Simple API: Intuitive interface with method chaining
  • πŸ“‘ Event-Driven: Real-time progress updates and status events
  • πŸ”„ Stream Support: Native Node.js stream integration for input/output
  • ⚑ TypeScript: Full TypeScript support with comprehensive type definitions
  • πŸ” Media Probing: Built-in FFprobe integration for media analysis
  • πŸ›‘οΈ Error Handling: Robust error handling with descriptive messages
  • βš™οΈ Flexible Configuration: Support for all FFmpeg options and arguments
  • πŸ“Š Progress Tracking: Detailed progress events with frame, time, and bitrate info
  • 🎯 Hybrid Package: Works with both CommonJS and ES modules

πŸ“¦ Installation

npm install --save ffmpeggy

or

yarn add ffmpeggy

Installing FFmpeg and FFprobe Binaries

If you don't want to provide your own binaries, you can use the following packages that provide binaries for both ffmpeg and ffprobe:

npm install --save ffmpeg-static ffprobe-static

or

yarn add ffmpeg-static ffprobe-static

You can then configure FFmpeggy to use these binaries:

import ffmpegBin from "ffmpeg-static";
import { path as ffprobeBin } from "ffprobe-static";

FFmpeggy.DefaultConfig = {
  ...FFmpeggy.DefaultConfig,
  ffprobeBin,
  ffmpegBin,
};

πŸš€ Quick Start

Basic Usage

import { FFmpeggy } from "ffmpeggy";

const ffmpeggy = new FFmpeggy();
try {
  ffmpeggy
    .setInput("input.mp4")
    .setOutput("output.mkv")
    .setOutputOptions(["-c:v h264"])
    .run();

  await ffmpeggy.done();
  console.log("Conversion completed!");
} catch (error) {
  console.error("Conversion failed:", error);
}

With Constructor Options

import { FFmpeggy } from "ffmpeggy";

const ffmpeggy = new FFmpeggy({
  autorun: true,
  input: "input.mp4",
  output: "output.mkv",
  outputOptions: ["-c:v h264"],
  overwriteExisting: true,
});

await ffmpeggy.done();

πŸ“š API Reference

Constructor Options

Option Type Description Default
cwd string Working directory for FFmpeg process.cwd()
input string | ReadStream Input file path or readable stream ""
output string | WriteStream Output file path or writable stream ""
pipe boolean Enable pipe mode (outputs to stream) false
globalOptions string[] FFmpeg global options ["-stats"]
inputOptions string[] FFmpeg input options []
outputOptions string[] FFmpeg output options []
overwriteExisting boolean Add -y flag to overwrite files false
hideBanner boolean Add -hide_banner flag true
autorun boolean Automatically run FFmpeg after setup false

Method Chaining

FFmpeggy supports method chaining for fluent configuration:

const ffmpeggy = new FFmpeggy()
  .setInput("input.mp4")
  .setOutput("output.mkv")
  .setOutputOptions(["-c:v h264", "-c:a aac"])
  .setOverwriteExisting(true)
  .setHideBanner(true);

Events

FFmpeggy extends EventEmitter and provides the following events:

start - (ffmpegArgs: readonly string[]) => void

Fires when the FFmpeg process starts. Provides the arguments passed to FFmpeg.

progress - (progress: FFmpeggyProgressEvent) => void

Fires during processing with detailed progress information:

interface FFmpeggyProgressEvent {
  frame?: number; // Current frame number
  fps?: number; // Current processing framerate
  q?: number; // Quality scale (usually 0)
  size?: number; // Current output size in KB
  time?: number; // Current processing time in seconds
  bitrate?: number; // Current bitrate
  duplicates?: number; // Duplicate frames
  dropped?: number; // Dropped frames
  speed?: number; // Processing speed multiplier
  duration?: number; // Total duration (calculated)
  percent?: number; // Progress percentage (calculated)
}

done - (file?: string, sizes?: FFmpeggyFinalSizes) => void

Fires when processing completes successfully:

interface FFmpeggyFinalSizes {
  video?: number; // Video stream size in bytes
  audio?: number; // Audio stream size in bytes
  subtitles?: number; // Subtitle stream size in bytes
  otherStreams?: number; // Other stream types size in bytes
  globalHeaders?: number; // Global headers size in bytes
  muxingOverhead?: number; // Muxing overhead as decimal
}

error - (error: Error) => void

Fires when an error occurs during processing.

exit - (code?: number \| null, error?: Error) => void

Fires when the FFmpeg process exits.

writing - (fileName: string) => void

Fires when FFmpeg begins writing to a file (useful for segmented output).

probe - (probeResult: FFprobeResult) => void

Fires when media probing completes.

Media Probing

FFmpeggy includes built-in FFprobe integration for media analysis:

// Static method
const probeResult = await FFmpeggy.probe("input.mp4");

// Instance method
const ffmpeggy = new FFmpeggy({ input: "input.mp4" });
const probeResult = await ffmpeggy.probe();

The probe result includes detailed information about streams, format, and metadata.

Stream Support

FFmpeggy supports Node.js streams for both input and output:

import { createReadStream, createWriteStream } from "fs";
import { FFmpeggy } from "ffmpeggy";

// Input stream
const ffmpeggy = new FFmpeggy({
  input: createReadStream("input.mp4"),
  inputOptions: ["-f mp4"], // Format hint for streams
  output: createWriteStream("output.mkv"),
  outputOptions: ["-f matroska", "-c:v h264"],
});

// Output stream
const ffmpeggy = new FFmpeggy({
  input: "input.mp4",
  pipe: true, // Enable pipe mode
});

const outputStream = ffmpeggy.toStream();
outputStream.pipe(createWriteStream("output.mkv"));

Utility Functions

FFmpeggy exports utility functions for time conversion:

import { secsToTimer, timerToSecs } from "ffmpeggy";

// Convert seconds to HH:MM:SS.MS format
const timer = secsToTimer(3661.5); // "01:01:01.50"

// Convert HH:MM:SS.MS format to seconds
const seconds = timerToSecs("01:01:01.50"); // 3661.5

πŸ”§ Advanced Usage

Event-Driven Processing

import { FFmpeggy } from "ffmpeggy";

const ffmpeggy = new FFmpeggy({
  autorun: true,
  input: "input.mp4",
  output: "output.mkv",
  outputOptions: ["-c:v h264"],
});

ffmpeggy
  .on("start", (args) => {
    console.log("FFmpeg started with args:", args);
  })
  .on("progress", (progress) => {
    console.log(`${progress.percent?.toFixed(1)}% - ${progress.time}s`);
  })
  .on("error", (error) => {
    console.error("Processing error:", error.message);
  })
  .on("done", (file, sizes) => {
    console.log("Processing completed!");
    if (sizes) {
      console.log(`Video: ${sizes.video} bytes`);
      console.log(`Audio: ${sizes.audio} bytes`);
      console.log(
        `Muxing overhead: ${(sizes.muxingOverhead * 100).toFixed(3)}%`
      );
    }
  });

Process Control

const ffmpeggy = new FFmpeggy({
  autorun: true,
  input: "input.mp4",
  output: "output.mkv",
});

// Stop processing
await ffmpeggy.stop();

// Reset instance
await ffmpeggy.reset();

// Wait for completion
const result = await ffmpeggy.done();
console.log("Output file:", result.file);

Error Handling

FFmpeggy provides comprehensive error handling:

try {
  const ffmpeggy = new FFmpeggy({
    input: "nonexistent.mp4",
    output: "output.mkv",
  });

  await ffmpeggy.run();
  await ffmpeggy.done();
} catch (error) {
  console.error("Error:", error.message);
  // Error messages are concise and descriptive
}

πŸ€” Why Another FFmpeg Wrapper?

FFmpeggy was created because existing solutions had limitations:

  • Maintenance: Many existing wrappers are poorly maintained
  • TypeScript: Lack of proper TypeScript support and type definitions
  • Complexity: Overly complex APIs that hide FFmpeg's power
  • Streams: Limited or no support for Node.js streams
  • Events: Missing real-time progress and status events

FFmpeggy aims to be:

  • Simple: Intuitive API that doesn't hide FFmpeg's capabilities
  • Type-Safe: Full TypeScript support with comprehensive types
  • Stream-Ready: Native Node.js stream integration
  • Event-Driven: Real-time progress and status updates
  • Maintained: Actively maintained with regular updates

πŸ“„ License

MIT

About

A minimal yet powerful wrapper for FFmpeg and FFprobe

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published