remotion 4.1.0-alpha5 → 4.1.0-alpha8

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 (40) hide show
  1. package/dist/cjs/Composition.d.ts +21 -6
  2. package/dist/cjs/Composition.js +4 -15
  3. package/dist/cjs/CompositionManager.d.ts +4 -4
  4. package/dist/cjs/Img.d.ts +1 -1
  5. package/dist/cjs/RemotionRoot.js +6 -4
  6. package/dist/cjs/ResolveCompositionConfig.js +17 -2
  7. package/dist/cjs/audio/Audio.d.ts +2 -2
  8. package/dist/cjs/audio/AudioForDevelopment.d.ts +1 -1
  9. package/dist/cjs/audio/AudioForRendering.d.ts +1 -1
  10. package/dist/cjs/config/input-props.d.ts +1 -1
  11. package/dist/cjs/config/input-props.js +2 -1
  12. package/dist/cjs/freeze.js +6 -2
  13. package/dist/cjs/index.d.ts +7 -4
  14. package/dist/cjs/index.js +1 -1
  15. package/dist/cjs/input-props-serialization.d.ts +14 -0
  16. package/dist/cjs/input-props-serialization.js +49 -0
  17. package/dist/cjs/internals.d.ts +67 -58
  18. package/dist/cjs/internals.js +7 -0
  19. package/dist/cjs/loop/index.js +1 -2
  20. package/dist/cjs/resolve-video-config.d.ts +2 -1
  21. package/dist/cjs/resolve-video-config.js +20 -33
  22. package/dist/cjs/series/index.js +1 -2
  23. package/dist/cjs/spring/index.js +1 -1
  24. package/dist/cjs/static-file.js +11 -2
  25. package/dist/cjs/timeline-position-state.d.ts +5 -3
  26. package/dist/cjs/timeline-position-state.js +25 -7
  27. package/dist/cjs/validation/validate-dimensions.d.ts +1 -1
  28. package/dist/cjs/validation/validate-dimensions.js +2 -2
  29. package/dist/cjs/validation/validate-duration-in-frames.d.ts +2 -3
  30. package/dist/cjs/validation/validate-duration-in-frames.js +6 -2
  31. package/dist/cjs/validation/validate-fps.d.ts +1 -1
  32. package/dist/cjs/validation/validate-fps.js +2 -2
  33. package/dist/cjs/version.d.ts +1 -1
  34. package/dist/cjs/version.js +1 -1
  35. package/dist/cjs/video/Video.d.ts +1 -1
  36. package/dist/cjs/video/VideoForDevelopment.d.ts +1 -1
  37. package/dist/cjs/video/VideoForRendering.d.ts +1 -1
  38. package/dist/esm/index.mjs +253 -175
  39. package/dist/esm/version.mjs +1 -1
  40. package/package.json +1 -1
@@ -58,7 +58,7 @@ function truthy(value) {
58
58
  }
59
59
 
60
60
  // Automatically generated on publish
61
- const VERSION = '4.1.0-alpha5';
61
+ const VERSION = '4.1.0-alpha8';
62
62
 
