simple-ffmpegjs 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +31 -1711
  2. package/package.json +1 -1
  3. package/types/index.d.mts +207 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-ffmpegjs",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Declarative video composition for Node.js — define clips, transitions, text, and audio as simple objects, and let FFmpeg handle the rest.",
5
5
  "author": "Brayden Blackwell <braydenblackwell21@gmail.com> (https://github.com/Fats403)",
6
6
  "license": "MIT",
package/types/index.d.mts CHANGED
@@ -167,9 +167,9 @@ declare namespace SIMPLEFFMPEG {
167
167
 
168
168
  // Font
169
169
  fontFile?: string;
170
- fontFamily?: string;
171
- fontSize?: number;
172
- fontColor?: string;
170
+ fontFamily?: string; // defaults to 'Sans' via fontconfig
171
+ fontSize?: number; // default 48
172
+ fontColor?: string; // default '#FFFFFF'
173
173
 
174
174
  // Position (xPercent/yPercent are percentages 0-1, x/y are pixels)
175
175
  /** Horizontal position as percentage (0 = left, 0.5 = center, 1 = right) */
@@ -270,19 +270,24 @@ declare namespace SIMPLEFFMPEG {
270
270
  | "letterbox";
271
271
 
272
272
  interface EffectParamsBase {
273
+ /** Base blend amount from 0 to 1 (default: 1) */
273
274
  amount?: number;
274
275
  }
275
276
 
276
277
  interface VignetteEffectParams extends EffectParamsBase {
278
+ /** Vignette angle in radians (default: PI/5) */
277
279
  angle?: number;
278
280
  }
279
281
 
280
282
  interface FilmGrainEffectParams extends EffectParamsBase {
283
+ /** Noise intensity 0-1 (default: 0.35). Independent from blend amount. */
281
284
  strength?: number;
285
+ /** Temporal grain changes every frame (default: true) */
282
286
  temporal?: boolean;
283
287
  }
284
288
 
285
289
  interface GaussianBlurEffectParams extends EffectParamsBase {
290
+ /** Gaussian blur sigma (default derived from amount) */
286
291
  sigma?: number;
287
292
  }
288
293
 
@@ -296,19 +301,24 @@ declare namespace SIMPLEFFMPEG {
296
301
  interface SepiaEffectParams extends EffectParamsBase {}
297
302
 
298
303
  interface BlackAndWhiteEffectParams extends EffectParamsBase {
304
+ /** Optional contrast boost (default: 1, range 0-3) */
299
305
  contrast?: number;
300
306
  }
301
307
 
302
308
  interface SharpenEffectParams extends EffectParamsBase {
309
+ /** Unsharp amount (default: 1.0, range 0-3) */
303
310
  strength?: number;
304
311
  }
305
312
 
306
313
  interface ChromaticAberrationEffectParams extends EffectParamsBase {
314
+ /** Horizontal pixel offset for R/B channels (default: 4, range 0-20) */
307
315
  shift?: number;
308
316
  }
309
317
 
310
318
  interface LetterboxEffectParams extends EffectParamsBase {
319
+ /** Bar height as fraction of frame height (default: 0.12, range 0-0.5) */
311
320
  size?: number;
321
+ /** Bar color (default: "black") */
312
322
  color?: string;
313
323
  }
314
324
 
@@ -327,11 +337,17 @@ declare namespace SIMPLEFFMPEG {
327
337
  interface EffectClip {
328
338
  type: "effect";
329
339
  effect: EffectName;
340
+ /** Start time on timeline in seconds. Required for effect clips. */
330
341
  position: number;
342
+ /** End time on timeline in seconds. Mutually exclusive with duration. */
331
343
  end?: number;
344
+ /** Duration in seconds (alternative to end). end = position + duration. */
332
345
  duration?: number;
346
+ /** Ramp-in duration in seconds */
333
347
  fadeIn?: number;
348
+ /** Ramp-out duration in seconds */
334
349
  fadeOut?: number;
350
+ /** Effect-specific params */
335
351
  params: EffectParams;
336
352
  }
337
353
 
@@ -435,6 +451,10 @@ declare namespace SIMPLEFFMPEG {
435
451
  validationMode?: "warn" | "strict";
436
452
  /** Default font file path (.ttf, .otf) applied to all text clips. Individual clips can override this with their own fontFile. */
437
453
  fontFile?: string;
454
+ /** Path to a .ttf/.otf emoji font for rendering emoji in text overlays (opt-in). Without this, emoji are silently stripped from text. Recommended: Noto Emoji (B&W outline). */
455
+ emojiFont?: string;
456
+ /** Custom directory for temporary files — gradient images, unrotated videos, intermediate renders, text/ASS temp files. Defaults to os.tmpdir() or the output directory depending on the operation. Useful for fast SSDs, ramdisks, or environments with constrained /tmp. */
457
+ tempDir?: string;
438
458
  }
439
459
 
440
460
  /** Log entry passed to onLog callback */
@@ -471,17 +491,66 @@ declare namespace SIMPLEFFMPEG {
471
491
  comment?: string;
472
492
  date?: string;
473
493
  genre?: string;
494
+ /** Custom metadata key-value pairs */
474
495
  custom?: Record<string, string>;
475
496
  }
476
497
 
477
498
  /** Thumbnail generation options */
478
499
  interface ThumbnailOptions {
500
+ /** Output path for thumbnail image */
479
501
  outputPath: string;
502
+ /** Time in seconds to capture (default: 0) */
480
503
  time?: number;
504
+ /** Thumbnail width (maintains aspect if height omitted) */
505
+ width?: number;
506
+ /** Thumbnail height (maintains aspect if width omitted) */
507
+ height?: number;
508
+ }
509
+
510
+ /** Keyframe extraction mode */
511
+ type KeyframeMode = "scene-change" | "interval";
512
+
513
+ /** Output format for extracted keyframes */
514
+ type KeyframeFormat = "jpeg" | "png";
515
+
516
+ /** Base options for SIMPLEFFMPEG.extractKeyframes() */
517
+ interface ExtractKeyframesBaseOptions {
518
+ /** Extraction mode: 'scene-change' detects visual transitions, 'interval' samples at fixed spacing (default: 'scene-change') */
519
+ mode?: KeyframeMode;
520
+ /** Scene detection sensitivity 0-1, lower = more frames (default: 0.3). Only for scene-change mode. */
521
+ sceneThreshold?: number;
522
+ /** Seconds between frames (default: 5). Only for interval mode. */
523
+ intervalSeconds?: number;
524
+ /** Maximum number of frames to extract */
525
+ maxFrames?: number;
526
+ /** Output image format (default: 'jpeg') */
527
+ format?: KeyframeFormat;
528
+ /** JPEG quality 1-31, lower is better (default: 2). Only applies to JPEG. */
529
+ quality?: number;
530
+ /** Output width in pixels (maintains aspect ratio if height omitted) */
481
531
  width?: number;
532
+ /** Output height in pixels (maintains aspect ratio if width omitted) */
482
533
  height?: number;
534
+ /** Custom directory for temporary files (default: os.tmpdir()). Only used when outputDir is not set. Useful for fast SSDs, ramdisks, or environments with constrained /tmp. */
535
+ tempDir?: string;
483
536
  }
484
537
 
538
+ /** Options with outputDir — writes to disk, returns string[] */
539
+ interface ExtractKeyframesToDiskOptions extends ExtractKeyframesBaseOptions {
540
+ /** Directory to write frame files to */
541
+ outputDir: string;
542
+ }
543
+
544
+ /** Options without outputDir — returns Buffer[] */
545
+ interface ExtractKeyframesToBufferOptions extends ExtractKeyframesBaseOptions {
546
+ outputDir?: undefined;
547
+ }
548
+
549
+ /** Combined options type for extractKeyframes */
550
+ type ExtractKeyframesOptions =
551
+ | ExtractKeyframesToDiskOptions
552
+ | ExtractKeyframesToBufferOptions;
553
+
485
554
  /** Options for SIMPLEFFMPEG.snapshot() — capture a single frame from a video */
486
555
  interface SnapshotOptions {
487
556
  /** Output image path (extension determines format: .jpg, .png, .webp, .bmp, .tiff) */
@@ -496,6 +565,7 @@ declare namespace SIMPLEFFMPEG {
496
565
  quality?: number;
497
566
  }
498
567
 
568
+ /** Hardware acceleration options */
499
569
  type HardwareAcceleration =
500
570
  | "auto"
501
571
  | "videotoolbox"
@@ -504,6 +574,7 @@ declare namespace SIMPLEFFMPEG {
504
574
  | "qsv"
505
575
  | "none";
506
576
 
577
+ /** Video codec options */
507
578
  type VideoCodec =
508
579
  | "libx264"
509
580
  | "libx265"
@@ -520,6 +591,7 @@ declare namespace SIMPLEFFMPEG {
520
591
  | "hevc_qsv"
521
592
  | string;
522
593
 
594
+ /** Audio codec options */
523
595
  type AudioCodec =
524
596
  | "aac"
525
597
  | "libmp3lame"
@@ -529,6 +601,7 @@ declare namespace SIMPLEFFMPEG {
529
601
  | "copy"
530
602
  | string;
531
603
 
604
+ /** Encoding preset options */
532
605
  type EncodingPreset =
533
606
  | "ultrafast"
534
607
  | "superfast"
@@ -540,6 +613,7 @@ declare namespace SIMPLEFFMPEG {
540
613
  | "slower"
541
614
  | "veryslow";
542
615
 
616
+ /** Resolution presets */
543
617
  type ResolutionPreset = "480p" | "720p" | "1080p" | "1440p" | "4k";
544
618
 
545
619
  // ─────────────────────────────────────────────────────────────────────────────
@@ -556,82 +630,147 @@ declare namespace SIMPLEFFMPEG {
556
630
 
557
631
  /** Custom position using percentages (0-1) */
558
632
  interface WatermarkPositionPercent {
633
+ /** Horizontal position as percentage (0 = left, 0.5 = center, 1 = right) */
559
634
  xPercent: number;
635
+ /** Vertical position as percentage (0 = top, 0.5 = center, 1 = bottom) */
560
636
  yPercent: number;
561
637
  }
562
638
 
563
639
  /** Custom position using pixels */
564
640
  interface WatermarkPositionPixel {
641
+ /** X position in pixels from left */
565
642
  x: number;
643
+ /** Y position in pixels from top */
566
644
  y: number;
567
645
  }
568
646
 
647
+ /** Watermark position options */
569
648
  type WatermarkPosition =
570
649
  | WatermarkPositionPreset
571
650
  | WatermarkPositionPercent
572
651
  | WatermarkPositionPixel;
573
652
 
653
+ /** Base watermark options shared by image and text watermarks */
574
654
  interface BaseWatermarkOptions {
655
+ /** Position preset or custom coordinates (default: 'bottom-right') */
575
656
  position?: WatermarkPosition;
657
+ /** Margin from edge in pixels when using preset positions (default: 20) */
576
658
  margin?: number;
659
+ /** Opacity from 0 (transparent) to 1 (opaque) (default: 1) */
577
660
  opacity?: number;
661
+ /** Start time in seconds (default: 0, start of video) */
578
662
  startTime?: number;
663
+ /** End time in seconds (default: end of video) */
579
664
  endTime?: number;
580
665
  }
581
666
 
667
+ /** Image watermark options */
582
668
  interface ImageWatermarkOptions extends BaseWatermarkOptions {
583
669
  type: "image";
670
+ /** Path to the watermark image file */
584
671
  url: string;
672
+ /** Scale relative to video width, 0-1 (default: 0.15, i.e., 15% of width) */
585
673
  scale?: number;
586
674
  }
587
675
 
676
+ /** Text watermark options */
588
677
  interface TextWatermarkOptions extends BaseWatermarkOptions {
589
678
  type: "text";
679
+ /** Text to display as watermark */
590
680
  text: string;
681
+ /** Font size in pixels (default: 24) */
591
682
  fontSize?: number;
683
+ /** Font color in hex format (default: '#FFFFFF') */
592
684
  fontColor?: string;
685
+ /** Font family name (default: 'Sans') */
593
686
  fontFamily?: string;
687
+ /** Path to custom font file */
594
688
  fontFile?: string;
689
+ /** Border/outline color */
595
690
  borderColor?: string;
691
+ /** Border/outline width in pixels */
596
692
  borderWidth?: number;
693
+ /** Shadow color */
597
694
  shadowColor?: string;
695
+ /** Shadow X offset */
598
696
  shadowX?: number;
697
+ /** Shadow Y offset */
599
698
  shadowY?: number;
600
699
  }
601
700
 
701
+ /** Watermark configuration - either image or text */
602
702
  type WatermarkOptions = ImageWatermarkOptions | TextWatermarkOptions;
603
703
 
604
704
  interface ExportOptions {
705
+ // ─────────────────────────────────────────────────────────────────────────
605
706
  // Output
707
+ // ─────────────────────────────────────────────────────────────────────────
708
+
709
+ /** Output file path (default: './output.mp4') */
606
710
  outputPath?: string;
607
711
 
712
+ // ─────────────────────────────────────────────────────────────────────────
608
713
  // Video Encoding
714
+ // ─────────────────────────────────────────────────────────────────────────
715
+
716
+ /** Video codec (default: 'libx264') */
609
717
  videoCodec?: VideoCodec;
718
+ /** Quality level 0-51, lower is better (default: 23) */
610
719
  crf?: number;
720
+ /** Encoding speed/quality tradeoff (default: 'medium') */
611
721
  preset?: EncodingPreset;
722
+ /** Target video bitrate (e.g., '5M', '2500k'). Overrides CRF when set. */
612
723
  videoBitrate?: string;
613
724
 
725
+ // ─────────────────────────────────────────────────────────────────────────
614
726
  // Audio Encoding
727
+ // ─────────────────────────────────────────────────────────────────────────
728
+
729
+ /** Audio codec (default: 'aac') */
615
730
  audioCodec?: AudioCodec;
731
+ /** Audio bitrate (default: '192k') */
616
732
  audioBitrate?: string;
733
+ /** Audio sample rate in Hz (default: 48000) */
617
734
  audioSampleRate?: number;
618
735
 
736
+ // ─────────────────────────────────────────────────────────────────────────
619
737
  // Hardware Acceleration
738
+ // ─────────────────────────────────────────────────────────────────────────
739
+
740
+ /** Hardware acceleration mode (default: 'none') */
620
741
  hwaccel?: HardwareAcceleration;
621
742
 
743
+ // ─────────────────────────────────────────────────────────────────────────
622
744
  // Output Resolution
745
+ // ─────────────────────────────────────────────────────────────────────────
746
+
747
+ /** Output width in pixels (scales the output) */
623
748
  outputWidth?: number;
749
+ /** Output height in pixels (scales the output) */
624
750
  outputHeight?: number;
751
+ /** Resolution preset ('720p', '1080p', '4k', etc.) */
625
752
  outputResolution?: ResolutionPreset;
626
753
 
754
+ // ─────────────────────────────────────────────────────────────────────────
627
755
  // Advanced Options
756
+ // ─────────────────────────────────────────────────────────────────────────
757
+
758
+ /** Export audio only (no video) */
628
759
  audioOnly?: boolean;
760
+ /** Enable two-pass encoding for better quality at target bitrate */
629
761
  twoPass?: boolean;
762
+ /** Metadata to embed in output file */
630
763
  metadata?: MetadataOptions;
764
+ /** Generate a thumbnail from the output */
631
765
  thumbnail?: ThumbnailOptions;
632
766
 
767
+ // ─────────────────────────────────────────────────────────────────────────
633
768
  // Debug & Logging
769
+ // ─────────────────────────────────────────────────────────────────────────
770
+
771
+ /** Enable verbose logging */
634
772
  verbose?: boolean;
773
+ /** FFmpeg log level (default: 'warning') */
635
774
  logLevel?:
636
775
  | "quiet"
637
776
  | "panic"
@@ -641,27 +780,49 @@ declare namespace SIMPLEFFMPEG {
641
780
  | "info"
642
781
  | "verbose"
643
782
  | "debug";
783
+ /** Save FFmpeg command to file for debugging */
644
784
  saveCommand?: string;
645
785
 
786
+ // ─────────────────────────────────────────────────────────────────────────
646
787
  // Callbacks & Control
788
+ // ─────────────────────────────────────────────────────────────────────────
789
+
790
+ /** Progress callback for monitoring export progress */
647
791
  onProgress?: (progress: ProgressInfo) => void;
648
792
  /** FFmpeg log callback for real-time stderr/stdout output */
649
793
  onLog?: (entry: LogEntry) => void;
794
+ /** AbortSignal for cancelling the export */
650
795
  signal?: AbortSignal;
651
796
 
652
- // Text Batching
797
+ // ─────────────────────────────────────────────────────────────────────────
798
+ // Text Batching (Advanced)
799
+ // ─────────────────────────────────────────────────────────────────────────
800
+
801
+ /** Maximum text overlay nodes per FFmpeg pass (default: 75) */
653
802
  textMaxNodesPerPass?: number;
803
+ /** Video codec for intermediate text passes (default: 'libx264') */
654
804
  intermediateVideoCodec?: string;
805
+ /** CRF for intermediate text passes (default: 18) */
655
806
  intermediateCrf?: number;
807
+ /** Preset for intermediate text passes (default: 'veryfast') */
656
808
  intermediatePreset?: string;
657
809
 
810
+ // ─────────────────────────────────────────────────────────────────────────
658
811
  // Watermark
812
+ // ─────────────────────────────────────────────────────────────────────────
813
+
814
+ /** Add a watermark overlay (image or text) to the video */
659
815
  watermark?: WatermarkOptions;
660
816
 
817
+ // ─────────────────────────────────────────────────────────────────────────
661
818
  // Timeline
819
+ // ─────────────────────────────────────────────────────────────────────────
820
+
662
821
  /**
663
822
  * Automatically adjust text/subtitle timings to compensate for timeline
664
823
  * compression caused by xfade transitions (default: true).
824
+ * When enabled, text positioned at "15s" will appear at the visual 15s mark
825
+ * even if transitions have compressed the actual timeline.
665
826
  */
666
827
  compensateTransitions?: boolean;
667
828
  }
@@ -765,6 +926,7 @@ declare class SIMPLEFFMPEG {
765
926
 
766
927
  /**
767
928
  * Get available platform presets
929
+ * @returns Map of preset names to their configurations
768
930
  */
769
931
  static getPresets(): Record<
770
932
  SIMPLEFFMPEG.PlatformPreset,
@@ -773,6 +935,7 @@ declare class SIMPLEFFMPEG {
773
935
 
774
936
  /**
775
937
  * Get list of available preset names
938
+ * @returns Array of preset names
776
939
  */
777
940
  static getPresetNames(): SIMPLEFFMPEG.PlatformPreset[];
778
941
 
@@ -858,6 +1021,46 @@ declare class SIMPLEFFMPEG {
858
1021
  options: SIMPLEFFMPEG.SnapshotOptions
859
1022
  ): Promise<string>;
860
1023
 
1024
+ /**
1025
+ * Extract keyframes from a video using scene-change detection or fixed time intervals.
1026
+ *
1027
+ * Scene-change mode uses FFmpeg's select=gt(scene,N) filter to detect visual transitions.
1028
+ * Interval mode extracts frames at fixed time intervals.
1029
+ *
1030
+ * When outputDir is provided, frames are written to disk and the method returns file paths.
1031
+ * Without outputDir, frames are returned as in-memory Buffer objects.
1032
+ *
1033
+ * @param filePath - Path to the source video file
1034
+ * @param options - Extraction options (with outputDir → string[], without → Buffer[])
1035
+ * @throws {SIMPLEFFMPEG.SimpleffmpegError} If arguments are invalid
1036
+ * @throws {SIMPLEFFMPEG.FFmpegError} If FFmpeg fails during extraction
1037
+ *
1038
+ * @example
1039
+ * // Scene-change detection — returns Buffer[]
1040
+ * const frames = await SIMPLEFFMPEG.extractKeyframes("./video.mp4", {
1041
+ * mode: "scene-change",
1042
+ * sceneThreshold: 0.4,
1043
+ * maxFrames: 8,
1044
+ * });
1045
+ *
1046
+ * @example
1047
+ * // Fixed interval — writes to disk, returns string[]
1048
+ * const paths = await SIMPLEFFMPEG.extractKeyframes("./video.mp4", {
1049
+ * mode: "interval",
1050
+ * intervalSeconds: 5,
1051
+ * outputDir: "./frames/",
1052
+ * format: "png",
1053
+ * });
1054
+ */
1055
+ static extractKeyframes(
1056
+ filePath: string,
1057
+ options: SIMPLEFFMPEG.ExtractKeyframesToDiskOptions
1058
+ ): Promise<string[]>;
1059
+ static extractKeyframes(
1060
+ filePath: string,
1061
+ options?: SIMPLEFFMPEG.ExtractKeyframesToBufferOptions
1062
+ ): Promise<Buffer[]>;
1063
+
861
1064
  /**
862
1065
  * Format validation result as human-readable string
863
1066
  */