CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fluent-ffmpeg

A fluent API to FFMPEG for audio and video manipulation with chainable methods

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

special-features.mddocs/

Special Features

Advanced functionality including screenshot generation, video concatenation, metadata analysis, and specialized processing recipes.

Capabilities

Screenshot Generation

Generate video thumbnails and screenshots at specific time points.

/**
 * Generate video screenshots
 * @param config - Screenshot configuration (number or object)
 * @param folder - Optional output directory (legacy parameter)
 * @returns FfmpegCommand instance
 * @emits 'filenames' event with generated filenames
 */
screenshots(config, folder?) // → FfmpegCommand
// Aliases: takeScreenshots, thumbnail, thumbnails, screenshot

Configuration Object:

interface ScreenshotConfig {
  count?: number;           // Number of screenshots
  folder?: string;          // Output directory
  filename?: string;        // Filename pattern ('%b', '%r', '%s', '%w', '%h', '%i')
  timemarks?: string[];     // Specific time points ['25%', '50%', '01:30:00']
  timestamps?: string[];    // Alias for timemarks
  fastSeek?: boolean;       // Use fast but less accurate seeking
  size?: string;            // Screenshot size ('320x240', '50%', etc.)
}

Usage Examples:

// Simple screenshot count
ffmpeg('video.mp4')
  .screenshots({
    count: 5,
    folder: './thumbnails',
    filename: 'thumb_%i.jpg'
  })
  .on('filenames', (filenames) => {
    console.log('Screenshots saved:', filenames);
  });

// Screenshots at specific times
ffmpeg('movie.mp4')
  .screenshots({
    timemarks: ['25%', '50%', '75%'],
    folder: './screenshots',
    filename: 'scene_%s.png',
    size: '640x360'
  });

// Precise timestamp screenshots
ffmpeg('presentation.mp4')
  .screenshots({
    timestamps: ['00:01:30', '00:05:45', '00:12:00'],
    folder: './slides',
    filename: 'slide_%i.jpg',
    fastSeek: false  // Accurate seeking
  });

// Custom filename patterns
ffmpeg('video.mp4')
  .screenshots({
    count: 3,
    folder: './thumbs',
    filename: '%b_thumb_%i_%wx%h.jpg',  // basename_thumb_index_widthxheight
    size: '300x200'
  });

// Legacy syntax (still supported)
ffmpeg('video.mp4')
  .screenshots(5, './thumbnails');

Filename Pattern Variables:

  • %b - Input basename (without extension)
  • %r - Resolution (WxH)
  • %s - Timestamp in seconds
  • %w - Width in pixels
  • %h - Height in pixels
  • %i - Screenshot index (1-based)
  • %000i - Zero-padded screenshot index (any number of zeros for padding)
  • %f - Input filename (full filename with extension)

Video Concatenation

Concatenate multiple input files into a single output.

/**
 * Concatenate inputs into single file
 * @param target - Output file path or writable stream
 * @param options - Optional pipe options for streams
 * @returns FfmpegCommand instance
 */
concat(target, options?) // → FfmpegCommand
// Aliases: concatenate, mergeToFile

Usage Examples:

// Basic concatenation
ffmpeg()
  .input('part1.mp4')
  .input('part2.mp4') 
  .input('part3.mp4')
  .concat('complete_movie.mp4')
  .on('end', () => {
    console.log('Concatenation finished');
  });

// Concatenate different formats (re-encodes)
ffmpeg()
  .input('intro.avi')
  .input('main_content.mov')
  .input('outro.mp4')
  .videoCodec('libx264')  // Ensure consistent encoding
  .audioCodec('aac')
  .concat('unified_format.mp4');

// Stream concatenation
const fs = require('fs');
const outputStream = fs.createWriteStream('concatenated.mp4');

ffmpeg()
  .input('video1.mp4')
  .input('video2.mp4')
  .concat(outputStream, { end: true });

// Concatenate with filters (for format consistency)
ffmpeg()
  .input('720p_video.mp4')
  .input('1080p_video.mp4')
  .complexFilter([
    '[0:v]scale=1920:1080[v0]',
    '[1:v]scale=1920:1080[v1]', 
    '[v0][0:a][v1][1:a]concat=n=2:v=1:a=1[outv][outa]'
  ])
  .outputOptions(['-map', '[outv]', '-map', '[outa]'])
  .save('uniform_concat.mp4');