63
63
  const checkMultipleRemotionVersions = () => {
64
64
  if (typeof globalThis === 'undefined') {
@@ -292,6 +292,142 @@ const CompositionManager = createContext({
292
292
  currentCompositionMetadata: null,
293
293
  });
294
294
 
295
+ const problematicCharacters = {
296
+ '%3A': ':',
297
+ '%2F': '/',
298
+ '%3F': '?',
299
+ '%23': '#',
300
+ '%5B': '[',
301
+ '%5D': ']',
302
+ '%40': '@',
303
+ '%21': '!',
304
+ '%24': '$',
305
+ '%26': '&',
306
+ '%27': "'",
307
+ '%28': '(',
308
+ '%29': ')',
309
+ '%2A': '*',
310
+ '%2B': '+',
311
+ '%2C': ',',
312
+ '%3B': ';',
313
+ };
314
+ const didWarn$1 = {};
315
+ const warnOnce$1 = (message) => {
316
+ if (didWarn$1[message]) {
317
+ return;
318
+ }
319
+ console.warn(message);
320
+ didWarn$1[message] = true;
321
+ };
322
+ const includesHexOfUnsafeChar = (path) => {
323
+ for (const key of Object.keys(problematicCharacters)) {
324
+ if (path.includes(key)) {
325
+ return { containsHex: true, hexCode: key };
326
+ }
327
+ }
328
+ return { containsHex: false };
329
+ };
330
+ const trimLeadingSlash = (path) => {
331
+ if (path.startsWith('/')) {
332
+ return trimLeadingSlash(path.substring(1));
333
+ }
334
+ return path;
335
+ };
336
+ const inner = (path) => {
337
+ if (typeof window !== 'undefined' && window.remotion_staticBase) {
338
+ return `${window.remotion_staticBase}/${trimLeadingSlash(path)}`;
339
+ }
340
+ return `/${trimLeadingSlash(path)}`;
341
+ };
342
+ const encodeBySplitting = (path) => {
343
+ const splitBySlash = path.split('/');
344
+ const encodedArray = splitBySlash.map((element) => {
345
+ return encodeURIComponent(element);
346
+ });
347
+ const merged = encodedArray.join('/');
348
+ return merged;
349
+ };
350
+ /**
351
+ * @description Reference a file from the public/ folder. If the file does not appear in the autocomplete, type the path manually.
352
+ * @see [Documentation](https://www.remotion.dev/docs/staticfile)
353
+ */
354
+ const staticFile = (path) => {
355
+ if (path.startsWith('http://') || path.startsWith('https://')) {
356
+ 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`);
357
+ }
358
+ if (path.startsWith('..') || path.startsWith('./')) {
359
+ 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`);
360
+ }
361
+ if (path.startsWith('/Users') ||
362
+ path.startsWith('/home') ||
363
+ path.startsWith('/tmp') ||
364
+ path.startsWith('/etc') ||
365
+ path.startsWith('/opt') ||
366
+ path.startsWith('/var') ||
367
+ path.startsWith('C:') ||
368
+ path.startsWith('D:') ||
369
+ path.startsWith('E:')) {
370
+ 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`);
371
+ }
372
+ if (path.startsWith('public/')) {
373
+ throw new TypeError(`Do not include the public/ prefix when using staticFile() - got "${path}". See: https://remotion.dev/docs/staticfile-relative-paths`);
374
+ }
375
+ const includesHex = includesHexOfUnsafeChar(path);
376
+ if (includesHex.containsHex) {
377
+ 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.`);
378
+ }
379
+ const preprocessed = encodeBySplitting(path);
380
+ const preparsed = inner(preprocessed);
381
+ if (!preparsed.startsWith('/')) {
382
+ return `/${preparsed}`;
383
+ }
384
+ return preparsed;
385
+ };
386
+
387
+ // Must keep this file in sync with the one in packages/lambda/src/shared/serialize-props.ts!
388
+ const DATE_TOKEN = 'remotion-date:';
389
+ const FILE_TOKEN = 'remotion-file:';
390
+ const serializeJSONWithDate = ({ data, indent, staticBase, }) => {
391
+ let customDateUsed = false;
392
+ let customFileUsed = false;
393
+ let mapUsed = false;
394
+ let setUsed = false;
395
+ const serializedString = JSON.stringify(data, function (key, value) {
396
+ const item = this[key];
397
+ if (item instanceof Date) {
398
+ customDateUsed = true;
399
+ return `${DATE_TOKEN}${item.toISOString()}`;
400
+ }
401
+ if (item instanceof Map) {
402
+ mapUsed = true;
403
+ return value;
404
+ }
405
+ if (item instanceof Set) {
406
+ setUsed = true;
407
+ return value;
408
+ }
409
+ if (typeof item === 'string' &&
410
+ staticBase !== null &&
411
+ item.startsWith(staticBase)) {
412
+ customFileUsed = true;
413
+ return `${FILE_TOKEN}${item.replace(staticBase + '/', '')}`;
414
+ }
415
+ return value;
416
+ }, indent);
417
+ return { serializedString, customDateUsed, customFileUsed, mapUsed, setUsed };
418
+ };
419
+ const deserializeJSONWithCustomFields = (data) => {
420
+ return JSON.parse(data, (_, value) => {
421
+ if (typeof value === 'string' && value.startsWith(DATE_TOKEN)) {
422
+ return new Date(value.replace(DATE_TOKEN, ''));
423
+ }
424
+ if (typeof value === 'string' && value.startsWith(FILE_TOKEN)) {
425
+ return staticFile(value.replace(FILE_TOKEN, ''));
426
+ }
427
+ return value;
428
+ });
429
+ };
430
+
295
431
  let didWarnSSRImport = false;
296
432
  const warnOnceSSRImport = () => {
297
433
  if (didWarnSSRImport) {
@@ -315,7 +451,7 @@ const getInputProps = () => {
315
451
  if (!param) {
316
452
  return {};
317
453
  }
318
- const parsed = JSON.parse(param);
454
+ const parsed = deserializeJSONWithCustomFields(param);
319
455
  return parsed;
320
456
  };
321
457
 
@@ -344,7 +480,7 @@ const EditorPropsProvider = ({ children }) => {
344
480
  return (jsx(EditorPropsContext.Provider, { value: ctx, children: children }));
345
481
  };
346
482
 
347
- const validateDimension = (amount, nameOfProp, location) => {
483
+ function validateDimension(amount, nameOfProp, location) {
348
484
  if (typeof amount !== 'number') {
349
485
  throw new Error(`The "${nameOfProp}" prop ${location} must be a number, but you passed a value of type ${typeof amount}`);
350
486
  }
@@ -360,9 +496,13 @@ const validateDimension = (amount, nameOfProp, location) => {
360
496
  if (amount <= 0) {
361
497
  throw new TypeError(`The "${nameOfProp}" prop ${location} must be positive, but got ${amount}.`);
362
498
  }
363
- };
499
+ }
364
500
 
365
- const validateDurationInFrames = ({ allowFloats, component, durationInFrames, }) => {
501
+ function validateDurationInFrames(durationInFrames, options) {
502
+ const { allowFloats, component } = options;
503
+ if (typeof durationInFrames === 'undefined') {
504
+ throw new Error(`The "durationInFrames" prop ${component} is missing.`);
505
+ }
366
506
  if (typeof durationInFrames !== 'number') {
367
507
  throw new Error(`The "durationInFrames" prop ${component} must be a number, but you passed a value of type ${typeof durationInFrames}`);
368
508
  }
@@ -375,21 +515,35 @@ const validateDurationInFrames = ({ allowFloats, component, durationInFrames, })
375
515
  if (!Number.isFinite(durationInFrames)) {
376
516
  throw new TypeError(`The "durationInFrames" prop ${component} must be finite, but got ${durationInFrames}.`);
377
517
  }
378
- };
518
+ }
379
519
 
380
- const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined, signal, }) => {
381
- var _a, _b, _c, _d, _e, _f, _g, _h;
520
+ function validateFps(fps, location, isGif) {
521
+ if (typeof fps !== 'number') {
522
+ throw new Error(`"fps" must be a number, but you passed a value of type ${typeof fps} ${location}`);
523
+ }
524
+ if (!Number.isFinite(fps)) {
525
+ throw new Error(`"fps" must be a finite, but you passed ${fps} ${location}`);
526
+ }
527
+ if (isNaN(fps)) {
528
+ throw new Error(`"fps" must not be NaN, but got ${fps} ${location}`);
529
+ }
530
+ if (fps <= 0) {
531
+ throw new TypeError(`"fps" must be positive, but got ${fps} ${location}`);
532
+ }
533
+ if (isGif && fps > 50) {
534
+ 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`);
535
+ }
536
+ }
537
+
538
+ const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined, signal, inputProps, }) => {
539
+ var _a, _b, _c, _d, _e, _f, _g;
382
540
  const calculatedProm = composition.calculateMetadata
383
541
  ? composition.calculateMetadata({
384
542
  defaultProps: (_a = composition.defaultProps) !== null && _a !== void 0 ? _a : {},
385
543
  props: {
386
544
  ...((_b = composition.defaultProps) !== null && _b !== void 0 ? _b : {}),
387
545
  ...(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 : {}),
546
+ ...inputProps,
393
547
  },
394
548
  abortSignal: signal,
395
549
  })
@@ -422,42 +576,34 @@ const resolveVideoConfig = ({ composition, editorProps: editorPropsOrUndefined,
422
576
  return {
423
577
  ...data,
424
578
  id: composition.id,
425
- defaultProps: (_d = composition.defaultProps) !== null && _d !== void 0 ? _d : {},
426
- props: (_e = composition.defaultProps) !== null && _e !== void 0 ? _e : {},
579
+ defaultProps: (_c = composition.defaultProps) !== null && _c !== void 0 ? _c : {},
580
+ props: {
581
+ ...((_d = composition.defaultProps) !== null && _d !== void 0 ? _d : {}),
582
+ ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}),
583
+ },
427
584
  };
428
585
  }
