simple-ffmpegjs 0.5.3 → 0.5.5

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/README.md CHANGED
@@ -8,6 +8,7 @@
8
8
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
9
9
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg" alt="Node.js ≥20"></a>
10
10
  <a href="https://codecov.io/gh/Fats403/simple-ffmpegjs"><img src="https://codecov.io/gh/Fats403/simple-ffmpegjs/branch/main/graph/badge.svg" alt="Coverage"></a>
11
+ <img src="https://img.shields.io/badge/dependencies-0-brightgreen.svg" alt="Zero Dependencies">
11
12
  </p>
12
13
 
13
14
  <p align="center">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-ffmpegjs",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
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",
@@ -11,6 +11,10 @@
11
11
  * `position`, it is placed immediately after the previous clip on the
12
12
  * same track (visual or audio). The first clip defaults to position 0.
13
13
  *
14
+ * 3. **fullDuration**: If an effect or text clip has `fullDuration: true`,
15
+ * its position defaults to 0 and end is resolved later in _prepareExport()
16
+ * once the visual timeline duration is known.
17
+ *
14
18
  * Clips are shallow-cloned — the caller's original objects are not mutated.
15
19
  */
16
20
 
@@ -48,6 +52,35 @@ function resolveClips(clips) {
48
52
  const c = { ...clip };
49
53
  const path = `clips[${index}]`;
50
54
 
55
+ // ── fullDuration shorthand ─────────────────────────────────────────
56
+ if (c.fullDuration === true) {
57
+ if (c.end != null) {
58
+ errors.push({
59
+ code: "INVALID_VALUE",
60
+ path: `${path}`,
61
+ message:
62
+ "Cannot specify both 'fullDuration' and 'end'. fullDuration spans the entire visual timeline automatically.",
63
+ received: { fullDuration: true, end: c.end },
64
+ });
65
+ return c;
66
+ }
67
+ if (c.duration != null) {
68
+ errors.push({
69
+ code: "INVALID_VALUE",
70
+ path: `${path}`,
71
+ message:
72
+ "Cannot specify both 'fullDuration' and 'duration'. fullDuration spans the entire visual timeline automatically.",
73
+ received: { fullDuration: true, duration: c.duration },
74
+ });
75
+ return c;
76
+ }
77
+ // Default position to 0; end is resolved in _prepareExport()
78
+ if (c.position == null) {
79
+ c.position = 0;
80
+ }
81
+ return c;
82
+ }
83
+
51
84
  // ── Conflict check: duration + end ──────────────────────────────────
52
85
  if (c.duration != null && c.end != null) {
53
86
  errors.push({
@@ -388,7 +388,7 @@ function validateEffectClip(clip, path, errors) {
388
388
  * Validate a single clip and return issues
389
389
  */
390
390
  function validateClip(clip, index, options = {}) {
391
- const { skipFileChecks = false } = options;
391
+ const { skipFileChecks = false, skipExtensionsCheck = false } = options;
392
392
  const errors = [];
393
393
  const warnings = [];
394
394
  const path = `clips[${index}]`;
@@ -474,12 +474,37 @@ function validateClip(clip, index, options = {}) {
474
474
  }
475
475
  }
476
476
 
477
- // Types that require position/end on timeline
477
+ // fullDuration validation
478
+ const fullDurationTypes = ["effect", "text"];
479
+ if (clip.fullDuration != null) {
480
+ if (clip.fullDuration !== true) {
481
+ errors.push(
482
+ createIssue(
483
+ ValidationCodes.INVALID_VALUE,
484
+ `${path}.fullDuration`,
485
+ "fullDuration must be true when specified",
486
+ clip.fullDuration,
487
+ ),
488
+ );
489
+ } else if (!fullDurationTypes.includes(clip.type)) {
490
+ errors.push(
491
+ createIssue(
492
+ ValidationCodes.INVALID_VALUE,
493
+ `${path}.fullDuration`,
494
+ `fullDuration is only supported on ${fullDurationTypes.join(", ")} clips`,
495
+ clip.type,
496
+ ),
497
+ );
498
+ }
499
+ }
500
+
501
+ // Types that require position/end on timeline (unless fullDuration is set)
502
+ const hasFullDuration = clip.fullDuration === true && fullDurationTypes.includes(clip.type);
478
503
  const requiresTimeline = ["video", "audio", "text", "image", "color", "effect"].includes(
479
504
  clip.type,
480
505
  );
481
506
 
482
- if (requiresTimeline) {
507
+ if (requiresTimeline && !hasFullDuration) {
483
508
  if (typeof clip.position !== "number") {
484
509
  errors.push(
485
510
  createIssue(
@@ -614,7 +639,9 @@ function validateClip(clip, index, options = {}) {
614
639
  } catch (_) {}
615
640
  }
616
641
 
617
- validateMediaUrlExtension(clip, path, errors);
642
+ if (!skipExtensionsCheck) {
643
+ validateMediaUrlExtension(clip, path, errors);
644
+ }
618
645
 
619
646
  if (typeof clip.cutFrom === "number") {
620
647
  if (!Number.isFinite(clip.cutFrom)) {
@@ -1381,6 +1408,7 @@ function validateTimelineGaps(clips) {
1381
1408
  * @param {Array} clips - Array of clip objects to validate
1382
1409
  * @param {Object} options - Validation options
1383
1410
  * @param {boolean} options.skipFileChecks - Skip file existence checks (useful for AI validation)
1411
+ * @param {boolean} options.skipExtensionsCheck - Skip media extension/type checks (video/image)
1384
1412
  * @returns {Object} Validation result { valid, errors, warnings }
1385
1413
  */
1386
1414
  function validateConfig(clips, options = {}) {
@@ -8,9 +8,10 @@ module.exports = {
8
8
  effect: "vignette" | "filmGrain" | "gaussianBlur" | "colorAdjust"
9
9
  | "sepia" | "blackAndWhite" | "sharpen" | "chromaticAberration"
10
10
  | "letterbox"; // Required: effect kind
11
- position: number; // Required: start time on timeline (seconds)
12
- end?: number; // End time on timeline (seconds). Use end OR duration, not both.
13
- duration?: number; // Duration in seconds (alternative to end). end = position + duration.
11
+ position?: number; // Start time on timeline (seconds). Required unless fullDuration is true.
12
+ end?: number; // End time on timeline (seconds). Use end OR duration, not both. Mutually exclusive with fullDuration.
13
+ duration?: number; // Duration in seconds (alternative to end). end = position + duration. Mutually exclusive with fullDuration.
14
+ fullDuration?: boolean; // When true, spans the full visual timeline. Mutually exclusive with end and duration.
14
15
  fadeIn?: number; // Optional: seconds to ramp in from 0 to full intensity
15
16
  fadeOut?: number; // Optional: seconds to ramp out from full intensity to 0
16
17
  params: EffectParams; // Required: effect-specific parameters
@@ -116,7 +117,7 @@ module.exports = {
116
117
  "Effect clips are adjustment layers: they modify underlying video during their active window.",
117
118
  "Effects do not satisfy visual timeline continuity checks and do not fill gaps.",
118
119
  "Use duration instead of end to specify length: end = position + duration. Cannot use both.",
119
- "position is required for effect clips (no auto-sequencing).",
120
+ "position is required unless fullDuration: true is set, which spans the entire visual timeline.",
120
121
  "fadeIn/fadeOut are optional linear envelope controls that avoid abrupt on/off changes.",
121
122
  "params.amount is a normalized blend amount from 0 to 1 (default: 1).",
122
123
  "filmGrain: use params.strength (0-1) for noise intensity, params.amount for blend alpha.",
@@ -5,9 +5,10 @@ module.exports = {
5
5
  "Render text overlays on the video with multiple display modes, positioning, styling, and animations.",
6
6
  schema: `{
7
7
  type: "text"; // Required: clip type identifier
8
- position: number; // Required: start time on timeline (seconds)
9
- end?: number; // End time on timeline (seconds). Use end OR duration, not both.
10
- duration?: number; // Duration in seconds (alternative to end). end = position + duration.
8
+ position?: number; // Start time on timeline (seconds). Required unless fullDuration is true.
9
+ end?: number; // End time on timeline (seconds). Use end OR duration, not both. Mutually exclusive with fullDuration.
10
+ duration?: number; // Duration in seconds (alternative to end). end = position + duration. Mutually exclusive with fullDuration.
11
+ fullDuration?: boolean; // When true, spans the full visual timeline. Mutually exclusive with end and duration.
11
12
 
12
13
  // Content
13
14
  text?: string; // Text content (required for "static" mode)
@@ -63,6 +63,8 @@ class SIMPLEFFMPEG {
63
63
  * @param {number} options.fps - Frames per second (default: 30)
64
64
  * @param {string} options.preset - Platform preset ('tiktok', 'youtube', 'instagram-post', etc.)
65
65
  * @param {string} options.validationMode - Validation behavior: 'warn' or 'strict' (default: 'warn')
66
+ * @param {boolean} options.skipFileChecks - Skip file existence checks during load() validation
67
+ * @param {boolean} options.skipExtensionsCheck - Skip media URL extension/type checks (video/image) during load() validation
66
68
  * @param {string} options.fontFile - Default font file path (.ttf, .otf) applied to all text clips unless overridden per-clip
67
69
  * @param {string} options.emojiFont - 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).
68
70
  * @param {string} options.tempDir - Custom directory for temporary files (gradient images, unrotated videos, intermediate renders). Defaults to os.tmpdir(). Useful for fast SSDs, ramdisks, or environments with constrained /tmp.
@@ -97,6 +99,8 @@ class SIMPLEFFMPEG {
97
99
  width: options.width || presetConfig.width || C.DEFAULT_WIDTH,
98
100
  height: options.height || presetConfig.height || C.DEFAULT_HEIGHT,
99
101
  validationMode: options.validationMode || C.DEFAULT_VALIDATION_MODE,
102
+ skipFileChecks: options.skipFileChecks === true,
103
+ skipExtensionsCheck: options.skipExtensionsCheck === true,
100
104
  preset: options.preset || null,
101
105
  fontFile: options.fontFile || null,
102
106
  emojiFont: options.emojiFont || null,
@@ -279,6 +283,9 @@ class SIMPLEFFMPEG {
279
283
  * @param {string} clipObjs[].text - Text content (for text clips)
280
284
  * @param {string} clipObjs[].mode - Text mode: 'static', 'word-replace', 'word-sequential', 'karaoke'
281
285
  * @param {string} clipObjs[].kenBurns - Ken Burns effect for images: 'zoom-in', 'zoom-out', 'pan-left', etc.
286
+ * @param {Object} options - Load options
287
+ * @param {boolean} options.skipFileChecks - Override file existence checks for media URLs
288
+ * @param {boolean} options.skipExtensionsCheck - Override extension/type validation for media URLs
282
289
  * @returns {Promise<void>} Resolves when all clips are loaded
283
290
  * @throws {ValidationError} If clip configuration is invalid
284
291
  *
@@ -288,7 +295,7 @@ class SIMPLEFFMPEG {
288
295
  * { type: 'text', text: 'Hello', position: 1, end: 4, fontSize: 48 }
289
296
  * ]);
290
297
  */
291
- async load(clipObjs) {
298
+ async load(clipObjs, options = {}) {
292
299
  // Guard against concurrent load() calls
293
300
  if (this._isLoading) {
294
301
  throw new SimpleffmpegError(
@@ -308,11 +315,21 @@ class SIMPLEFFMPEG {
308
315
 
309
316
  // Resolve shorthand: duration → end, auto-sequential positioning
310
317
  const resolved = resolveClips(clipObjs);
318
+ const skipExtensionsCheck =
319
+ typeof options.skipExtensionsCheck === "boolean"
320
+ ? options.skipExtensionsCheck
321
+ : this.options.skipExtensionsCheck;
322
+ const skipFileChecks =
323
+ typeof options.skipFileChecks === "boolean"
324
+ ? options.skipFileChecks
325
+ : this.options.skipFileChecks;
311
326
 
312
327
  // Merge resolution errors into validation
313
328
  const result = validateConfig(resolved.clips, {
314
329
  width: this.options.width,
315
330
  height: this.options.height,
331
+ skipFileChecks,
332
+ skipExtensionsCheck,
316
333
  });
317
334
 
318
335
  // Prepend resolution errors (e.g. duration+end conflict)
@@ -561,6 +578,20 @@ class SIMPLEFFMPEG {
561
578
  }
562
579
  }
563
580
 
581
+ // Expand fullDuration clips now that finalVisualEnd is known
582
+ for (const clip of this.effectClips) {
583
+ if (clip.fullDuration === true) {
584
+ clip.position = clip.position ?? 0;
585
+ clip.end = finalVisualEnd;
586
+ }
587
+ }
588
+ for (const clip of this.textClips) {
589
+ if (clip.fullDuration === true) {
590
+ clip.position = clip.position ?? 0;
591
+ clip.end = finalVisualEnd;
592
+ }
593
+ }
594
+
564
595
  // Overlay effects (adjustment layer clips) on the composed video output.
565
596
  if (this.effectClips.length > 0 && hasVideo && finalVideoLabel) {
566
597
  const effectRes = buildEffectFilters(this.effectClips, finalVideoLabel);
@@ -1272,6 +1303,7 @@ class SIMPLEFFMPEG {
1272
1303
  * @param {Array} clips - Array of clip objects to validate
1273
1304
  * @param {Object} options - Validation options
1274
1305
  * @param {boolean} options.skipFileChecks - Skip file existence checks (useful for AI)
1306
+ * @param {boolean} options.skipExtensionsCheck - Skip media URL extension/type checks (video/image)
1275
1307
  * @returns {Object} Validation result { valid, errors, warnings }
1276
1308
  *
1277
1309
  * @example
package/types/index.d.mts CHANGED
@@ -4,9 +4,7 @@ declare namespace SIMPLEFFMPEG {
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
6
6
  /** Base error class for all simple-ffmpeg errors */
7
- class SimpleffmpegError extends Error {
8
- name: "SimpleffmpegError";
9
- }
7
+ class SimpleffmpegError extends Error {}
10
8
 
11
9
  /** Thrown when clip validation fails */
12
10
  class ValidationError extends SimpleffmpegError {
@@ -159,8 +157,12 @@ declare namespace SIMPLEFFMPEG {
159
157
  interface TextClip {
160
158
  type: "text";
161
159
  text?: string;
162
- position: number;
163
- end: number;
160
+ /** Start time on timeline in seconds. Required unless fullDuration is true. */
161
+ position?: number;
162
+ /** End time on timeline in seconds. Mutually exclusive with fullDuration. */
163
+ end?: number;
164
+ /** When true, the clip spans the full visual timeline (position 0 to end of last video/image/color clip). Mutually exclusive with end and duration. */
165
+ fullDuration?: boolean;
164
166
  mode?: TextMode;
165
167
  words?: TextWordWindow[];
166
168
  wordTimestamps?: number[];
@@ -337,12 +339,14 @@ declare namespace SIMPLEFFMPEG {
337
339
  interface EffectClip {
338
340
  type: "effect";
339
341
  effect: EffectName;
340
- /** Start time on timeline in seconds. Required for effect clips. */
341
- position: number;
342
- /** End time on timeline in seconds. Mutually exclusive with duration. */
342
+ /** Start time on timeline in seconds. Required unless fullDuration is true. */
343
+ position?: number;
344
+ /** End time on timeline in seconds. Mutually exclusive with duration and fullDuration. */
343
345
  end?: number;
344
- /** Duration in seconds (alternative to end). end = position + duration. */
346
+ /** Duration in seconds (alternative to end). end = position + duration. Mutually exclusive with fullDuration. */
345
347
  duration?: number;
348
+ /** When true, the clip spans the full visual timeline (position 0 to end of last video/image/color clip). Mutually exclusive with end and duration. */
349
+ fullDuration?: boolean;
346
350
  /** Ramp-in duration in seconds */
347
351
  fadeIn?: number;
348
352
  /** Ramp-out duration in seconds */
@@ -430,6 +434,8 @@ declare namespace SIMPLEFFMPEG {
430
434
  interface ValidateOptions {
431
435
  /** Skip file existence checks (useful for AI generating configs before files exist) */
432
436
  skipFileChecks?: boolean;
437
+ /** Skip media URL extension/type checks for video/image clips */
438
+ skipExtensionsCheck?: boolean;
433
439
  /** Project width - used to validate Ken Burns images are large enough */
434
440
  width?: number;
435
441
  /** Project height - used to validate Ken Burns images are large enough */
@@ -449,6 +455,10 @@ declare namespace SIMPLEFFMPEG {
449
455
  height?: number;
450
456
  /** Validation mode: 'warn' logs warnings, 'strict' throws on warnings (default: 'warn') */
451
457
  validationMode?: "warn" | "strict";
458
+ /** Skip file existence checks during load() validation */
459
+ skipFileChecks?: boolean;
460
+ /** Skip media URL extension/type checks for video/image clips during load() validation */
461
+ skipExtensionsCheck?: boolean;
452
462
  /** Default font file path (.ttf, .otf) applied to all text clips. Individual clips can override this with their own fontFile. */
453
463
  fontFile?: string;
454
464
  /** 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). */
@@ -864,6 +874,14 @@ declare namespace SIMPLEFFMPEG {
864
874
  totalDuration: number;
865
875
  }
866
876
 
877
+ /** Options for load() */
878
+ interface LoadOptions {
879
+ /** Override file existence checks for media URLs */
880
+ skipFileChecks?: boolean;
881
+ /** Override extension/type validation for media URLs (video/image) */
882
+ skipExtensionsCheck?: boolean;
883
+ }
884
+
867
885
  // ─────────────────────────────────────────────────────────────────────────────
868
886
  // Media Info (probe)
869
887
  // ─────────────────────────────────────────────────────────────────────────────
@@ -907,8 +925,12 @@ declare class SIMPLEFFMPEG {
907
925
  /**
908
926
  * Load clips into the project
909
927
  * @param clips Array of clip descriptors (video, audio, text, image, music)
928
+ * @param options Load options
910
929
  */
911
- load(clips: SIMPLEFFMPEG.Clip[]): Promise<void[]>;
930
+ load(
931
+ clips: SIMPLEFFMPEG.Clip[],
932
+ options?: SIMPLEFFMPEG.LoadOptions
933
+ ): Promise<void[]>;
912
934
 
913
935
  /**
914
936
  * Get a preview of the FFmpeg command without executing it (dry-run)
@@ -979,6 +1001,26 @@ declare class SIMPLEFFMPEG {
979
1001
  */
980
1002
  static getDuration(clips: SIMPLEFFMPEG.Clip[]): number;
981
1003
 
1004
+ /**
1005
+ * Calculate the total transition overlap for a clips configuration.
1006
+ * Returns the total seconds consumed by xfade transition overlaps
1007
+ * among visual clips (video, image, color).
1008
+ *
1009
+ * Pure function — same clips always produce the same result. No file I/O.
1010
+ *
1011
+ * @param clips - Array of clip objects
1012
+ * @returns Total transition overlap in seconds
1013
+ *
1014
+ * @example
1015
+ * const overlap = SIMPLEFFMPEG.getTransitionOverlap([
1016
+ * { type: "video", url: "./a.mp4", duration: 5 },
1017
+ * { type: "video", url: "./b.mp4", duration: 10,
1018
+ * transition: { type: "fade", duration: 0.5 } },
1019
+ * ]);
1020
+ * // overlap === 0.5
1021
+ */
1022
+ static getTransitionOverlap(clips: SIMPLEFFMPEG.Clip[]): number;
1023
+
982
1024
  /**
983
1025
  * Probe a media file and return comprehensive metadata.
984
1026
  *
@@ -1066,36 +1108,6 @@ declare class SIMPLEFFMPEG {
1066
1108
  */
1067
1109
  static formatValidationResult(result: SIMPLEFFMPEG.ValidationResult): string;
1068
1110
 
1069
- /**
1070
- * Validation error codes for programmatic handling
1071
- */
1072
- static readonly ValidationCodes: typeof SIMPLEFFMPEG.ValidationCodes;
1073
-
1074
- /**
1075
- * Base error class for all simple-ffmpeg errors
1076
- */
1077
- static readonly SimpleffmpegError: typeof SIMPLEFFMPEG.SimpleffmpegError;
1078
-
1079
- /**
1080
- * Thrown when clip validation fails
1081
- */
1082
- static readonly ValidationError: typeof SIMPLEFFMPEG.ValidationError;
1083
-
1084
- /**
1085
- * Thrown when FFmpeg command execution fails
1086
- */
1087
- static readonly FFmpegError: typeof SIMPLEFFMPEG.FFmpegError;
1088
-
1089
- /**
1090
- * Thrown when a media file cannot be found or accessed
1091
- */
1092
- static readonly MediaNotFoundError: typeof SIMPLEFFMPEG.MediaNotFoundError;
1093
-
1094
- /**
1095
- * Thrown when export is cancelled via AbortSignal
1096
- */
1097
- static readonly ExportCancelledError: typeof SIMPLEFFMPEG.ExportCancelledError;
1098
-
1099
1111
  /**
1100
1112
  * Get the clip schema as formatted prompt-ready text.
1101
1113
  * Returns a structured description of all clip types accepted by load(),
package/types/index.d.ts CHANGED
@@ -4,9 +4,7 @@ declare namespace SIMPLEFFMPEG {
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
6
6
  /** Base error class for all simple-ffmpeg errors */
7
- class SimpleffmpegError extends Error {
8
- name: "SimpleffmpegError";
9
- }
7
+ class SimpleffmpegError extends Error {}
10
8
 
11
9
  /** Thrown when clip validation fails */
12
10
  class ValidationError extends SimpleffmpegError {
@@ -159,8 +157,12 @@ declare namespace SIMPLEFFMPEG {
159
157
  interface TextClip {
160
158
  type: "text";
161
159
  text?: string;
162
- position: number;
163
- end: number;
160
+ /** Start time on timeline in seconds. Required unless fullDuration is true. */
161
+ position?: number;
162
+ /** End time on timeline in seconds. Mutually exclusive with fullDuration. */
163
+ end?: number;
164
+ /** When true, the clip spans the full visual timeline (position 0 to end of last video/image/color clip). Mutually exclusive with end and duration. */
165
+ fullDuration?: boolean;
164
166
  mode?: TextMode;
165
167
  words?: TextWordWindow[];
166
168
  wordTimestamps?: number[];
@@ -337,12 +339,14 @@ declare namespace SIMPLEFFMPEG {
337
339
  interface EffectClip {
338
340
  type: "effect";
339
341
  effect: EffectName;
340
- /** Start time on timeline in seconds. Required for effect clips. */
341
- position: number;
342
- /** End time on timeline in seconds. Mutually exclusive with duration. */
342
+ /** Start time on timeline in seconds. Required unless fullDuration is true. */
343
+ position?: number;
344
+ /** End time on timeline in seconds. Mutually exclusive with duration and fullDuration. */
343
345
  end?: number;
344
- /** Duration in seconds (alternative to end). end = position + duration. */
346
+ /** Duration in seconds (alternative to end). end = position + duration. Mutually exclusive with fullDuration. */
345
347
  duration?: number;
348
+ /** When true, the clip spans the full visual timeline (position 0 to end of last video/image/color clip). Mutually exclusive with end and duration. */
349
+ fullDuration?: boolean;
346
350
  /** Ramp-in duration in seconds */
347
351
  fadeIn?: number;
348
352
  /** Ramp-out duration in seconds */
@@ -430,6 +434,8 @@ declare namespace SIMPLEFFMPEG {
430
434
  interface ValidateOptions {
431
435
  /** Skip file existence checks (useful for AI generating configs before files exist) */
432
436
  skipFileChecks?: boolean;
437
+ /** Skip media URL extension/type checks for video/image clips */
438
+ skipExtensionsCheck?: boolean;
433
439
  /** Project width - used to validate Ken Burns images are large enough */
434
440
  width?: number;
435
441
  /** Project height - used to validate Ken Burns images are large enough */
@@ -449,6 +455,10 @@ declare namespace SIMPLEFFMPEG {
449
455
  height?: number;
450
456
  /** Validation mode: 'warn' logs warnings, 'strict' throws on warnings (default: 'warn') */
451
457
  validationMode?: "warn" | "strict";
458
+ /** Skip file existence checks during load() validation */
459
+ skipFileChecks?: boolean;
460
+ /** Skip media URL extension/type checks for video/image clips during load() validation */
461
+ skipExtensionsCheck?: boolean;
452
462
  /** Default font file path (.ttf, .otf) applied to all text clips. Individual clips can override this with their own fontFile. */
453
463
  fontFile?: string;
454
464
  /** 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). */
@@ -864,6 +874,14 @@ declare namespace SIMPLEFFMPEG {
864
874
  totalDuration: number;
865
875
  }
866
876
 
877
+ /** Options for load() */
878
+ interface LoadOptions {
879
+ /** Override file existence checks for media URLs */
880
+ skipFileChecks?: boolean;
881
+ /** Override extension/type validation for media URLs (video/image) */
882
+ skipExtensionsCheck?: boolean;
883
+ }
884
+
867
885
  // ─────────────────────────────────────────────────────────────────────────────
868
886
  // Media Info (probe)
869
887
  // ─────────────────────────────────────────────────────────────────────────────
@@ -907,8 +925,12 @@ declare class SIMPLEFFMPEG {
907
925
  /**
908
926
  * Load clips into the project
909
927
  * @param clips Array of clip descriptors (video, audio, text, image, music)
928
+ * @param options Load options
910
929
  */
911
- load(clips: SIMPLEFFMPEG.Clip[]): Promise<void[]>;
930
+ load(
931
+ clips: SIMPLEFFMPEG.Clip[],
932
+ options?: SIMPLEFFMPEG.LoadOptions
933
+ ): Promise<void[]>;
912
934
 
913
935
  /**
914
936
  * Get a preview of the FFmpeg command without executing it (dry-run)
@@ -1086,36 +1108,6 @@ declare class SIMPLEFFMPEG {
1086
1108
  */
1087
1109
  static formatValidationResult(result: SIMPLEFFMPEG.ValidationResult): string;
1088
1110
 
1089
- /**
1090
- * Validation error codes for programmatic handling
1091
- */
1092
- static readonly ValidationCodes: typeof SIMPLEFFMPEG.ValidationCodes;
1093
-
1094
- /**
1095
- * Base error class for all simple-ffmpeg errors
1096
- */
1097
- static readonly SimpleffmpegError: typeof SIMPLEFFMPEG.SimpleffmpegError;
1098
-
1099
- /**
1100
- * Thrown when clip validation fails
1101
- */
1102
- static readonly ValidationError: typeof SIMPLEFFMPEG.ValidationError;
1103
-
1104
- /**
1105
- * Thrown when FFmpeg command execution fails
1106
- */
1107
- static readonly FFmpegError: typeof SIMPLEFFMPEG.FFmpegError;
1108
-
1109
- /**
1110
- * Thrown when a media file cannot be found or accessed
1111
- */
1112
- static readonly MediaNotFoundError: typeof SIMPLEFFMPEG.MediaNotFoundError;
1113
-
1114
- /**
1115
- * Thrown when export is cancelled via AbortSignal
1116
- */
1117
- static readonly ExportCancelledError: typeof SIMPLEFFMPEG.ExportCancelledError;
1118
-
1119
1111
  /**
1120
1112
  * Get the clip schema as formatted prompt-ready text.
1121
1113
  * Returns a structured description of all clip types accepted by load(),