Metadata Analysis

Analyze input files using FFprobe for detailed media information.

/**
 * Analyze input with ffprobe
 * @param index - Optional input index (default: current/last input)
 * @param options - Optional ffprobe options array
 * @param callback - Callback function (err, data) => void
 */
ffprobe(index?, options?, callback) // → void

/**
 * Static method for direct file analysis
 * @param file - Input file path
 * @param callback - Callback function (err, data) => void
 */
FfmpegCommand.ffprobe(file, callback) // → void
// Also supports: FfmpegCommand.ffprobe(file, options, callback)

Usage Examples:

// Basic metadata analysis
ffmpeg.ffprobe('video.mp4', (err, metadata) => {
  if (err) {
    console.error('Probe error:', err);
    return;
  }
  
  console.log('Duration:', metadata.format.duration);
  console.log('Bitrate:', metadata.format.bit_rate);
  console.log('Format:', metadata.format.format_name);
  
  metadata.streams.forEach((stream, index) => {
    console.log(`Stream ${index}:`, {
      type: stream.codec_type,
      codec: stream.codec_name,
      duration: stream.duration
    });
  });
});

// Probe specific input in command
ffmpeg('input1.mp4')
  .input('input2.avi')
  .ffprobe(1, (err, metadata) => {  // Probe second input
    if (!err) {
      console.log('Second input metadata:', metadata);
    }
  });

// Probe with custom options
ffmpeg.ffprobe('video.mkv', ['-show_chapters', '-show_format'], (err, data) => {
  if (!err) {
    console.log('Chapters:', data.chapters);
    console.log('Format info:', data.format);
  }
});

// Promise wrapper for ffprobe
const probeAsync = (file) => {
  return new Promise((resolve, reject) => {
    ffmpeg.ffprobe(file, (err, metadata) => {
      if (err) reject(err);
      else resolve(metadata);
    });
  });
};

// Use with async/await
const analyzeVideo = async (filename) => {
  try {
    const metadata = await probeAsync(filename);
    
    const videoStream = metadata.streams.find(s => s.codec_type === 'video');
    const audioStream = metadata.streams.find(s => s.codec_type === 'audio');
    
    return {
      duration: parseFloat(metadata.format.duration),
      size: parseInt(metadata.format.size),
      video: videoStream ? {
        codec: videoStream.codec_name,
        width: videoStream.width,
        height: videoStream.height,
        fps: eval(videoStream.r_frame_rate)  // "30000/1001" format
      } : null,
      audio: audioStream ? {
        codec: audioStream.codec_name,
        sampleRate: audioStream.sample_rate,
        channels: audioStream.channels
      } : null
    };
  } catch (error) {
    console.error('Analysis failed:', error);
    return null;
  }
};

Custom Options and Filters

Advanced filter configurations for complex processing.

/**
 * Set complex filtergraph for advanced processing
 * @param spec - Filter specification (string or object array)
 * @param map - Optional output stream mappings
 * @returns FfmpegCommand instance
 */
complexFilter(spec, map?) // → FfmpegCommand
// Aliases: filterGraph

Usage Examples:

// Picture-in-picture with fade transition
ffmpeg()
  .input('background.mp4')
  .input('overlay.mp4')
  .complexFilter([
    '[1:v]scale=320:240[pip]',
    '[0:v][pip]overlay=main_w-overlay_w-10:10:enable=\'between(t,5,25)\'[out]'
  ])
  .outputOptions(['-map', '[out]', '-map', '0:a'])
  .save('pip_video.mp4');

// Multi-input audio mixing
ffmpeg()
  .input('music.mp3')
  .input('narration.wav')
  .input('effects.wav')
  .complexFilter([
    '[0:a]volume=0.3[music]',
    '[1:a]volume=1.0[voice]', 
    '[2:a]volume=0.5[fx]',
    '[music][voice][fx]amix=inputs=3[out]'
  ])
  .outputOptions(['-map', '[out]'])
  .save('mixed_audio.mp3');

