simple-ffmpegjs 0.2.0 → 0.3.1

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.
package/types/index.d.mts CHANGED
@@ -44,7 +44,8 @@ declare namespace SIMPLEFFMPEG {
44
44
  | "text"
45
45
  | "music"
46
46
  | "backgroundAudio"
47
- | "image";
47
+ | "image"
48
+ | "subtitle";
48
49
 
49
50
  interface BaseClip {
50
51
  type: ClipType;
@@ -73,6 +74,8 @@ declare namespace SIMPLEFFMPEG {
73
74
  url: string;
74
75
  cutFrom?: number;
75
76
  volume?: number;
77
+ /** Loop the audio to fill the entire video duration */
78
+ loop?: boolean;
76
79
  }
77
80
 
78
81
  interface ImageClip extends BaseClip {
@@ -87,18 +90,25 @@ declare namespace SIMPLEFFMPEG {
87
90
  | "pan-down";
88
91
  }
89
92
 
90
- type TextMode = "static" | "word-replace" | "word-sequential";
93
+ type TextMode = "static" | "word-replace" | "word-sequential" | "karaoke";
91
94
  type TextAnimationType =
92
95
  | "none"
93
96
  | "fade-in"
97
+ | "fade-out"
94
98
  | "fade-in-out"
99
+ | "fade"
95
100
  | "pop"
96
- | "pop-bounce";
101
+ | "pop-bounce"
102
+ | "scale-in"
103
+ | "pulse"
104
+ | "typewriter";
97
105
 
98
106
  interface TextWordWindow {
99
107
  text: string;
100
108
  start: number;
101
109
  end: number;
110
+ /** Add line break after this word (for multi-line karaoke) */
111
+ lineBreak?: boolean;
102
112
  }
103
113
 
104
114
  interface TextClip {
@@ -125,6 +135,10 @@ declare namespace SIMPLEFFMPEG {
125
135
  x?: number;
126
136
  /** Absolute Y position in pixels */
127
137
  y?: number;
138
+ /** Pixel offset added to X position (works with x, xPercent, or center default) */
139
+ xOffset?: number;
140
+ /** Pixel offset added to Y position (works with y, yPercent, or center default) */
141
+ yOffset?: number;
128
142
 
129
143
  // Styling
130
144
  borderColor?: string;
@@ -139,9 +153,40 @@ declare namespace SIMPLEFFMPEG {
139
153
  // Animation
140
154
  animation?: {
141
155
  type: TextAnimationType;
156
+ /** Entry animation duration in seconds (default: 0.25) */
142
157
  in?: number;
158
+ /** Exit animation duration in seconds (default: same as in) */
143
159
  out?: number;
160
+ /** Animation intensity 0-1 for scale-in and pulse (default: 0.3) */
161
+ intensity?: number;
162
+ /** Speed for typewriter (sec/char, default: 0.05) or pulse (cycles/sec, default: 1) */
163
+ speed?: number;
144
164
  };
165
+
166
+ /** Highlight color for karaoke mode (default: '#FFFF00') */
167
+ highlightColor?: string;
168
+
169
+ /** Highlight style for karaoke mode: 'smooth' (gradual fill) or 'instant' (default: 'smooth') */
170
+ highlightStyle?: "smooth" | "instant";
171
+ }
172
+
173
+ /** Subtitle clip for importing external subtitle files */
174
+ interface SubtitleClip {
175
+ type: "subtitle";
176
+ /** Path to subtitle file (.srt, .ass, .ssa, .vtt) */
177
+ url: string;
178
+ /** Timeline position offset (default: 0) - adds to all subtitle timestamps */
179
+ position?: number;
180
+ /** Optional end time to cut off subtitles */
181
+ end?: number;
182
+
183
+ // Styling (for SRT/VTT import - ASS files use their own styles)
184
+ fontFamily?: string;
185
+ fontSize?: number;
186
+ fontColor?: string;
187
+ borderColor?: string;
188
+ borderWidth?: number;
189
+ opacity?: number;
145
190
  }
146
191
 
147
192
  type Clip =
@@ -149,18 +194,96 @@ declare namespace SIMPLEFFMPEG {
149
194
  | AudioClip
150
195
  | BackgroundMusicClip
151
196
  | ImageClip
152
- | TextClip;
197
+ | TextClip
198
+ | SubtitleClip;
153
199
 
154
200
  // ─────────────────────────────────────────────────────────────────────────────
155
201
  // Options
156
202
  // ─────────────────────────────────────────────────────────────────────────────
157
203
 
204
+ /** Platform preset names */
205
+ type PlatformPreset =
206
+ | "tiktok"
207
+ | "youtube-short"
208
+ | "instagram-reel"
209
+ | "instagram-story"
210
+ | "snapchat"
211
+ | "instagram-post"
212
+ | "instagram-square"
213
+ | "youtube"
214
+ | "twitter"
215
+ | "facebook"
216
+ | "landscape"
217
+ | "twitter-portrait"
218
+ | "instagram-portrait";
219
+
220
+ /** Platform preset configuration */
221
+ interface PresetConfig {
222
+ width: number;
223
+ height: number;
224
+ fps: number;
225
+ }
226
+
227
+ /** Validation error/warning codes */
228
+ const ValidationCodes: {
229
+ readonly INVALID_TYPE: "INVALID_TYPE";
230
+ readonly MISSING_REQUIRED: "MISSING_REQUIRED";
231
+ readonly INVALID_VALUE: "INVALID_VALUE";
232
+ readonly INVALID_RANGE: "INVALID_RANGE";
233
+ readonly INVALID_TIMELINE: "INVALID_TIMELINE";
234
+ readonly TIMELINE_GAP: "TIMELINE_GAP";
235
+ readonly FILE_NOT_FOUND: "FILE_NOT_FOUND";
236
+ readonly INVALID_FORMAT: "INVALID_FORMAT";
237
+ readonly INVALID_WORD_TIMING: "INVALID_WORD_TIMING";
238
+ readonly OUTSIDE_BOUNDS: "OUTSIDE_BOUNDS";
239
+ };
240
+
241
+ type ValidationCode = (typeof ValidationCodes)[keyof typeof ValidationCodes];
242
+
243
+ /** A single validation error or warning */
244
+ interface ValidationIssue {
245
+ /** Error code for programmatic handling */
246
+ code: ValidationCode;
247
+ /** Path to the problematic field (e.g., "clips[0].url") */
248
+ path: string;
249
+ /** Human-readable error message */
250
+ message: string;
251
+ /** The actual value that caused the issue (optional) */
252
+ received?: unknown;
253
+ }
254
+
255
+ /** Result from validate() */
256
+ interface ValidationResult {
257
+ /** Whether the configuration is valid (no errors) */
258
+ valid: boolean;
259
+ /** Array of validation errors (issues that will cause failures) */
260
+ errors: ValidationIssue[];
261
+ /** Array of validation warnings (potential issues that won't block) */
262
+ warnings: ValidationIssue[];
263
+ }
264
+
265
+ /** Options for validate() */
266
+ interface ValidateOptions {
267
+ /** Skip file existence checks (useful for AI generating configs before files exist) */
268
+ skipFileChecks?: boolean;
269
+ /** Gap handling mode - affects timeline gap validation */
270
+ fillGaps?: "none" | "black";
271
+ /** Project width - used to validate Ken Burns images are large enough */
272
+ width?: number;
273
+ /** Project height - used to validate Ken Burns images are large enough */
274
+ height?: number;
275
+ /** If true, undersized Ken Burns images will error instead of warn (default: false, images are auto-upscaled) */
276
+ strictKenBurns?: boolean;
277
+ }
278
+
158
279
  interface SIMPLEFFMPEGOptions {
159
- /** Frames per second (default: 30) */
280
+ /** Platform preset (e.g., 'tiktok', 'youtube', 'instagram-reel'). Sets width, height, fps. */
281
+ preset?: PlatformPreset;
282
+ /** Frames per second (default: 30, or from preset) */
160
283
  fps?: number;
161
- /** Output width in pixels (default: 1920) */
284
+ /** Output width in pixels (default: 1920, or from preset) */
162
285
  width?: number;
163
- /** Output height in pixels (default: 1080) */
286
+ /** Output height in pixels (default: 1080, or from preset) */
164
287
  height?: number;
165
288
  /** Validation mode: 'warn' logs warnings, 'strict' throws on warnings (default: 'warn') */
166
289
  validationMode?: "warn" | "strict";
@@ -251,6 +374,65 @@ declare namespace SIMPLEFFMPEG {
251
374
 
252
375
  type ResolutionPreset = "480p" | "720p" | "1080p" | "1440p" | "4k";
253
376
 
377
+ // ─────────────────────────────────────────────────────────────────────────────
378
+ // Watermark Types
379
+ // ─────────────────────────────────────────────────────────────────────────────
380
+
381
+ /** Preset position for watermarks */
382
+ type WatermarkPositionPreset =
383
+ | "top-left"
384
+ | "top-right"
385
+ | "bottom-left"
386
+ | "bottom-right"
387
+ | "center";
388
+
389
+ /** Custom position using percentages (0-1) */
390
+ interface WatermarkPositionPercent {
391
+ xPercent: number;
392
+ yPercent: number;
393
+ }
394
+
395
+ /** Custom position using pixels */
396
+ interface WatermarkPositionPixel {
397
+ x: number;
398
+ y: number;
399
+ }
400
+
401
+ type WatermarkPosition =
402
+ | WatermarkPositionPreset
403
+ | WatermarkPositionPercent
404
+ | WatermarkPositionPixel;
405
+
406
+ interface BaseWatermarkOptions {
407
+ position?: WatermarkPosition;
408
+ margin?: number;
409
+ opacity?: number;
410
+ startTime?: number;
411
+ endTime?: number;
412
+ }
413
+
414
+ interface ImageWatermarkOptions extends BaseWatermarkOptions {
415
+ type: "image";
416
+ url: string;
417
+ scale?: number;
418
+ }
419
+
420
+ interface TextWatermarkOptions extends BaseWatermarkOptions {
421
+ type: "text";
422
+ text: string;
423
+ fontSize?: number;
424
+ fontColor?: string;
425
+ fontFamily?: string;
426
+ fontFile?: string;
427
+ borderColor?: string;
428
+ borderWidth?: number;
429
+ shadowColor?: string;
430
+ shadowX?: number;
431
+ shadowY?: number;
432
+ }
433
+
434
+ type WatermarkOptions = ImageWatermarkOptions | TextWatermarkOptions;
435
+
254
436
  interface ExportOptions {
255
437
  // Output
256
438
  outputPath?: string;
@@ -302,6 +484,16 @@ declare namespace SIMPLEFFMPEG {
302
484
  intermediateVideoCodec?: string;
303
485
  intermediateCrf?: number;
304
486
  intermediatePreset?: string;
487
+
488
+ // Watermark
489
+ watermark?: WatermarkOptions;
490
+
491
+ // Timeline
492
+ /**
493
+ * Automatically adjust text/subtitle timings to compensate for timeline
494
+ * compression caused by xfade transitions (default: true).
495
+ */
496
+ compensateTransitions?: boolean;
305
497
  }
306
498
 
307
499
  /** Result from preview() method */
@@ -337,6 +529,73 @@ declare class SIMPLEFFMPEG {
337
529
  * @param options Export options including outputPath, onProgress, and signal
338
530
  */
339
531
  export(options?: SIMPLEFFMPEG.ExportOptions): Promise<string>;
532
+
533
+ /**
534
+ * Get available platform presets
535
+ */
536
+ static getPresets(): Record<
537
+ SIMPLEFFMPEG.PlatformPreset,
538
+ SIMPLEFFMPEG.PresetConfig
539
+ >;
540
+
541
+ /**
542
+ * Get list of available preset names
543
+ */
544
+ static getPresetNames(): SIMPLEFFMPEG.PlatformPreset[];
545
+
546
+ /**
547
+ * Validate clips configuration without creating a project.
548
+ * Useful for AI feedback loops and pre-validation.
549
+ *
550
+ * @param clips - Array of clip objects to validate
551
+ * @param options - Validation options
552
+ * @returns Validation result with valid flag, errors, and warnings
553
+ *
554
+ * @example
555
+ * const result = SIMPLEFFMPEG.validate(clips, { skipFileChecks: true });
556
+ * if (!result.valid) {
557
+ * result.errors.forEach(e => console.log(`[${e.code}] ${e.path}: ${e.message}`));
558
+ * }
559
+ */
560
+ static validate(
561
+ clips: SIMPLEFFMPEG.Clip[],
562
+ options?: SIMPLEFFMPEG.ValidateOptions
563
+ ): SIMPLEFFMPEG.ValidationResult;
564
+
565
+ /**
566
+ * Format validation result as human-readable string
567
+ */
568
+ static formatValidationResult(result: SIMPLEFFMPEG.ValidationResult): string;
569
+
570
+ /**
571
+ * Validation error codes for programmatic handling
572
+ */
573
+ static readonly ValidationCodes: typeof SIMPLEFFMPEG.ValidationCodes;
574
+
575
+ /**
576
+ * Base error class for all simple-ffmpeg errors
577
+ */
578
+ static readonly SimpleffmpegError: typeof SIMPLEFFMPEG.SimpleffmpegError;
579
+
580
+ /**
581
+ * Thrown when clip validation fails
582
+ */
583
+ static readonly ValidationError: typeof SIMPLEFFMPEG.ValidationError;
584
+
585
+ /**
586
+ * Thrown when FFmpeg command execution fails
587
+ */
588
+ static readonly FFmpegError: typeof SIMPLEFFMPEG.FFmpegError;
589
+
590
+ /**
591
+ * Thrown when a media file cannot be found or accessed
592
+ */
593
+ static readonly MediaNotFoundError: typeof SIMPLEFFMPEG.MediaNotFoundError;
594
+
595
+ /**
596
+ * Thrown when export is cancelled via AbortSignal
597
+ */
598
+ static readonly ExportCancelledError: typeof SIMPLEFFMPEG.ExportCancelledError;
340
599
  }
341
600
 
342
601
  export default SIMPLEFFMPEG;