429
586
  return {
430
587
  ...data,
431
588
  id: composition.id,
432
- defaultProps: (_f = composition.defaultProps) !== null && _f !== void 0 ? _f : {},
433
- props: (_h = (_g = calculatedProm.props) !== null && _g !== void 0 ? _g : composition.defaultProps) !== null && _h !== void 0 ? _h : {},
589
+ defaultProps: (_e = composition.defaultProps) !== null && _e !== void 0 ? _e : {},
590
+ props: (_g = (_f = calculatedProm.props) !== null && _f !== void 0 ? _f : composition.defaultProps) !== null && _g !== void 0 ? _g : {},
434
591
  };
435
592
  };
436
593
  const validateCalculated = ({ composition, calculated, }) => {
437
594
  var _a, _b, _c, _d, _e, _f, _g, _h;
438
- const potentialErrorLocation = `calculated by calculateMetadata() for the composition "${composition.id}"`;
439
- 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;
440
- if (!width) {
441
- throw new TypeError('Composition width was neither specified via the `width` prop nor the `calculateMetadata()` function.');
442
- }
443
- validateDimension(width, 'width', potentialErrorLocation);
444
- 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;
445
- if (!height) {
446
- throw new TypeError('Composition height was neither specified via the `height` prop nor the `calculateMetadata()` function.');
447
- }
448
- validateDimension(width, 'height', potentialErrorLocation);
595
+ const calculateMetadataErrorLocation = `calculated by calculateMetadata() for the composition "${composition.id}"`;
596
+ const defaultErrorLocation = `of the "<Composition />" component with the id "${composition.id}"`;
597
+ 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;
598
+ validateDimension(width, 'width', (calculated === null || calculated === void 0 ? void 0 : calculated.width) ? calculateMetadataErrorLocation : defaultErrorLocation);
599
+ 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;
600
+ validateDimension(height, 'height', (calculated === null || calculated === void 0 ? void 0 : calculated.height) ? calculateMetadataErrorLocation : defaultErrorLocation);
449
601
  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;
450
- if (!fps) {
451
- throw new TypeError('Composition fps was neither specified via the `fps` prop nor the `calculateMetadata()` function.');
452
- }
602
+ validateFps(fps, (calculated === null || calculated === void 0 ? void 0 : calculated.fps) ? calculateMetadataErrorLocation : defaultErrorLocation, false);
453
603
  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;
454
- if (!durationInFrames) {
455
- throw new TypeError('Composition durationInFrames was neither specified via the `durationInFrames` prop nor the `calculateMetadata()` function.');
456
- }
457
- validateDurationInFrames({
458
- durationInFrames,
459
- component: potentialErrorLocation,
604
+ validateDurationInFrames(durationInFrames, {
460
605
  allowFloats: false,
606
+ component: `of the "<Composition />" component with the id "${composition.id}"`,
461
607
  });
462
608
  return { width, height, fps, durationInFrames };
463
609
  };
@@ -484,12 +630,23 @@ const ResolveCompositionConfig = ({ children }) => {
484
630
  : {};
485
631
  }, [allEditorProps, renderModalComposition]);
486
632
  const doResolution = useCallback((composition, editorProps) => {
633
+ var _a;
487
634
  const controller = new AbortController();
488
635
  if (currentCompositionMetadata) {
489
636
  return controller;
490
637
  }
638
+ const inputProps = typeof window === 'undefined' ||
639
+ getRemotionEnvironment() === 'player-development' ||
640
+ getRemotionEnvironment() === 'player-production'
641
+ ? {}
642
+ : (_a = getInputProps()) !== null && _a !== void 0 ? _a : {};
491
643
  const { signal } = controller;
492
- const promOrNot = resolveVideoConfig({ composition, editorProps, signal });
644
+ const promOrNot = resolveVideoConfig({
645
+ composition,
646
+ editorProps,
647
+ inputProps,
648
+ signal,
649
+ });
493
650
  if (typeof promOrNot === 'object' && 'then' in promOrNot) {
494
651
  setResolvedConfigs((r) => ({
495
652
  ...r,
@@ -623,7 +780,11 @@ const useResolvedVideoConfig = (preferredCompositionId) => {
623
780
  return {
624
781
  type: 'success',
625
782
  result: {
626
- ...composition,
783
+ width: composition.width,
784
+ height: composition.height,
785
+ fps: composition.fps,
786
+ id: composition.id,
787
+ durationInFrames: composition.durationInFrames,
627
788
  defaultProps: (_b = composition.defaultProps) !== null && _b !== void 0 ? _b : {},
628
789
  props: {
629
790
  ...((_c = composition.defaultProps) !== null && _c !== void 0 ? _c : {}),
@@ -677,7 +838,7 @@ const useVideo = () => {
677
838
  };
678
839
 
679
840
  const TimelineContext = createContext({
680
- frame: 0,
841
+ frame: {},
681
842
  playing: false,
682
843
  playbackRate: 1,
683
844
  rootId: '',
@@ -697,16 +858,32 @@ const SetTimelineContext = createContext({
697
858
  throw new Error('default');
698
859
  },
699
860
  });
861
+ const makeKey = (composition) => {
862
+ return `remotion.time.${composition}`;
863
+ };
864
+ const persistCurrentFrame = (frame, composition) => {
865
+ localStorage.setItem(makeKey(composition), String(frame));
866
+ };
867
+ const getFrameForComposition = (composition) => {
868
+ var _a, _b;
869
+ const frame = localStorage.getItem(makeKey(composition));
870
+ return frame
871
+ ? Number(frame)
872
+ : (_b = (typeof window === 'undefined' ? 0 : (_a = window.remotion_initialFrame) !== null && _a !== void 0 ? _a : 0)) !== null && _b !== void 0 ? _b : 0;
873
+ };
700
874
  const useTimelinePosition = () => {
875
+ var _a, _b;
701
876
  const videoConfig = useVideo();
702
877
  const state = useContext(TimelineContext);
703
- // A dynamically calculated duration using calculateMetadata()
704
- // may lead to the frame being bigger than the duration.
705
- // If we have the config, we clamp the frame to the duration.
706
878
  if (!videoConfig) {
707
- return state.frame;
879
+ return typeof window === 'undefined'
880
+ ? 0
881
+ : (_a = window.remotion_initialFrame) !== null && _a !== void 0 ? _a : 0;
708
882
  }
709
- return Math.min(videoConfig.durationInFrames - 1, state.frame);
883
+ const unclamped = (_b = state.frame[videoConfig.id]) !== null && _b !== void 0 ? _b : (typeof window !== 'undefined' && window.remotion_isPlayer
884
+ ? 0
885
+ : getFrameForComposition(videoConfig.id));
886
+ return Math.min(videoConfig.durationInFrames - 1, unclamped);
710
887
  };
711
888
  const useTimelineSetFrame = () => {
712
889
  const { setFrame } = useContext(SetTimelineContext);
@@ -722,6 +899,8 @@ var TimelinePosition = /*#__PURE__*/Object.freeze({
722
899
  __proto__: null,
723
900
  TimelineContext: TimelineContext,
724
901
  SetTimelineContext: SetTimelineContext,
902
+ persistCurrentFrame: persistCurrentFrame,
903
+ getFrameForComposition: getFrameForComposition,
725
904
  useTimelinePosition: useTimelinePosition,
726
905
  useTimelineSetFrame: useTimelineSetFrame,
727
906
  usePlayingState: usePlayingState
@@ -923,8 +1102,7 @@ const useCurrentFrame = () => {
923
1102
  const Loop = ({ durationInFrames, times = Infinity, children, name, ...props }) => {
924
1103
  const currentFrame = useCurrentFrame();
925
1104
  const { durationInFrames: compDuration } = useVideoConfig();
926
- validateDurationInFrames({
927
- durationInFrames,
1105
+ validateDurationInFrames(durationInFrames, {
928
1106
  component: 'of the <Loop /> component',
929
1107
  allowFloats: true,
930
1108
  });
@@ -1283,13 +1461,13 @@ const evaluateVolume = ({ frame, volume, mediaVolume = 1, allowAmplificationDuri
1283
1461
  return Math.max(0, Math.min(maxVolume, evaluated));
1284
1462
  };
1285
1463
 
1286
- const didWarn$1 = {};
1287
- const warnOnce$1 = (message) => {
1288
- if (didWarn$1[message]) {
1464
+ const didWarn = {};
1465
+ const warnOnce = (message) => {
1466
+ if (didWarn[message]) {
1289
1467
  return;
1290
1468
  }
1291
1469
  console.warn(message);
1292
- didWarn$1[message] = true;
1470
+ didWarn[message] = true;
1293
1471
  };
1294
1472
  const useMediaInTimeline = ({ volume, mediaVolume, mediaRef, src, mediaType, playbackRate, }) => {
1295
1473
  const videoConfig = useVideoConfig();
@@ -1327,7 +1505,7 @@ const useMediaInTimeline = ({ volume, mediaVolume, mediaRef, src, mediaType, pla
1327
1505
  }, [duration, startsAt, volume, mediaVolume]);
1328
1506
  useEffect(() => {
1329
1507
  if (typeof volume === 'number' && volume !== initialVolume) {
1330
- 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`);
1508
+ 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`);
1331
1509
  }
1332
1510
  }, [initialVolume, mediaType, src, volume]);
1333
1511
  useEffect(() => {
@@ -2399,24 +2577,6 @@ const validateDefaultAndInputProps = (defaultProps, name, compositionId) => {
2399
2577
  }
2400
2578
  };
2401
2579
 
2402
- const validateFps = (fps, location, isGif) => {
2403
- if (typeof fps !== 'number') {
2404
- throw new Error(`"fps" must be a number, but you passed a value of type ${typeof fps} ${location}`);
2405
- }
2406
- if (!Number.isFinite(fps)) {
2407
- throw new Error(`"fps" must be a finite, but you passed ${fps} ${location}`);
2408
- }
2409
- if (isNaN(fps)) {
2410
- throw new Error(`"fps" must not be NaN, but got ${fps} ${location}`);
2411
- }
2412
- if (fps <= 0) {
2413
- throw new TypeError(`"fps" must be positive, but got ${fps} ${location}`);
2414
- }
2415
- if (isGif && fps > 50) {
2416
- 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`);
2417
- }
2418
- };
2419
-
2420
2580
  const Fallback = () => {
2421
2581
  useEffect(() => {
2422
2582
  const fallback = delayRender('Waiting for Root component to unsuspend');
@@ -2451,20 +2611,12 @@ const Composition = ({ width, height, fps, durationInFrames, id, defaultProps, s
2451
2611
  throw new Error('No id for composition passed.');
2452
2612
  }
2453
2613
  validateCompositionId(id);
2454
- validateDimension(width, 'width', 'of the <Composition/> component');
2455
- validateDimension(height, 'height', 'of the <Composition/> component');
2456
- validateDurationInFrames({
2457
- durationInFrames,
2458
- component: 'of the <Composition/> component',
2459
- allowFloats: false,
2460
- });
2461
- validateFps(fps, 'as a prop of the <Composition/> component', false);
2462
2614
  validateDefaultAndInputProps(defaultProps, 'defaultProps', id);
2463
2615
  registerComposition({
2464
- durationInFrames,
2465
- fps,
2466
- height,
2467
- width,
2616
+ durationInFrames: durationInFrames !== null && durationInFrames !== void 0 ? durationInFrames : undefined,
2617
+ fps: fps !== null && fps !== void 0 ? fps : undefined,
2618
+ height: height !== null && height !== void 0 ? height : undefined,
2619
+ width: width !== null && width !== void 0 ? width : undefined,
2468
2620
  id,
2469
2621
  folderName,
2470
2622
  component: lazy,
@@ -2718,6 +2870,7 @@ class Easing {
2718
2870
  * @see [Documentation](https://www.remotion.dev/docs/freeze)
2719
2871
  */
2720
2872
  const Freeze = ({ frame, children }) => {
2873
+ const videoConfig = useVideoConfig();
2721
2874
  if (typeof frame === 'undefined') {
2722
2875
  throw new Error(`The <Freeze /> component requires a 'frame' prop, but none was passed.`);
2723
2876
  }
@@ -2738,9 +2891,11 @@ const Freeze = ({ frame, children }) => {
2738
2891
  imperativePlaying: {
2739
2892
  current: false,
2740
2893
  },
2741
- frame,
2894
+ frame: {
2895
+ [videoConfig.id]: frame,
2896
+ },
2742
2897
  };
2743
- }, [context, frame]);
2898
+ }, [context, frame, videoConfig.id]);
2744
2899
  return (jsx(TimelineContext.Provider, { value: value, children: jsx(SequenceContext.Provider, { value: null, children: children }) }));
2745
2900
  };
2746
2901
 
@@ -3461,9 +3616,8 @@ const waitForRoot = (fn) => {
3461
3616
  };
3462
3617
 
3463
3618
  const RemotionRoot = ({ children, numberOfAudioTags }) => {
3464
- var _a;
3465
3619
  const [remotionRootId] = useState(() => String(random(null)));
3466
- const [frame, setFrame] = useState((_a = window.remotion_initialFrame) !== null && _a !== void 0 ? _a : 0);
3620
+ const [frame, setFrame] = useState({});
3467
3621
  const [playing, setPlaying] = useState(false);
3468
3622
  const imperativePlaying = useRef(false);
3469
3623
  const [fastRefreshes, setFastRefreshes] = useState(0);
@@ -3472,9 +3626,12 @@ const RemotionRoot = ({ children, numberOfAudioTags }) => {
3472
3626
  if (typeof window !== 'undefined') {
3473
3627
  // eslint-disable-next-line react-hooks/rules-of-hooks
3474
3628
  useLayoutEffect(() => {
3475
- window.remotion_setFrame = (f) => {
3629
+ window.remotion_setFrame = (f, composition) => {
3476
3630
  const id = delayRender(`Setting the current frame to ${f}`);
3477
- setFrame(f);
3631
+ setFrame((s) => ({
3632
+ ...s,
3633
+ [composition]: f,
3634
+ }));
3478
3635
  requestAnimationFrame(() => continueRender(id));
3479
3636
  };
3480
3637
  window.remotion_isPlayer = false;
@@ -3668,6 +3825,11 @@ const Internals = {
3668
3825
  REMOTION_STUDIO_CONTAINER_ELEMENT,
3669
3826
  AssetManager,
3670
3827
  bundleName: 'bundle.js',
3828
+ persistCurrentFrame,
3829
+ useTimelineSetFrame,
3830
+ serializeJSONWithDate,
3831
+ deserializeJSONWithCustomFields,
3832
+ FILE_TOKEN,
3671
3833
  };
3672
3834
 
3673
3835
  const flattenChildren = (children) => {
@@ -3717,8 +3879,7 @@ const Series = ({ children }) => {
3717
3879
  const { durationInFrames, children: _children, from, ...passedProps } = castedChild.props; // `from` is not accepted and must be filtered out if used in JS
3718
3880
  if (i !== flattenedChildren.length - 1 ||
3719
3881
  durationInFramesProp !== Infinity) {
3720
- validateDurationInFrames({
3721
- durationInFrames: durationInFramesProp,
3882
+ validateDurationInFrames(durationInFramesProp, {
3722
3883
  component: `of a <Series.Sequence /> component`,
3723
3884
  allowFloats: true,
3724
3885
  });
@@ -3970,7 +4131,7 @@ function spring({ frame: passedFrame, fps, config = {}, from = 0, to = 1, durati
3970
4131
  };
3971
4132
  const frame = (reverse
3972
4133
  ? (passedDurationInFrames !== null && passedDurationInFrames !== void 0 ? passedDurationInFrames : naturalDurationGetter.get()) - passedFrame
3973
- : passedFrame) - delay;
4134
+ : passedFrame) - (reverse ? -delay : delay);
3974
4135
  const spr = springCalculation({
3975
4136
  fps,
3976
4137
  frame: passedDurationInFrames === undefined
@@ -3989,89 +4150,6 @@ function spring({ frame: passedFrame, fps, config = {}, from = 0, to = 1, durati
3989
4150
  return Math.max(spr.current, to);
3990
4151
  }
3991
4152
 
3992
- const problematicCharacters = {
3993
- '%3A': ':',
3994
- '%2F': '/',
3995
- '%3F': '?',
3996
- '%23': '#',
3997
- '%5B': '[',
3998
- '%5D': ']',
3999
- '%40': '@',
4000
- '%21': '!',
4001
- '%24': '$',
4002
- '%26': '&',
4003
- '%27': "'",
4004
- '%28': '(',
4005
- '%29': ')',
4006
- '%2A': '*',
4007
- '%2B': '+',
4008
- '%2C': ',',
4009
- '%3B': ';',
4010
- };
4011
- const didWarn = {};
4012
- const warnOnce = (message) => {
4013
- if (didWarn[message]) {
4014
- return;
4015
- }
4016
- console.warn(message);
4017
- didWarn[message] = true;
4018
- };
4019
- const includesHexOfUnsafeChar = (path) => {
4020
- for (const key of Object.keys(problematicCharacters)) {
4021
- if (path.includes(key)) {
4022
- return { containsHex: true, hexCode: key };
4023
- }
4024
- }
4025
- return { containsHex: false };
4026
- };
4027
- const trimLeadingSlash = (path) => {
4028
- if (path.startsWith('/')) {
4029
- return trimLeadingSlash(path.substring(1));
4030
- }
4031
- return path;
4032
- };
4033
- const inner = (path) => {
4034
- if (typeof window !== 'undefined' && window.remotion_staticBase) {
4035
- return `${window.remotion_staticBase}/${trimLeadingSlash(path)}`;
4036
- }
4037
- return `/${trimLeadingSlash(path)}`;
4038
- };
4039
- /**
4040
- * @description Reference a file from the public/ folder. If the file does not appear in the autocomplete, type the path manually.
4041
- * @see [Documentation](https://www.remotion.dev/docs/staticfile)
4042
- */
4043
- const staticFile = (path) => {
4044
- if (path.startsWith('http://') || path.startsWith('https://')) {
4045
- 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`);
4046
- }
4047
- if (path.startsWith('..') || path.startsWith('./')) {
4048
- 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`);
4049
- }
4050
- if (path.startsWith('/Users') ||
4051
- path.startsWith('/home') ||
4052
- path.startsWith('/tmp') ||
4053
- path.startsWith('/etc') ||
4054
- path.startsWith('/opt') ||
4055
- path.startsWith('/var') ||
4056
- path.startsWith('C:') ||
4057
- path.startsWith('D:') ||
4058
- path.startsWith('E:')) {
4059
- 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`);
4060
- }
4061
- if (path.startsWith('public/')) {
4062
- throw new TypeError(`Do not include the public/ prefix when using staticFile() - got "${path}". See: https://remotion.dev/docs/staticfile-relative-paths`);
4063
- }
4064
- const includesHex = includesHexOfUnsafeChar(path);
4065
- if (includesHex.containsHex) {
4066
- warnOnce(`WARNING: You seem to pass an already encoded path (path contains ${includesHex.hexCode}). The encoding gets automatically handled by staticFile() `);
4067
- }
4068
- const preparsed = inner(encodeURIComponent(path));
4069
- if (!preparsed.startsWith('/')) {
4070
- return `/${preparsed}`;
4071
- }
4072
- return preparsed;
4073
- };
4074
-
4075
4153
  /**
4076
4154
  * @description A `<Still />` is a `<Composition />` that is only 1 frame long.
4077
4155
  * @see [Documentation](https://www.remotion.dev/docs/still)
@@ -4622,7 +4700,7 @@ const Config = new Proxy(proxyObj, {
4622
4700
  console.warn('+ Replace:');
4623
4701
  console.warn('import {Config} from "@remotion/cli/config";');
4624
4702
  console.warn();
4625
- console.warn('For more information, see https://v4.remotion.dev/docs/4-0-migration.');
4703
+ console.warn('For more information, see https://www.remotion.dev/docs/4-0-migration.');
4626
4704
  process.exit(1);
4627
4705
  };
4628
4706
  },
@@ -1,4 +1,4 @@
1
1
  // Automatically generated on publish
2
- const VERSION = '4.1.0-alpha5';
2
+ const VERSION = '4.1.0-alpha8';
3
3
 
4
4
  export { VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remotion",
3
- "version": "4.1.0-alpha5",
3
+ "version": "4.1.0-alpha8",
4
4
  "description": "Render videos in React",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/cjs/index.d.ts",