// Split screen effect
ffmpeg()
  .input('left_video.mp4')
  .input('right_video.mp4')
  .complexFilter([
    '[0:v]crop=iw/2:ih:0:0[left]',
    '[1:v]crop=iw/2:ih:iw/2:0[right]',
    '[left][right]hstack[out]'
  ])
  .outputOptions(['-map', '[out]'])
  .save('split_screen.mp4');

Advanced Recipe Patterns

Batch Processing

Process multiple files with consistent settings.

const processVideoFiles = async (inputFiles, outputDir) => {
  const baseCommand = ffmpeg()
    .videoCodec('libx264')
    .audioCodec('aac')
    .videoBitrate('2000k')
    .size('1920x1080');
  
  const promises = inputFiles.map(inputFile => {
    const outputFile = `${outputDir}/${path.basename(inputFile, path.extname(inputFile))}.mp4`;
    
    return new Promise((resolve, reject) => {
      baseCommand.clone()
        .input(inputFile)
        .save(outputFile)
        .on('end', resolve)
        .on('error', reject);
    });
  });
  
  await Promise.all(promises);
  console.log('Batch processing complete');
};

Conditional Processing

Apply different processing based on input characteristics.

const smartProcess = async (inputFile, outputFile) => {
  // Analyze input first
  const metadata = await probeAsync(inputFile);
  const videoStream = metadata.streams.find(s => s.codec_type === 'video');
  
  let command = ffmpeg(inputFile);
  
  // Adjust processing based on input
  if (videoStream.width > 1920) {
    command = command.size('1920x?');  // Downscale if too large
  }
  
  if (videoStream.codec_name !== 'h264') {
    command = command.videoCodec('libx264');  // Re-encode if not H.264
  }
  
  // Set bitrate based on resolution
  const pixels = videoStream.width * videoStream.height;
  const bitrate = Math.max(1000, Math.min(5000, pixels / 500));
  
  command
    .videoBitrate(`${bitrate}k`)
    .save(outputFile);
};

Special Feature Types

/**
 * Screenshot configuration interface
 */
interface ScreenshotConfig {
  count?: number;           // Number of screenshots to generate
  folder?: string;          // Output directory path
  filename?: string;        // Filename pattern with variables
  timemarks?: string[];     // Time positions (['25%', '50%'] or ['00:01:30'])
  timestamps?: string[];    // Alias for timemarks
  fastSeek?: boolean;       // Fast seeking (less accurate, faster)
  size?: string;            // Output size ('WxH', 'W%', etc.)
}

/**
 * FFprobe metadata structure (simplified)
 */
interface ProbeData {
  streams: StreamInfo[];    // Array of stream information
  format: FormatInfo;       // Container format information
  chapters?: ChapterInfo[]; // Chapter information (if available)
}

/**
 * Stream information from FFprobe
 */
interface StreamInfo {
  index: number;            // Stream index
  codec_name: string;       // Codec name
  codec_type: string;       // 'video', 'audio', 'subtitle', 'data'
  duration?: string;        // Stream duration in seconds
  width?: number;           // Video width (video streams)
  height?: number;          // Video height (video streams)
  sample_rate?: string;     // Audio sample rate (audio streams)
  channels?: number;        // Audio channel count (audio streams)
  r_frame_rate?: string;    // Video frame rate as ratio (video streams)
}

/**
 * Format information from FFprobe
 */
interface FormatInfo {
  filename: string;         // Input filename
  format_name: string;      // Format name
  duration: string;         // Duration in seconds
  size: string;             // File size in bytes
  bit_rate: string;         // Overall bitrate
  nb_streams: number;       // Number of streams
  tags?: { [key: string]: string }; // Metadata tags
}

/**
 * Time specification for screenshots
 */
type TimeMarkSpec = 
  | string                  // '25%', '50%', '01:30:00.500'
  | number;                 // Seconds as number

/**
 * Complex filter specification
 */
type ComplexFilterSpec = 
  | string                  // Single filter string
  | FilterObject[];         // Array of filter objects

/**
 * Filter object for complex filtergraphs
 */
interface FilterObject {
  filter: string;           // Filter name
  options?: string | Object; // Filter parameters
  inputs?: string[];        // Input stream labels
  outputs?: string[];       // Output stream labels
}

docs

audio-processing.md

configuration.md

index.md

input-management.md

output-management.md

processing-control.md

special-features.md

video-processing.md

tile.json