remotion 4.1.0-alpha1 → 4.1.0-alpha10

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 (51) hide show
  1. package/dist/cjs/Composition.d.ts +26 -10
  2. package/dist/cjs/Composition.js +8 -18
  3. package/dist/cjs/CompositionManager.d.ts +11 -12
  4. package/dist/cjs/CompositionManagerContext.d.ts +4 -3
  5. package/dist/cjs/Img.d.ts +1 -1
  6. package/dist/cjs/Img.js +2 -2
  7. package/dist/cjs/NativeLayers.js +5 -4
  8. package/dist/cjs/RemotionRoot.js +6 -4
  9. package/dist/cjs/RenderAssetManager.d.ts +11 -0
  10. package/dist/cjs/RenderAssetManager.js +40 -0
  11. package/dist/cjs/ResolveCompositionConfig.js +24 -7
  12. package/dist/cjs/Still.d.ts +1 -1
  13. package/dist/cjs/audio/AudioForDevelopment.js +1 -1
  14. package/dist/cjs/audio/shared-audio-tags.d.ts +1 -1
  15. package/dist/cjs/audio/shared-audio-tags.js +5 -1
  16. package/dist/cjs/config/input-props.d.ts +1 -1
  17. package/dist/cjs/config/input-props.js +2 -1
  18. package/dist/cjs/delay-render.js +22 -14
  19. package/dist/cjs/freeze.js +6 -2
  20. package/dist/cjs/index.d.ts +17 -7
  21. package/dist/cjs/index.js +1 -1
  22. package/dist/cjs/input-props-serialization.d.ts +15 -0
  23. package/dist/cjs/input-props-serialization.js +49 -0
  24. package/dist/cjs/internals.d.ts +80 -62
  25. package/dist/cjs/internals.js +13 -0
  26. package/dist/cjs/loop/index.js +1 -2
  27. package/dist/cjs/props-if-has-props.d.ts +2 -2
  28. package/dist/cjs/resolve-video-config.d.ts +3 -2
  29. package/dist/cjs/resolve-video-config.js +23 -33
  30. package/dist/cjs/series/index.js +2 -3
  31. package/dist/cjs/spring/index.js +1 -1
  32. package/dist/cjs/static-file.js +11 -2
  33. package/dist/cjs/timeline-position-state.d.ts +5 -3
  34. package/dist/cjs/timeline-position-state.js +25 -7
  35. package/dist/cjs/use-unsafe-video-config.js +2 -1
  36. package/dist/cjs/use-video.d.ts +1 -1
  37. package/dist/cjs/use-video.js +3 -3
  38. package/dist/cjs/validation/validate-dimensions.d.ts +1 -1
  39. package/dist/cjs/validation/validate-dimensions.js +2 -2
  40. package/dist/cjs/validation/validate-duration-in-frames.d.ts +2 -3
  41. package/dist/cjs/validation/validate-duration-in-frames.js +6 -2
  42. package/dist/cjs/validation/validate-fps.d.ts +1 -1
  43. package/dist/cjs/validation/validate-fps.js +2 -2
  44. package/dist/cjs/version.d.ts +1 -1
  45. package/dist/cjs/version.js +1 -1
  46. package/dist/cjs/video/OffthreadVideo.js +6 -3
  47. package/dist/cjs/video/OffthreadVideoForRendering.js +8 -2
  48. package/dist/cjs/video-config.d.ts +2 -1
  49. package/dist/esm/index.mjs +323 -214
  50. package/dist/esm/version.mjs +1 -1
  51. package/package.json +2 -2
@@ -16,13 +16,14 @@ const NativeLayersProvider = ({ children, }) => {
16
16
  clipRegion,
17
17
  };
18
18
  }, [clipRegion, setClipRegion]);
19
- useLayoutEffect(() => {
20
- if (typeof window !== 'undefined') {
19
+ if (typeof window !== 'undefined') {
20
+ // eslint-disable-next-line react-hooks/rules-of-hooks
21
+ useLayoutEffect(() => {
21
22
  window.remotion_getClipRegion = () => {
22
23
  return clipRegion;
23
24
  };
24
- }
25
- }, [clipRegion, setClipRegion]);
25
+ }, [clipRegion, setClipRegion]);
26
+ }
26
27
  return (jsx(NativeLayersContext.Provider, { value: context, children: children }));
27
28
  };
28
29
 
@@ -58,7 +59,7 @@ function truthy(value) {
58
59
  }
59
60
 
60
61
  // Automatically generated on publish
61
- const VERSION = '4.1.0-alpha1';
62
+ const VERSION = '4.1.0-alpha10';
62
63
 
63
64
  const checkMultipleRemotionVersions = () => {
64
65
  if (typeof globalThis === 'undefined') {
@@ -292,6 +293,142 @@ const CompositionManager = createContext({
292
293
  currentCompositionMetadata: null,
293
294
  });
294
295
 
296
+ const problematicCharacters = {
297
+ '%3A': ':',
298
+ '%2F': '/',
299
+ '%3F': '?',
300
+ '%23': '#',
301
+ '%5B': '[',
302
+ '%5D': ']',
303
+ '%40': '@',
304
+ '%21': '!',
305
+ '%24': '$',
306
+ '%26': '&',
307
+ '%27': "'",
308
+ '%28': '(',
309
+ '%29': ')',
310
+ '%2A': '*',
311
+ '%2B': '+',
312
+ '%2C': ',',
313
+ '%3B': ';',
314
+ };
315
+ const didWarn$1 = {};
316
+ const warnOnce$1 = (message) => {
317
+ if (didWarn$1[message]) {
318
+ return;
319
+ }
320
+ console.warn(message);
321
+ didWarn$1[message] = true;
322
+ };
323
+ const includesHexOfUnsafeChar = (path) => {
324
+ for (const key of Object.keys(problematicCharacters)) {
325
+ if (path.includes(key)) {
326
+ return { containsHex: true, hexCode: key };
327
+ }
328
+ }
329
+ return { containsHex: false };
330
+ };
331
+ const trimLeadingSlash = (path) => {
332
+ if (path.startsWith('/')) {
333
+ return trimLeadingSlash(path.substring(1));
334
+ }
335
+ return path;
336
+ };
337
+ const inner = (path) => {
338
+ if (typeof window !== 'undefined' && window.remotion_staticBase) {
339
+ return `${window.remotion_staticBase}/${trimLeadingSlash(path)}`;
340
+ }
341
+ return `/${trimLeadingSlash(path)}`;
342
+ };
343
+ const encodeBySplitting = (path) => {
344
+ const splitBySlash = path.split('/');
345
+ const encodedArray = splitBySlash.map((element) => {
346
+ return encodeURIComponent(element);
347
+ });
348
+ const merged = encodedArray.join('/');
349
+ return merged;
350
+ };
351
+ /**
352
+ * @description Reference a file from the public/ folder. If the file does not appear in the autocomplete, type the path manually.
353
+ * @see [Documentation](https://www.remotion.dev/docs/staticfile)
354
+ */
355
+ const staticFile = (path) => {
356
+ if (path.startsWith('http://') || path.startsWith('https://')) {
357
+ throw new TypeError(`staticFile() does not support remote URLs - got "${path}". Instead, pass the URL without wrapping it in staticFile(). See: https://remotion.dev/docs/staticfile-remote-urls`);
358
+ }
359
+ if (path.startsWith('..') || path.startsWith('./')) {
360
+ throw new TypeError(`staticFile() does not support relative paths - got "${path}". Instead, pass the name of a file that is inside the public/ folder. See: https://remotion.dev/docs/staticfile-relative-paths`);
361
+ }
362
+ if (path.startsWith('/Users') ||
363
+ path.startsWith('/home') ||
364
+ path.startsWith('/tmp') ||
365
+ path.startsWith('/etc') ||
366
+ path.startsWith('/opt') ||
367
+ path.startsWith('/var') ||
368
+ path.startsWith('C:') ||
369
+ path.startsWith('D:') ||
370
+ path.startsWith('E:')) {
371
+ throw new TypeError(`staticFile() does not support absolute paths - got "${path}". Instead, pass the name of a file that is inside the public/ folder. See: https://remotion.dev/docs/staticfile-relative-paths`);
372
+ }
373
+ if (path.startsWith('public/')) {
374
+ throw new TypeError(`Do not include the public/ prefix when using staticFile() - got "${path}". See: https://remotion.dev/docs/staticfile-relative-paths`);
375
+ }
376
+ const includesHex = includesHexOfUnsafeChar(path);
377
+ if (includesHex.containsHex) {
378
+ warnOnce$1(`WARNING: You seem to pass an already encoded path (path contains ${includesHex.hexCode}). Since Remotion 4.0, the encoding is done by staticFile() itself. You may want to remove a encodeURIComponent() wrapping.`);
379
+ }
380
+ const preprocessed = encodeBySplitting(path);
381
+ const preparsed = inner(preprocessed);
382
+ if (!preparsed.startsWith('/')) {
383
+ return `/${preparsed}`;
384
+ }
385
+ return preparsed;
386
+ };
387
+
388
+ // Must keep this file in sync with the one in packages/lambda/src/shared/serialize-props.ts!
389
+ const DATE_TOKEN = 'remotion-date:';
390
+ const FILE_TOKEN = 'remotion-file:';
391
+ const serializeJSONWithDate = ({ data, indent, staticBase, }) => {
392
+ let customDateUsed = false;
393
+ let customFileUsed = false;
394
+ let mapUsed = false;
395
+ let setUsed = false;
396
+ const serializedString = JSON.stringify(data, function (key, value) {
397
+ const item = this[key];
398
+ if (item instanceof Date) {
399
+ customDateUsed = true;
400
+ return `${DATE_TOKEN}${item.toISOString()}`;
401
+ }
402
+ if (item instanceof Map) {
403
+ mapUsed = true;
404
+ return value;
405
+ }
406
+ if (item instanceof Set) {
407
+ setUsed = true;
408
+ return value;
409
+ }
410
+ if (typeof item === 'string' &&
411
+ staticBase !== null &&
412
+ item.startsWith(staticBase)) {
413
+ customFileUsed = true;
414
+ return `${FILE_TOKEN}${item.replace(staticBase + '/', '')}`;
415
+ }
416
+ return value;
417
+ }, indent);
418
+ return { serializedString, customDateUsed, customFileUsed, mapUsed, setUsed };
419
+ };
420
+ const deserializeJSONWithCustomFields = (data) => {
421
+ return JSON.parse(data, (_, value) => {
422
+ if (typeof value === 'string' && value.startsWith(DATE_TOKEN)) {
423
+ return new Date(value.replace(DATE_TOKEN, ''));
424
+ }
425
+ if (typeof value === 'string' && value.startsWith(FILE_TOKEN)) {
426
+ return staticFile(value.replace(FILE_TOKEN, ''));
427
+ }
428
+ return value;
429
+ });
430
+ };
431
+
295
432
  let didWarnSSRImport = false;
296
433
  const warnOnceSSRImport = () => {
297
434
  if (didWarnSSRImport) {
@@ -315,7 +452,7 @@ const getInputProps = () => {
315
452
  if (!param) {
316
453
  return {};
317
454
  }
318
- const parsed = JSON.parse(param);
455
+ const parsed = deserializeJSONWithCustomFields(param);
319
456
  return parsed;
320
457
  };
321
458
 
@@ -344,7 +481,7 @@ const EditorPropsProvider = ({ children }) => {
344
481
  return (jsx(EditorPropsContext.Provider, { value: ctx, children: children }));
345
482
  };
346
483
 
347
- const validateDimension = (amount, nameOfProp, location) => {
484
+ function validateDimension(amount, nameOfProp, location) {
348
485
  if (typeof amount !== 'number') {
349
486
  throw new Error(`The "${nameOfProp}" prop ${location} must be a number, but you passed a value of type ${typeof amount}`);
350
487
  }
@@ -360,9 +497,13 @@ const validateDimension = (amount, nameOfProp, location) => {
360
497
  if (amount <= 0) {
361
498
  throw new TypeError(`The "${nameOfProp}" prop ${location} must be positive, but got ${amount}.`);
362
499
  }
363
- };
500
+ }
364
501
 
365
- const validateDurationInFrames = ({ allowFloats, component, durationInFrames, }) => {
502
+ function validateDurationInFrames(durationInFrames, options) {
503
+ const { allowFloats, component } = options;
504
+ if (typeof durationInFrames === 'undefined') {
505
+ throw new Error(`The "durationInFrames" prop ${component} is missing.`);
506
+ }
366
507
  if (typeof durationInFrames !== 'number') {
367
508
  throw new Error(`The "durationInFrames" prop ${component} must be a number, but you passed a value of type ${typeof durationInFrames}`);
368
509
  }
@@ -375,21 +516,35 @@ const validateDurationInFrames = ({ allowFloats, component, durationInFrames, })
375
516
  if (!Number.isFinite(durationInFrames)) {
376
517
  throw new TypeError(`The "durationInFrames" prop ${component} must be finite, but got ${durationInFrames}.`);
377
518
  }
378
- };
519
+ }
379
520
 
380
- const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined, signal, }) => {
381
- var _a, _b, _c, _d, _e, _f;
521
+ function validateFps(fps, location, isGif) {
522
+ if (typeof fps !== 'number') {
523
+ throw new Error(`"fps" must be a number, but you passed a value of type ${typeof fps} ${location}`);
524
+ }
525
+ if (!Number.isFinite(fps)) {
526
+ throw new Error(`"fps" must be a finite, but you passed ${fps} ${location}`);
527
+ }
528
+ if (isNaN(fps)) {
529
+ throw new Error(`"fps" must not be NaN, but got ${fps} ${location}`);
530
+ }
531
+ if (fps <= 0) {
532
+ throw new TypeError(`"fps" must be positive, but got ${fps} ${location}`);
533
+ }
534
+ if (isGif && fps > 50) {
535
+ throw new TypeError(`The FPS for a GIF cannot be higher than 50. Use the --every-nth-frame option to lower the FPS: https://remotion.dev/docs/render-as-gif`);
536
+ }
537
+ }
538
+
539
+ const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined, signal, inputProps, }) => {
540
+ var _a, _b, _c, _d, _e, _f, _g;
382
541
  const calculatedProm = composition.calculateMetadata
383
542
  ? composition.calculateMetadata({
384
543
  defaultProps: (_a = composition.defaultProps) !== null && _a !== void 0 ? _a : {},
385
544
  props: {
386
545
  ...((_b = composition.defaultProps) !== null && _b !== void 0 ? _b : {}),
387
546
  ...(editorPropsOrUndefined !== null && editorPropsOrUndefined !== void 0 ? editorPropsOrUndefined : {}),
388
- ...(typeof window === 'undefined' ||
389
- getRemotionEnvironment() === 'player-development' ||
390
- getRemotionEnvironment() === 'player-production'
391
- ? {}
392
- : (_c = getInputProps()) !== null && _c !== void 0 ? _c : {}),
547
+ ...inputProps,
393
548
  },
394
549
  abortSignal: signal,
395
550
  })
@@ -398,7 +553,7 @@ const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined,
398
553
  typeof calculatedProm === 'object' &&
399
554
  'then' in calculatedProm) {
400
555
  return calculatedProm.then((c) => {
401
- var _a, _b;
556
+ var _a, _b, _c;
402
557
  const { height, width, durationInFrames, fps } = validateCalculated({
403
558
  calculated: c,
404
559
  composition,
@@ -409,7 +564,8 @@ const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined,
409
564
  fps,
410
565
  durationInFrames,
411
566
  id: composition.id,
412
- defaultProps: (_b = (_a = c.props) !== null && _a !== void 0 ? _a : composition.defaultProps) !== null && _b !== void 0 ? _b : {},
567
+ defaultProps: (_a = composition.defaultProps) !== null && _a !== void 0 ? _a : {},
568
+ props: (_c = (_b = c.props) !== null && _b !== void 0 ? _b : composition.defaultProps) !== null && _c !== void 0 ? _c : {},
413
569
  };
414
570
  });
415
571
  }
@@ -421,40 +577,34 @@ const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined,
421
577
  return {
422
578
  ...data,
423
579
  id: composition.id,
424
- defaultProps: (_d = composition === null || composition === void 0 ? void 0 : composition.defaultProps) !== null && _d !== void 0 ? _d : {},
580
+ defaultProps: (_c = composition.defaultProps) !== null && _c !== void 0 ? _c : {},
581
+ props: {
582
+ ...((_d = composition.defaultProps) !== null && _d !== void 0 ? _d : {}),
583
+ ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}),
584
+ },
425
585
  };
426
586
  }
427
587
  return {
428
588
  ...data,
429
589
  id: composition.id,
430
- defaultProps: (_f = (_e = calculatedProm === null || calculatedProm === void 0 ? void 0 : calculatedProm.props) !== null && _e !== void 0 ? _e : composition.defaultProps) !== null && _f !== void 0 ? _f : {},
590
+ defaultProps: (_e = composition.defaultProps) !== null && _e !== void 0 ? _e : {},
591
+ props: (_g = (_f = calculatedProm.props) !== null && _f !== void 0 ? _f : composition.defaultProps) !== null && _g !== void 0 ? _g : {},
431
592
  };
432
593
  };
433
594
  const validateCalculated = ({ composition, calculated, }) => {
434
595
  var _a, _b, _c, _d, _e, _f, _g, _h;
435
- const potentialErrorLocation = `calculated by calculateMetadata() for the composition "${composition.id}"`;
436
- const width = (_b = (_a = calculated === null || calculated === void 0 ? void 0 : calculated.width) !== null && _a !== void 0 ? _a : composition.width) !== null && _b !== void 0 ? _b : null;
437
- if (!width) {
438
- throw new TypeError('Composition width was neither specified via the `width` prop nor the `calculateMetadata()` function.');
439
- }
440
- validateDimension(width, 'width', potentialErrorLocation);
441
- const height = (_d = (_c = calculated === null || calculated === void 0 ? void 0 : calculated.height) !== null && _c !== void 0 ? _c : composition.height) !== null && _d !== void 0 ? _d : null;
442
- if (!height) {
443
- throw new TypeError('Composition height was neither specified via the `height` prop nor the `calculateMetadata()` function.');
444
- }
445
- validateDimension(width, 'height', potentialErrorLocation);
596
+ const calculateMetadataErrorLocation = `calculated by calculateMetadata() for the composition "${composition.id}"`;
597
+ const defaultErrorLocation = `of the "<Composition />" component with the id "${composition.id}"`;
598
+ const width = (_b = (_a = calculated === null || calculated === void 0 ? void 0 : calculated.width) !== null && _a !== void 0 ? _a : composition.width) !== null && _b !== void 0 ? _b : undefined;
599
+ validateDimension(width, 'width', (calculated === null || calculated === void 0 ? void 0 : calculated.width) ? calculateMetadataErrorLocation : defaultErrorLocation);
600
+ const height = (_d = (_c = calculated === null || calculated === void 0 ? void 0 : calculated.height) !== null && _c !== void 0 ? _c : composition.height) !== null && _d !== void 0 ? _d : undefined;
601
+ validateDimension(height, 'height', (calculated === null || calculated === void 0 ? void 0 : calculated.height) ? calculateMetadataErrorLocation : defaultErrorLocation);
446
602
  const fps = (_f = (_e = calculated === null || calculated === void 0 ? void 0 : calculated.fps) !== null && _e !== void 0 ? _e : composition.fps) !== null && _f !== void 0 ? _f : null;
447
- if (!fps) {
448
- throw new TypeError('Composition fps was neither specified via the `fps` prop nor the `calculateMetadata()` function.');
449
- }
603
+ validateFps(fps, (calculated === null || calculated === void 0 ? void 0 : calculated.fps) ? calculateMetadataErrorLocation : defaultErrorLocation, false);
450
604
  const durationInFrames = (_h = (_g = calculated === null || calculated === void 0 ? void 0 : calculated.durationInFrames) !== null && _g !== void 0 ? _g : composition.durationInFrames) !== null && _h !== void 0 ? _h : null;
451
- if (!durationInFrames) {
452
- throw new TypeError('Composition durationInFrames was neither specified via the `durationInFrames` prop nor the `calculateMetadata()` function.');
453
- }
454
- validateDurationInFrames({
455
- durationInFrames,
456
- component: potentialErrorLocation,
605
+ validateDurationInFrames(durationInFrames, {
457
606
  allowFloats: false,
607
+ component: `of the "<Composition />" component with the id "${composition.id}"`,
458
608
  });
459
609
  return { width, height, fps, durationInFrames };
460
610
  };
@@ -481,12 +631,23 @@ const ResolveCompositionConfig = ({ children }) => {
481
631
  : {};
482
632
  }, [allEditorProps, renderModalComposition]);
483
633
  const doResolution = useCallback((composition, editorProps) => {
634
+ var _a;
484
635
  const controller = new AbortController();
485
636
  if (currentCompositionMetadata) {
486
637
  return controller;
487
638
  }
639
+ const inputProps = typeof window === 'undefined' ||
640
+ getRemotionEnvironment() === 'player-development' ||
641
+ getRemotionEnvironment() === 'player-production'
642
+ ? {}
643
+ : (_a = getInputProps()) !== null && _a !== void 0 ? _a : {};
488
644
  const { signal } = controller;
489
- const promOrNot = resolveVideoConfig({ composition, editorProps, signal });
645
+ const promOrNot = resolveVideoConfig({
646
+ composition,
647
+ editorProps,
648
+ inputProps,
649
+ signal,
650
+ });
490
651
  if (typeof promOrNot === 'object' && 'then' in promOrNot) {
491
652
  setResolvedConfigs((r) => ({
492
653
  ...r,
@@ -601,7 +762,7 @@ const useResolvedVideoConfig = (preferredCompositionId) => {
601
762
  return composition ? (_a = allEditorProps[composition.id]) !== null && _a !== void 0 ? _a : {} : {};
602
763
  }, [allEditorProps, composition]);
603
764
  return useMemo(() => {
604
- var _a, _b, _c;
765
+ var _a, _b, _c, _d;
605
766
  if (!composition) {
606
767
  return null;
607
768
  }
@@ -611,7 +772,8 @@ const useResolvedVideoConfig = (preferredCompositionId) => {
611
772
  result: {
612
773
  ...currentCompositionMetadata,
613
774
  id: composition.id,
614
- defaultProps: (_a = currentCompositionMetadata.defaultProps) !== null && _a !== void 0 ? _a : {},
775
+ props: currentCompositionMetadata.props,
776
+ defaultProps: (_a = composition.defaultProps) !== null && _a !== void 0 ? _a : {},
615
777
  },
616
778
  };
617
779
  }
@@ -619,15 +781,20 @@ const useResolvedVideoConfig = (preferredCompositionId) => {
619
781
  return {
620
782
  type: 'success',
621
783
  result: {
622
- ...composition,
623
- defaultProps: {
624
- ...((_b = composition.defaultProps) !== null && _b !== void 0 ? _b : {}),
784
+ width: composition.width,
785
+ height: composition.height,
786
+ fps: composition.fps,
787
+ id: composition.id,
788
+ durationInFrames: composition.durationInFrames,
789
+ defaultProps: (_b = composition.defaultProps) !== null && _b !== void 0 ? _b : {},
790
+ props: {
791
+ ...((_c = composition.defaultProps) !== null && _c !== void 0 ? _c : {}),
625
792
  ...(selectedEditorProps !== null && selectedEditorProps !== void 0 ? selectedEditorProps : {}),
626
793
  ...(typeof window === 'undefined' ||
627
794
  getRemotionEnvironment() === 'player-development' ||
628
795
  getRemotionEnvironment() === 'player-production'
629
796
  ? {}
630
- : (_c = getInputProps()) !== null && _c !== void 0 ? _c : {}),
797
+ : (_d = getInputProps()) !== null && _d !== void 0 ? _d : {}),
631
798
  },
632
799
  },
633
800
  };
@@ -646,7 +813,7 @@ const useVideo = () => {
646
813
  });
647
814
  const resolved = useResolvedVideoConfig(context.currentComposition);
648
815
  return useMemo(() => {
649
- var _a;
816
+ var _a, _b;
650
817
  if (!resolved) {
651
818
  return null;
652
819
  }
@@ -661,18 +828,18 @@ const useVideo = () => {
661
828
  }
662
829
  return {
663
830
  ...resolved.result,
664
- defaultProps: selected.defaultProps,
831
+ defaultProps: (_a = selected.defaultProps) !== null && _a !== void 0 ? _a : {},
665
832
  id: selected.id,
666
833
  // We override the selected metadata with the metadata that was passed to renderMedia(),
667
834
  // and don't allow it to be changed during render anymore
668
- ...((_a = context.currentCompositionMetadata) !== null && _a !== void 0 ? _a : {}),
835
+ ...((_b = context.currentCompositionMetadata) !== null && _b !== void 0 ? _b : {}),
669
836
  component: selected.component,
670
837
  };
671
838
  }, [context.currentCompositionMetadata, resolved, selected]);
672
839
  };
673
840
 
674
841
  const TimelineContext = createContext({
675
- frame: 0,
842
+ frame: {},
676
843
  playing: false,
677
844
  playbackRate: 1,
678
845
  rootId: '',
@@ -692,16 +859,32 @@ const SetTimelineContext = createContext({
692
859
  throw new Error('default');
693
860
  },
694
861
  });
862
+ const makeKey = (composition) => {
863
+ return `remotion.time.${composition}`;
864
+ };
865
+ const persistCurrentFrame = (frame, composition) => {
866
+ localStorage.setItem(makeKey(composition), String(frame));
867
+ };
868
+ const getFrameForComposition = (composition) => {
869
+ var _a, _b;
870
+ const frame = localStorage.getItem(makeKey(composition));
871
+ return frame
872
+ ? Number(frame)
873
+ : (_b = (typeof window === 'undefined' ? 0 : (_a = window.remotion_initialFrame) !== null && _a !== void 0 ? _a : 0)) !== null && _b !== void 0 ? _b : 0;
874
+ };
695
875
  const useTimelinePosition = () => {
876
+ var _a, _b;
696
877
  const videoConfig = useVideo();
697
878
  const state = useContext(TimelineContext);
698
- // A dynamically calculated duration using calculateMetadata()
699
- // may lead to the frame being bigger than the duration.
700
- // If we have the config, we clamp the frame to the duration.
701
879
  if (!videoConfig) {
702
- return state.frame;
880
+ return typeof window === 'undefined'
881
+ ? 0
882
+ : (_a = window.remotion_initialFrame) !== null && _a !== void 0 ? _a : 0;
703
883
  }
704
- return Math.min(videoConfig.durationInFrames - 1, state.frame);
884
+ const unclamped = (_b = state.frame[videoConfig.id]) !== null && _b !== void 0 ? _b : (typeof window !== 'undefined' && window.remotion_isPlayer
885
+ ? 0
886
+ : getFrameForComposition(videoConfig.id));
887
+ return Math.min(videoConfig.durationInFrames - 1, unclamped);
705
888
  };
706
889
  const useTimelineSetFrame = () => {
707
890
  const { setFrame } = useContext(SetTimelineContext);
@@ -717,6 +900,8 @@ var TimelinePosition = /*#__PURE__*/Object.freeze({
717
900
  __proto__: null,
718
901
  TimelineContext: TimelineContext,
719
902
  SetTimelineContext: SetTimelineContext,
903
+ persistCurrentFrame: persistCurrentFrame,
904
+ getFrameForComposition: getFrameForComposition,
720
905
  useTimelinePosition: useTimelinePosition,
721
906
  useTimelineSetFrame: useTimelineSetFrame,
722
907
  usePlayingState: usePlayingState
@@ -736,7 +921,7 @@ const useUnsafeVideoConfig = () => {
736
921
  if (!video) {
737
922
  return null;
738
923
  }
739
- const { id, durationInFrames, fps, height, width, defaultProps } = video;
924
+ const { id, durationInFrames, fps, height, width, defaultProps, props } = video;
740
925
  return {
741
926
  id,
742
927
  width,
@@ -744,6 +929,7 @@ const useUnsafeVideoConfig = () => {
744
929
  fps,
745
930
  durationInFrames: ctxDuration !== null && ctxDuration !== void 0 ? ctxDuration : durationInFrames,
746
931
  defaultProps,
932
+ props,
747
933
  };
748
934
  }, [ctxDuration, video]);
749
935
  };
@@ -917,8 +1103,7 @@ const useCurrentFrame = () => {
917
1103
  const Loop = ({ durationInFrames, times = Infinity, children, name, ...props }) => {
918
1104
  const currentFrame = useCurrentFrame();
919
1105
  const { durationInFrames: compDuration } = useVideoConfig();
920
- validateDurationInFrames({
921
- durationInFrames,
1106
+ validateDurationInFrames(durationInFrames, {
922
1107
  component: 'of the <Loop /> component',
923
1108
  allowFloats: true,
924
1109
  });
@@ -1277,13 +1462,13 @@ const evaluateVolume = ({ frame, volume, mediaVolume = 1, allowAmplificationDuri
1277
1462
  return Math.max(0, Math.min(maxVolume, evaluated));
1278
1463
  };
1279
1464
 
1280
- const didWarn$1 = {};
1281
- const warnOnce$1 = (message) => {
1282
- if (didWarn$1[message]) {
1465
+ const didWarn = {};
1466
+ const warnOnce = (message) => {
1467
+ if (didWarn[message]) {
1283
1468
  return;
1284
1469
  }
1285
1470
  console.warn(message);
1286
- didWarn$1[message] = true;
1471
+ didWarn[message] = true;
1287
1472
  };
1288
1473
  const useMediaInTimeline = ({ volume, mediaVolume, mediaRef, src, mediaType, playbackRate, }) => {
1289
1474
  const videoConfig = useVideoConfig();
@@ -1321,7 +1506,7 @@ const useMediaInTimeline = ({ volume, mediaVolume, mediaRef, src, mediaType, pla
1321
1506
  }, [duration, startsAt, volume, mediaVolume]);
1322
1507
  useEffect(() => {
1323
1508
  if (typeof volume === 'number' && volume !== initialVolume) {
1324
- warnOnce$1(`Remotion: The ${mediaType} with src ${src} has changed it's volume. Prefer the callback syntax for setting volume to get better timeline display: https://www.remotion.dev/docs/using-audio/#controlling-volume`);
1509
+ warnOnce(`Remotion: The ${mediaType} with src ${src} has changed it's volume. Prefer the callback syntax for setting volume to get better timeline display: https://www.remotion.dev/docs/using-audio/#controlling-volume`);
1325
1510
  }
1326
1511
  }, [initialVolume, mediaType, src, volume]);
1327
1512
  useEffect(() => {
@@ -1880,7 +2065,11 @@ const SharedAudioContextProvider = ({ children, numberOfAudioTags, component })
1880
2065
  };
1881
2066
  }, [component, resetAudio]);
1882
2067
  return (jsxs(SharedAudioContext.Provider, { value: value, children: [refs.map(({ id, ref }) => {
1883
- return jsx("audio", { ref: ref, src: EMPTY_AUDIO }, id);
2068
+ return (
2069
+ // Without preload="metadata", iOS will seek the time internally
2070
+ // but not actually with sound. Adding `preload="metadata"` helps here.
2071
+ // https://discord.com/channels/809501355504959528/817306414069710848/1130519583367888906
2072
+ jsx("audio", { ref: ref, preload: "metadata", src: EMPTY_AUDIO }, id));
1884
2073
  }), children] }));
1885
2074
  };
1886
2075
  const useSharedAudio = (aud, audioId) => {
@@ -2008,7 +2197,7 @@ const AudioForDevelopmentForwardRefFunction = (props, ref) => {
2008
2197
  if (initialShouldPreMountAudioElements) {
2009
2198
  return null;
2010
2199
  }
2011
- return jsx("audio", { ref: audioRef, ...propsToPass });
2200
+ return jsx("audio", { ref: audioRef, preload: "metadata", ...propsToPass });
2012
2201
  };
2013
2202
  const AudioForDevelopment = forwardRef(AudioForDevelopmentForwardRefFunction);
2014
2203
 
@@ -2051,7 +2240,9 @@ if (typeof window !== 'undefined') {
2051
2240
  window.remotion_renderReady = false;
2052
2241
  }
2053
2242
  let handles = [];
2054
- const timeouts = {};
2243
+ if (typeof window !== 'undefined') {
2244
+ window.remotion_delayRenderTimeouts = {};
2245
+ }
2055
2246
  const DELAY_RENDER_CALLSTACK_TOKEN = 'The delayRender was called:';
2056
2247
  const defaultTimeout = 30000;
2057
2248
  /**
@@ -2073,18 +2264,23 @@ const delayRender = (label) => {
2073
2264
  const timeoutToUse = typeof window === 'undefined'
2074
2265
  ? defaultTimeout
2075
2266
  : ((_c = window.remotion_puppeteerTimeout) !== null && _c !== void 0 ? _c : defaultTimeout) - 2000;
2076
- timeouts[handle] = setTimeout(() => {
2077
- const message = [
2078
- `A delayRender()`,
2079
- label ? `"${label}"` : null,
2080
- `was called but not cleared after ${timeoutToUse}ms. See https://remotion.dev/docs/timeout for help.`,
2081
- DELAY_RENDER_CALLSTACK_TOKEN,
2082
- called,
2083
- ]
2084
- .filter(truthy)
2085
- .join(' ');
2086
- throw new Error(message);
2087
- }, timeoutToUse);
2267
+ if (typeof window !== 'undefined') {
2268
+ window.remotion_delayRenderTimeouts[handle] = {
2269
+ label: label !== null && label !== void 0 ? label : null,
2270
+ timeout: setTimeout(() => {
2271
+ const message = [
2272
+ `A delayRender()`,
2273
+ label ? `"${label}"` : null,
2274
+ `was called but not cleared after ${timeoutToUse}ms. See https://remotion.dev/docs/timeout for help.`,
2275
+ DELAY_RENDER_CALLSTACK_TOKEN,
2276
+ called,
2277
+ ]
2278
+ .filter(truthy)
2279
+ .join(' ');
2280
+ throw new Error(message);
2281
+ }, timeoutToUse),
2282
+ };
2283
+ }
2088
2284
  }
2089
2285
  if (typeof window !== 'undefined') {
2090
2286
  window.remotion_renderReady = false;
@@ -2107,7 +2303,8 @@ const continueRender = (handle) => {
2107
2303
  handles = handles.filter((h) => {
2108
2304
  if (h === handle) {
2109
2305
  if (getRemotionEnvironment() === 'rendering') {
2110
- clearTimeout(timeouts[handle]);
2306
+ clearTimeout(window.remotion_delayRenderTimeouts[handle].timeout);
2307
+ delete window.remotion_delayRenderTimeouts[handle];
2111
2308
  }
2112
2309
  return false;
2113
2310
  }
@@ -2393,24 +2590,6 @@ const validateDefaultAndInputProps = (defaultProps, name, compositionId) => {
2393
2590
  }
2394
2591
  };
2395
2592
 
2396
- const validateFps = (fps, location, isGif) => {
2397
- if (typeof fps !== 'number') {
2398
- throw new Error(`"fps" must be a number, but you passed a value of type ${typeof fps} ${location}`);
2399
- }
2400
- if (!Number.isFinite(fps)) {
2401
- throw new Error(`"fps" must be a finite, but you passed ${fps} ${location}`);
2402
- }
2403
- if (isNaN(fps)) {
2404
- throw new Error(`"fps" must not be NaN, but got ${fps} ${location}`);
2405
- }
2406
- if (fps <= 0) {
2407
- throw new TypeError(`"fps" must be positive, but got ${fps} ${location}`);
2408
- }
2409
- if (isGif && fps > 50) {
2410
- throw new TypeError(`The FPS for a GIF cannot be higher than 50. Use the --every-nth-frame option to lower the FPS: https://remotion.dev/docs/render-as-gif`);
2411
- }
2412
- };
2413
-
2414
2593
  const Fallback = () => {
2415
2594
  useEffect(() => {
2416
2595
  const fallback = delayRender('Waiting for Root component to unsuspend');
@@ -2445,20 +2624,12 @@ const Composition = ({ width, height, fps, durationInFrames, id, defaultProps, s
2445
2624
  throw new Error('No id for composition passed.');
2446
2625
  }
2447
2626
  validateCompositionId(id);
2448
- validateDimension(width, 'width', 'of the <Composition/> component');
2449
- validateDimension(height, 'height', 'of the <Composition/> component');
2450
- validateDurationInFrames({
2451
- durationInFrames,
2452
- component: 'of the <Composition/> component',
2453
- allowFloats: false,
2454
- });
2455
- validateFps(fps, 'as a prop of the <Composition/> component', false);
2456
2627
  validateDefaultAndInputProps(defaultProps, 'defaultProps', id);
2457
2628
  registerComposition({
2458
- durationInFrames,
2459
- fps,
2460
- height,
2461
- width,
2629
+ durationInFrames: durationInFrames !== null && durationInFrames !== void 0 ? durationInFrames : undefined,
2630
+ fps: fps !== null && fps !== void 0 ? fps : undefined,
2631
+ height: height !== null && height !== void 0 ? height : undefined,
2632
+ width: width !== null && width !== void 0 ? width : undefined,
2462
2633
  id,
2463
2634
  folderName,
2464
2635
  component: lazy,
@@ -2493,14 +2664,14 @@ const Composition = ({ width, height, fps, durationInFrames, id, defaultProps, s
2493
2664
  if (resolved === null || resolved.type !== 'success') {
2494
2665
  return null;
2495
2666
  }
2496
- return createPortal(jsx(ClipComposition, { children: jsx(CanUseRemotionHooksProvider, { children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Comp, { ...((_a = resolved.result.defaultProps) !== null && _a !== void 0 ? _a : {}) }) }) }) }), portalNode());
2667
+ return createPortal(jsx(ClipComposition, { children: jsx(CanUseRemotionHooksProvider, { children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsx(Comp, { ...((_a = resolved.result.props) !== null && _a !== void 0 ? _a : {}) }) }) }) }), portalNode());
2497
2668
  }
2498
2669
  if (environment === 'rendering' && video && video.component === lazy) {
2499
2670
  const Comp = lazy;
2500
2671
  if (resolved === null || resolved.type !== 'success') {
2501
2672
  return null;
2502
2673
  }
2503
- return createPortal(jsx(CanUseRemotionHooksProvider, { children: jsx(Suspense, { fallback: jsx(Fallback, {}), children: jsx(Comp, { ...((_b = resolved.result.defaultProps) !== null && _b !== void 0 ? _b : {}) }) }) }), portalNode());
2674
+ return createPortal(jsx(CanUseRemotionHooksProvider, { children: jsx(Suspense, { fallback: jsx(Fallback, {}), children: jsx(Comp, { ...((_b = resolved.result.props) !== null && _b !== void 0 ? _b : {}) }) }) }), portalNode());
2504
2675
  }
2505
2676
  return null;
2506
2677
  };
@@ -2712,6 +2883,7 @@ class Easing {
2712
2883
  * @see [Documentation](https://www.remotion.dev/docs/freeze)
2713
2884
  */
2714
2885
  const Freeze = ({ frame, children }) => {
2886
+ const videoConfig = useVideoConfig();
2715
2887
  if (typeof frame === 'undefined') {
2716
2888
  throw new Error(`The <Freeze /> component requires a 'frame' prop, but none was passed.`);
2717
2889
  }
@@ -2732,9 +2904,11 @@ const Freeze = ({ frame, children }) => {
2732
2904
  imperativePlaying: {
2733
2905
  current: false,
2734
2906
  },
2735
- frame,
2907
+ frame: {
2908
+ [videoConfig.id]: frame,
2909
+ },
2736
2910
  };
2737
- }, [context, frame]);
2911
+ }, [context, frame, videoConfig.id]);
2738
2912
  return (jsx(TimelineContext.Provider, { value: value, children: jsx(SequenceContext.Provider, { value: null, children: children }) }));
2739
2913
  };
2740
2914
 
@@ -2852,7 +3026,7 @@ const ImgRefForwarding = ({ onError, maxRetries = 2, src, ...props }, ref) => {
2852
3026
  if (process.env.NODE_ENV === 'test') {
2853
3027
  return;
2854
3028
  }
2855
- const newHandle = delayRender('Loading <Img> with src=' + src);
3029
+ const newHandle = delayRender('Loading <Img> with src=' + actualSrc);
2856
3030
  const { current } = imageRef;
2857
3031
  const onComplete = () => {
2858
3032
  var _a, _b, _c, _d;
@@ -2876,7 +3050,7 @@ const ImgRefForwarding = ({ onError, maxRetries = 2, src, ...props }, ref) => {
2876
3050
  current === null || current === void 0 ? void 0 : current.removeEventListener('load', didLoad);
2877
3051
  continueRender(newHandle);
2878
3052
  };
2879
- }, [src]);
3053
+ }, [actualSrc]);
2880
3054
  }
2881
3055
  return (jsx("img", { ...props, ref: imageRef, src: actualSrc, onError: didGetError }));
2882
3056
  };
@@ -3455,9 +3629,8 @@ const waitForRoot = (fn) => {
3455
3629
  };
3456
3630
 
3457
3631
  const RemotionRoot = ({ children, numberOfAudioTags }) => {
3458
- var _a;
3459
3632
  const [remotionRootId] = useState(() => String(random(null)));
3460
- const [frame, setFrame] = useState((_a = window.remotion_initialFrame) !== null && _a !== void 0 ? _a : 0);
3633
+ const [frame, setFrame] = useState({});
3461
3634
  const [playing, setPlaying] = useState(false);
3462
3635
  const imperativePlaying = useRef(false);
3463
3636
  const [fastRefreshes, setFastRefreshes] = useState(0);
@@ -3466,9 +3639,12 @@ const RemotionRoot = ({ children, numberOfAudioTags }) => {
3466
3639
  if (typeof window !== 'undefined') {
3467
3640
  // eslint-disable-next-line react-hooks/rules-of-hooks
3468
3641
  useLayoutEffect(() => {
3469
- window.remotion_setFrame = (f) => {
3642
+ window.remotion_setFrame = (f, composition) => {
3470
3643
  const id = delayRender(`Setting the current frame to ${f}`);
3471
- setFrame(f);
3644
+ setFrame((s) => ({
3645
+ ...s,
3646
+ [composition]: f,
3647
+ }));
3472
3648
  requestAnimationFrame(() => continueRender(id));
3473
3649
  };
3474
3650
  window.remotion_isPlayer = false;
@@ -3662,6 +3838,15 @@ const Internals = {
3662
3838
  REMOTION_STUDIO_CONTAINER_ELEMENT,
3663
3839
  AssetManager,
3664
3840
  bundleName: 'bundle.js',
3841
+ bundleMapName: 'bundle.js.map',
3842
+ persistCurrentFrame,
3843
+ useTimelineSetFrame,
3844
+ serializeJSONWithDate,
3845
+ deserializeJSONWithCustomFields,
3846
+ FILE_TOKEN,
3847
+ DATE_TOKEN,
3848
+ NativeLayersProvider,
3849
+ ClipComposition,
3665
3850
  };
3666
3851
 
3667
3852
  const flattenChildren = (children) => {
@@ -3708,11 +3893,10 @@ const Series = ({ children }) => {
3708
3893
  throw new TypeError(`A <Series.Sequence /> component (${debugInfo}) was detected to not have any children. Delete it to fix this error.`);
3709
3894
  }
3710
3895
  const durationInFramesProp = castedChild.props.durationInFrames;
3711
- const { durationInFrames, children: _children, ...passedProps } = castedChild.props;
3896
+ const { durationInFrames, children: _children, from, ...passedProps } = castedChild.props; // `from` is not accepted and must be filtered out if used in JS
3712
3897
  if (i !== flattenedChildren.length - 1 ||
3713
3898
  durationInFramesProp !== Infinity) {
3714
- validateDurationInFrames({
3715
- durationInFrames: durationInFramesProp,
3899
+ validateDurationInFrames(durationInFramesProp, {
3716
3900
  component: `of a <Series.Sequence /> component`,
3717
3901
  allowFloats: true,
3718
3902
  });
@@ -3964,7 +4148,7 @@ function spring({ frame: passedFrame, fps, config = {}, from = 0, to = 1, durati
3964
4148
  };
3965
4149
  const frame = (reverse
3966
4150
  ? (passedDurationInFrames !== null && passedDurationInFrames !== void 0 ? passedDurationInFrames : naturalDurationGetter.get()) - passedFrame
3967
- : passedFrame) - delay;
4151
+ : passedFrame) - (reverse ? -delay : delay);
3968
4152
  const spr = springCalculation({
3969
4153
  fps,
3970
4154
  frame: passedDurationInFrames === undefined
@@ -3983,89 +4167,6 @@ function spring({ frame: passedFrame, fps, config = {}, from = 0, to = 1, durati
3983
4167
  return Math.max(spr.current, to);
3984
4168
  }
3985
4169
 
3986
- const problematicCharacters = {
3987
- '%3A': ':',
3988
- '%2F': '/',
3989
- '%3F': '?',
3990
- '%23': '#',
3991
- '%5B': '[',
3992
- '%5D': ']',
3993
- '%40': '@',
3994
- '%21': '!',
3995
- '%24': '$',
3996
- '%26': '&',
3997
- '%27': "'",
3998
- '%28': '(',
3999
- '%29': ')',
4000
- '%2A': '*',
4001
- '%2B': '+',
4002
- '%2C': ',',
4003
- '%3B': ';',
4004
- };
4005
- const didWarn = {};
4006
- const warnOnce = (message) => {
4007
- if (didWarn[message]) {
4008
- return;
4009
- }
4010
- console.warn(message);
4011
- didWarn[message] = true;
4012
- };
4013
- const includesHexOfUnsafeChar = (path) => {
4014
- for (const key of Object.keys(problematicCharacters)) {
4015
- if (path.includes(key)) {
4016
- return { containsHex: true, hexCode: key };
4017
- }
4018
- }
4019
- return { containsHex: false };
4020
- };
4021
- const trimLeadingSlash = (path) => {
4022
- if (path.startsWith('/')) {
4023
- return trimLeadingSlash(path.substring(1));
4024
- }
4025
- return path;
4026
- };
4027
- const inner = (path) => {
4028
- if (typeof window !== 'undefined' && window.remotion_staticBase) {
4029
- return `${window.remotion_staticBase}/${trimLeadingSlash(path)}`;
4030
- }
4031
- return `/${trimLeadingSlash(path)}`;
4032
- };
4033
- /**
4034
- * @description Reference a file from the public/ folder. If the file does not appear in the autocomplete, type the path manually.
4035
- * @see [Documentation](https://www.remotion.dev/docs/staticfile)
4036
- */
4037
- const staticFile = (path) => {
4038
- if (path.startsWith('http://') || path.startsWith('https://')) {
4039
- throw new TypeError(`staticFile() does not support remote URLs - got "${path}". Instead, pass the URL without wrapping it in staticFile(). See: https://remotion.dev/docs/staticfile-remote-urls`);
4040
- }
4041
- if (path.startsWith('..') || path.startsWith('./')) {
4042
- throw new TypeError(`staticFile() does not support relative paths - got "${path}". Instead, pass the name of a file that is inside the public/ folder. See: https://remotion.dev/docs/staticfile-relative-paths`);
4043
- }
4044
- if (path.startsWith('/Users') ||
4045
- path.startsWith('/home') ||
4046
- path.startsWith('/tmp') ||
4047
- path.startsWith('/etc') ||
4048
- path.startsWith('/opt') ||
4049
- path.startsWith('/var') ||
4050
- path.startsWith('C:') ||
4051
- path.startsWith('D:') ||
4052
- path.startsWith('E:')) {
4053
- throw new TypeError(`staticFile() does not support absolute paths - got "${path}". Instead, pass the name of a file that is inside the public/ folder. See: https://remotion.dev/docs/staticfile-relative-paths`);
4054
- }
4055
- if (path.startsWith('public/')) {
4056
- throw new TypeError(`Do not include the public/ prefix when using staticFile() - got "${path}". See: https://remotion.dev/docs/staticfile-relative-paths`);
4057
- }
4058
- const includesHex = includesHexOfUnsafeChar(path);
4059
- if (includesHex.containsHex) {
4060
- warnOnce(`WARNING: You seem to pass an already encoded path (path contains ${includesHex.hexCode}). The encoding gets automatically handled by staticFile() `);
4061
- }
4062
- const preparsed = inner(encodeURIComponent(path));
4063
- if (!preparsed.startsWith('/')) {
4064
- return `/${preparsed}`;
4065
- }
4066
- return preparsed;
4067
- };
4068
-
4069
4170
  /**
4070
4171
  * @description A `<Still />` is a `<Composition />` that is only 1 frame long.
4071
4172
  * @see [Documentation](https://www.remotion.dev/docs/still)
@@ -4155,8 +4256,13 @@ const OffthreadVideoForRendering = ({ onError, volume: volumeProp, playbackRate,
4155
4256
  return `http://localhost:${window.remotion_proxyPort}/proxy?src=${encodeURIComponent(getAbsoluteSrc(src))}&time=${encodeURIComponent(currentTime)}&transparent=${String(transparent)}`;
4156
4257
  }, [currentTime, src, transparent]);
4157
4258
  const onErr = useCallback((e) => {
4158
- onError === null || onError === void 0 ? void 0 : onError(e);
4159
- }, [onError]);
4259
+ if (onError) {
4260
+ onError === null || onError === void 0 ? void 0 : onError(e);
4261
+ }
4262
+ else {
4263
+ cancelRender('Failed to load image with src ' + actualSrc);
4264
+ }
4265
+ }, [actualSrc, onError]);
4160
4266
  const className = useMemo(() => {
4161
4267
  return [OFFTHREAD_VIDEO_CLASS_NAME, props.className]
4162
4268
  .filter(truthy)
@@ -4328,7 +4434,9 @@ const VideoForDevelopment = forwardRef(VideoForDevelopmentRefForwardingFunction)
4328
4434
  * @see [Documentation](https://www.remotion.dev/docs/offthreadvideo)
4329
4435
  */
4330
4436
  const OffthreadVideo = (props) => {
4331
- const { startFrom, endAt, imageFormat, ...otherProps } = props;
4437
+ // Should only destruct `startFrom` and `endAt` from props,
4438
+ // rest gets drilled down
4439
+ const { startFrom, endAt, ...otherProps } = props;
4332
4440
  const environment = useRemotionEnvironment();
4333
4441
  const onDuration = useCallback(() => undefined, []);
4334
4442
  if (typeof props.src !== 'string') {
@@ -4345,9 +4453,10 @@ const OffthreadVideo = (props) => {
4345
4453
  }
4346
4454
  validateMediaProps(props, 'Video');
4347
4455
  if (environment === 'rendering') {
4348
- return (jsx(OffthreadVideoForRendering, { imageFormat: imageFormat, ...otherProps }));
4456
+ return jsx(OffthreadVideoForRendering, { ...otherProps });
4349
4457
  }
4350
- return (jsx(VideoForDevelopment, { onDuration: onDuration, onlyWarnForMediaSeekingError: true, ...otherProps }));
4458
+ const { transparent, ...withoutTransparent } = otherProps;
4459
+ return (jsx(VideoForDevelopment, { onDuration: onDuration, onlyWarnForMediaSeekingError: true, ...withoutTransparent }));
4351
4460
  };
4352
4461
 
4353
4462
  const VideoForRenderingForwardFunction = ({ onError, volume: volumeProp, allowAmplificationDuringRender, playbackRate, onDuration, ...props }, ref) => {
@@ -4614,7 +4723,7 @@ const Config = new Proxy(proxyObj, {
4614
4723
  console.warn('+ Replace:');
4615
4724
  console.warn('import {Config} from "@remotion/cli/config";');
4616
4725
  console.warn();
4617
- console.warn('For more information, see https://v4.remotion.dev/docs/4-0-migration.');
4726
+ console.warn('For more information, see https://www.remotion.dev/docs/4-0-migration.');
4618
4727
  process.exit(1);
4619
4728
  };
4620
4729
  },