@remotion/studio 4.0.372 → 4.0.374

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.
@@ -17634,7 +17634,7 @@ var loadPlaybackRate = () => {
17634
17634
 
17635
17635
  // src/components/PlaybackRatePersistor.tsx
17636
17636
  var PlaybackRatePersistor = () => {
17637
- const { setPlaybackRate, playbackRate } = useContext49(Internals40.Timeline.TimelineContext);
17637
+ const { setPlaybackRate, playbackRate } = useContext49(Internals40.TimelineContext);
17638
17638
  useEffect54(() => {
17639
17639
  setPlaybackRate(loadPlaybackRate());
17640
17640
  }, [setPlaybackRate]);
@@ -17943,7 +17943,35 @@ var TimelineZoomControls = () => {
17943
17943
  import ReactDOM8 from "react-dom/client";
17944
17944
  import { Internals as Internals44 } from "remotion";
17945
17945
  import { jsx as jsx176 } from "react/jsx-runtime";
17946
- var compose = ({
17946
+ var svgToImageBitmap = (svg) => {
17947
+ const computedStyle = getComputedStyle(svg);
17948
+ const { transform: originalTransform } = computedStyle;
17949
+ svg.style.transform = "none";
17950
+ const svgDimensions = svg.getBoundingClientRect();
17951
+ svg.style.transform = originalTransform;
17952
+ if (svgDimensions.width === 0 || svgDimensions.height === 0) {
17953
+ return Promise.resolve(null);
17954
+ }
17955
+ const svgData = new XMLSerializer().serializeToString(svg);
17956
+ return new Promise((resolve, reject) => {
17957
+ const image = new Image(svgDimensions.width, svgDimensions.height);
17958
+ const url = "data:image/svg+xml;base64," + window.btoa(svgData);
17959
+ image.onload = function() {
17960
+ resolve({
17961
+ image,
17962
+ width: svgDimensions.width,
17963
+ height: svgDimensions.height,
17964
+ left: svgDimensions.left,
17965
+ top: svgDimensions.top
17966
+ });
17967
+ };
17968
+ image.onerror = () => {
17969
+ reject(new Error("Failed to convert SVG to image"));
17970
+ };
17971
+ image.src = url;
17972
+ });
17973
+ };
17974
+ var compose = async ({
17947
17975
  composables,
17948
17976
  width,
17949
17977
  height
@@ -17954,7 +17982,15 @@ var compose = ({
17954
17982
  throw new Error("Could not get context");
17955
17983
  }
17956
17984
  for (const composable of composables) {
17957
- context.drawImage(composable, 0, 0);
17985
+ if (composable.type === "canvas") {
17986
+ const boundingClientRect = composable.element.getBoundingClientRect();
17987
+ context.drawImage(composable.element, boundingClientRect.left, boundingClientRect.top);
17988
+ } else if (composable.type === "svg") {
17989
+ const imageBitmap = await svgToImageBitmap(composable.element);
17990
+ if (imageBitmap) {
17991
+ context.drawImage(imageBitmap.image, imageBitmap.left, imageBitmap.top);
17992
+ }
17993
+ }
17958
17994
  }
17959
17995
  return canvas;
17960
17996
  };
@@ -17963,86 +17999,75 @@ var findCanvasElements = (element) => {
17963
17999
  const composables = [];
17964
18000
  Array.from(canvasElements).forEach((canvasElement) => {
17965
18001
  const canvas = canvasElement;
17966
- composables.push(canvas);
18002
+ composables.push({
18003
+ type: "canvas",
18004
+ element: canvas
18005
+ });
18006
+ });
18007
+ return composables;
18008
+ };
18009
+ var findSvgElements = (element) => {
18010
+ const svgElements = element.querySelectorAll("svg");
18011
+ const composables = [];
18012
+ Array.from(svgElements).forEach((svgElement) => {
18013
+ const svg = svgElement;
18014
+ composables.push({
18015
+ type: "svg",
18016
+ element: svg
18017
+ });
17967
18018
  });
17968
18019
  return composables;
17969
18020
  };
17970
- var waitForReady = (timeoutInMilliseconds) => {
18021
+ var waitForReady = (timeoutInMilliseconds, scope) => {
17971
18022
  const { promise, resolve, reject } = Promise.withResolvers();
17972
18023
  const start = Date.now();
17973
18024
  const interval = setInterval(() => {
17974
- if (window.remotion_renderReady === true) {
18025
+ if (scope.remotion_renderReady === true) {
17975
18026
  resolve(true);
17976
18027
  clearInterval(interval);
17977
18028
  return;
17978
18029
  }
17979
- if (window.remotion_cancelledError !== undefined) {
17980
- reject(window.remotion_cancelledError);
18030
+ if (scope.remotion_cancelledError !== undefined) {
18031
+ reject(scope.remotion_cancelledError);
17981
18032
  clearInterval(interval);
17982
18033
  return;
17983
18034
  }
17984
18035
  if (Date.now() - start > timeoutInMilliseconds + 3000) {
17985
- reject(new Error(Object.values(window.remotion_delayRenderTimeouts).map((d) => d.label).join(", ")));
18036
+ reject(new Error(Object.values(scope.remotion_delayRenderTimeouts).map((d) => d.label).join(", ")));
17986
18037
  clearInterval(interval);
17987
18038
  }
17988
18039
  }, 50);
17989
18040
  return promise;
17990
18041
  };
17991
- var renderStillOnWeb = async ({
18042
+ var COMP_ID = "markup";
18043
+ var internalRenderStillOnWeb = async ({
17992
18044
  Component,
17993
18045
  width,
17994
18046
  height,
17995
18047
  fps,
17996
18048
  durationInFrames,
17997
- frame: frame2
18049
+ frame: frame2,
18050
+ delayRenderTimeoutInMilliseconds,
18051
+ logLevel
17998
18052
  }) => {
17999
18053
  const div = document.createElement("div");
18000
18054
  div.style.display = "flex";
18001
18055
  div.style.backgroundColor = "transparent";
18002
- div.style.position = "absolute";
18056
+ div.style.position = "fixed";
18003
18057
  div.style.width = `${width}px`;
18004
18058
  div.style.height = `${height}px`;
18059
+ div.style.zIndex = "-9999";
18005
18060
  document.body.appendChild(div);
18006
- const delayRenderTimeoutInMilliseconds = 1e4;
18007
18061
  if (!ReactDOM8.createRoot) {
18008
18062
  throw new Error("@remotion/web-renderer requires React 18 or higher");
18009
18063
  }
18010
- const compositionManagerContext = {
18011
- currentCompositionMetadata: {
18012
- durationInFrames,
18013
- fps,
18014
- height,
18015
- width,
18016
- props: {},
18017
- defaultCodec: null,
18018
- defaultOutName: null,
18019
- defaultVideoImageFormat: null,
18020
- defaultPixelFormat: null,
18021
- defaultProResProfile: null
18022
- },
18023
- folders: [],
18024
- compositions: [
18025
- {
18026
- id: "markup",
18027
- component: Component,
18028
- nonce: 0,
18029
- defaultProps: undefined,
18030
- folderName: null,
18031
- parentFolderName: null,
18032
- schema: null,
18033
- calculateMetadata: null,
18034
- durationInFrames,
18035
- fps,
18036
- height,
18037
- width
18038
- }
18039
- ],
18040
- canvasContent: {
18041
- type: "composition",
18042
- compositionId: "markup"
18043
- }
18044
- };
18045
18064
  const root = ReactDOM8.createRoot(div);
18065
+ const delayRenderScope = {
18066
+ remotion_renderReady: true,
18067
+ remotion_delayRenderTimeouts: {},
18068
+ remotion_puppeteerTimeout: delayRenderTimeoutInMilliseconds,
18069
+ remotion_attempt: 0
18070
+ };
18046
18071
  root.render(/* @__PURE__ */ jsx176(Internals44.RemotionEnvironmentContext, {
18047
18072
  value: {
18048
18073
  isStudio: false,
@@ -18051,54 +18076,80 @@ var renderStillOnWeb = async ({
18051
18076
  isReadOnlyStudio: false,
18052
18077
  isClientSideRendering: true
18053
18078
  },
18054
- children: /* @__PURE__ */ jsx176(Internals44.RemotionRoot, {
18055
- audioEnabled: true,
18056
- videoEnabled: true,
18057
- logLevel: "info",
18058
- numberOfAudioTags: 0,
18059
- onlyRenderComposition: null,
18060
- currentCompositionMetadata: {
18061
- props: {},
18062
- durationInFrames,
18063
- fps,
18064
- height,
18065
- width,
18066
- defaultCodec: null,
18067
- defaultOutName: null,
18068
- defaultVideoImageFormat: null,
18069
- defaultPixelFormat: null,
18070
- defaultProResProfile: null
18071
- },
18072
- audioLatencyHint: "interactive",
18073
- children: /* @__PURE__ */ jsx176(Internals44.CanUseRemotionHooks, {
18074
- value: true,
18075
- children: /* @__PURE__ */ jsx176(Internals44.CompositionManager.Provider, {
18076
- value: compositionManagerContext,
18077
- children: /* @__PURE__ */ jsx176(Component, {})
18079
+ children: /* @__PURE__ */ jsx176(Internals44.DelayRenderContextType.Provider, {
18080
+ value: delayRenderScope,
18081
+ children: /* @__PURE__ */ jsx176(Internals44.CompositionManagerProvider, {
18082
+ initialCanvasContent: {
18083
+ type: "composition",
18084
+ compositionId: COMP_ID
18085
+ },
18086
+ onlyRenderComposition: null,
18087
+ currentCompositionMetadata: {
18088
+ props: {},
18089
+ durationInFrames,
18090
+ fps,
18091
+ height,
18092
+ width,
18093
+ defaultCodec: null,
18094
+ defaultOutName: null,
18095
+ defaultVideoImageFormat: null,
18096
+ defaultPixelFormat: null,
18097
+ defaultProResProfile: null
18098
+ },
18099
+ initialCompositions: [
18100
+ {
18101
+ id: COMP_ID,
18102
+ component: Component,
18103
+ nonce: 0,
18104
+ defaultProps: undefined,
18105
+ folderName: null,
18106
+ parentFolderName: null,
18107
+ schema: null,
18108
+ calculateMetadata: null,
18109
+ durationInFrames,
18110
+ fps,
18111
+ height,
18112
+ width
18113
+ }
18114
+ ],
18115
+ children: /* @__PURE__ */ jsx176(Internals44.RemotionRoot, {
18116
+ audioEnabled: false,
18117
+ videoEnabled: true,
18118
+ logLevel,
18119
+ numberOfAudioTags: 0,
18120
+ audioLatencyHint: "interactive",
18121
+ frameState: {
18122
+ [COMP_ID]: frame2
18123
+ },
18124
+ children: /* @__PURE__ */ jsx176(Internals44.CanUseRemotionHooks, {
18125
+ value: true,
18126
+ children: /* @__PURE__ */ jsx176(Component, {})
18127
+ })
18078
18128
  })
18079
18129
  })
18080
18130
  })
18081
18131
  }));
18082
- window.remotion_setFrame(frame2, "markup", frame2);
18083
- await waitForReady(delayRenderTimeoutInMilliseconds);
18132
+ await waitForReady(delayRenderTimeoutInMilliseconds, delayRenderScope);
18084
18133
  const canvasElements = findCanvasElements(div);
18085
- const composed = compose({
18086
- composables: canvasElements,
18134
+ const svgElements = findSvgElements(div);
18135
+ const composed = await compose({
18136
+ composables: [...canvasElements, ...svgElements],
18087
18137
  width,
18088
18138
  height
18089
18139
  });
18090
18140
  const imageData = await composed.convertToBlob({
18091
18141
  type: "image/png"
18092
18142
  });
18093
- const blob = new Blob([imageData], { type: "image/png" });
18094
- const url = URL.createObjectURL(blob);
18095
- const a = document.createElement("a");
18096
- a.href = url;
18097
- a.download = "composed.png";
18098
- a.click();
18099
- URL.revokeObjectURL(url);
18100
18143
  root.unmount();
18101
18144
  div.remove();
18145
+ return imageData;
18146
+ };
18147
+ var renderStillOnWeb = (options) => {
18148
+ return internalRenderStillOnWeb({
18149
+ ...options,
18150
+ delayRenderTimeoutInMilliseconds: options.delayRenderTimeoutInMilliseconds ?? 30000,
18151
+ logLevel: options.logLevel ?? "info"
18152
+ });
18102
18153
  };
18103
18154
 
18104
18155
  // src/components/WebRender/TriggerWebRender.tsx
@@ -18125,6 +18176,13 @@ var TriggerWebRender = () => {
18125
18176
  fps: video.fps,
18126
18177
  durationInFrames: video.durationInFrames,
18127
18178
  frame: frame2
18179
+ }).then((blob) => {
18180
+ const url = URL.createObjectURL(blob);
18181
+ const a = document.createElement("a");
18182
+ a.href = url;
18183
+ a.download = "composed.png";
18184
+ a.click();
18185
+ URL.revokeObjectURL(url);
18128
18186
  });
18129
18187
  }, [video, frame2]);
18130
18188
  return /* @__PURE__ */ jsx177(Button, {
@@ -18187,7 +18245,7 @@ var padding2 = {
18187
18245
  width: TIMELINE_PADDING
18188
18246
  };
18189
18247
  var PreviewToolbar = ({ readOnlyStudio, bufferStateDelayInMilliseconds }) => {
18190
- const { playbackRate, setPlaybackRate } = useContext53(Internals46.Timeline.TimelineContext);
18248
+ const { playbackRate, setPlaybackRate } = useContext53(Internals46.TimelineContext);
18191
18249
  const { mediaMuted } = useContext53(Internals46.MediaVolumeContext);
18192
18250
  const { setMediaMuted } = useContext53(Internals46.SetMediaVolumeContext);
18193
18251
  const isVideoComposition = useIsVideoComposition();
@@ -20468,7 +20526,7 @@ import { Internals as Internals52 } from "remotion";
20468
20526
  var lastTimelinePositionWhileScrolling = null;
20469
20527
  var TimelinePlayCursorSyncer = () => {
20470
20528
  const video = Internals52.useVideo();
20471
- const timelineContext = useContext65(Internals52.Timeline.TimelineContext);
20529
+ const timelineContext = useContext65(Internals52.TimelineContext);
20472
20530
  const timelinePosition = Internals52.Timeline.useTimelinePosition();
20473
20531
  const { canvasContent } = useContext65(Internals52.CompositionManager);
20474
20532
  const { zoom: zoomMap } = useContext65(TimelineZoomCtx);
@@ -31823,6 +31881,12 @@ class FlacDemuxer extends Demuxer {
31823
31881
  slice.skip(-1);
31824
31882
  continue;
31825
31883
  }
31884
+ if (nextIsLegit.num - frameHeader.num !== 1) {
31885
+ console.log("skipping", nextIsLegit.num, frameHeader.num);
31886
+ slice.skip(-1);
31887
+ continue;
31888
+ }
31889
+ console.log(frameHeader.num, nextIsLegit.num, slice.filePos);
31826
31890
  return {
31827
31891
  num: frameHeader.num,
31828
31892
  blockSize: frameHeader.blockSize,
@@ -32488,7 +32552,7 @@ class UrlSource extends Source {
32488
32552
  return this._orchestrator.read(start, end);
32489
32553
  }
32490
32554
  async _runWorker(worker) {
32491
- while (true) {
32555
+ while (!worker.aborted) {
32492
32556
  const existing = this._existingResponses.get(worker);
32493
32557
  this._existingResponses.delete(worker);
32494
32558
  let abortController = existing?.abortController;
@@ -32537,9 +32601,6 @@ class UrlSource extends Source {
32537
32601
  throw error;
32538
32602
  }
32539
32603
  }
32540
- if (worker.aborted) {
32541
- break;
32542
- }
32543
32604
  const { done, value } = readResult;
32544
32605
  if (done) {
32545
32606
  this._orchestrator.forgetWorker(worker);
@@ -32552,9 +32613,6 @@ class UrlSource extends Source {
32552
32613
  this.onread?.(worker.currentPos, worker.currentPos + value.length);
32553
32614
  this._orchestrator.supplyWorkerData(worker, value);
32554
32615
  }
32555
- if (worker.aborted) {
32556
- break;
32557
- }
32558
32616
  }
32559
32617
  worker.running = false;
32560
32618
  }
@@ -32746,7 +32804,7 @@ class ReadOrchestrator {
32746
32804
  currentPos: startPos,
32747
32805
  targetPos,
32748
32806
  running: false,
32749
- aborted: this.disposed,
32807
+ aborted: false,
32750
32808
  pendingSlices: [],
32751
32809
  age: this.nextAge++
32752
32810
  };
@@ -32785,7 +32843,9 @@ class ReadOrchestrator {
32785
32843
  });
32786
32844
  }
32787
32845
  supplyWorkerData(worker, bytes) {
32788
- assert(!worker.aborted);
32846
+ if (this.disposed) {
32847
+ return;
32848
+ }
32789
32849
  const start = worker.currentPos;
32790
32850
  const end = start + bytes.length;
32791
32851
  this.insertIntoCache({
@@ -33572,7 +33632,7 @@ import { useVideoConfig as useVideoConfig5 } from "remotion";
33572
33632
  async function extractFrames({
33573
33633
  src,
33574
33634
  timestampsInSeconds,
33575
- onFrame,
33635
+ onVideoSample,
33576
33636
  signal
33577
33637
  }) {
33578
33638
  const input2 = new Input({
@@ -33613,8 +33673,7 @@ async function extractFrames({
33613
33673
  if (!videoSample) {
33614
33674
  continue;
33615
33675
  }
33616
- const videoFrame = videoSample.toVideoFrame();
33617
- onFrame(videoFrame);
33676
+ onVideoSample(videoSample);
33618
33677
  }
33619
33678
  } catch (error) {
33620
33679
  if (error instanceof InputDisposedError) {
@@ -33630,11 +33689,15 @@ async function extractFrames({
33630
33689
  }
33631
33690
 
33632
33691
  // src/helpers/frame-database.ts
33633
- var makeFrameDatabaseKey = (src, timestamp) => `${src}|${timestamp}`;
33692
+ var KEY_SEPARATOR = "|";
33693
+ var makeFrameDatabaseKey = (src, timestamp) => `${src}${KEY_SEPARATOR}${timestamp}`;
33694
+ var getFrameDatabaseKeyPrefix = (src) => {
33695
+ return `${src}${KEY_SEPARATOR}`;
33696
+ };
33634
33697
  var frameDatabase = new Map;
33635
33698
  var aspectRatioCache = new Map;
33636
33699
  var getTimestampFromFrameDatabaseKey = (key4) => {
33637
- const split = key4.split("|");
33700
+ const split = key4.split(KEY_SEPARATOR);
33638
33701
  return Number(split[split.length - 1]);
33639
33702
  };
33640
33703
  var getAspectRatioFromCache = (src) => {
@@ -33655,6 +33718,19 @@ var clearOldFrames = () => {
33655
33718
  frameDatabase.delete(key4);
33656
33719
  }
33657
33720
  };
33721
+ var clearFramesForSrc = (src) => {
33722
+ const keysToRemove = [];
33723
+ const prefix = getFrameDatabaseKeyPrefix(src);
33724
+ for (const [key4, frame2] of frameDatabase.entries()) {
33725
+ if (key4.startsWith(prefix)) {
33726
+ frame2.frame.close();
33727
+ keysToRemove.push(key4);
33728
+ }
33729
+ }
33730
+ for (const key4 of keysToRemove) {
33731
+ frameDatabase.delete(key4);
33732
+ }
33733
+ };
33658
33734
 
33659
33735
  // src/helpers/resize-video-frame.ts
33660
33736
  var calculateNewDimensionsFromScale = ({
@@ -33796,7 +33872,8 @@ var fillWithCachedFrames = ({
33796
33872
  segmentDuration,
33797
33873
  fromSeconds
33798
33874
  }) => {
33799
- const keys = Array.from(frameDatabase.keys()).filter((k) => k.startsWith(src));
33875
+ const prefix = getFrameDatabaseKeyPrefix(src);
33876
+ const keys = Array.from(frameDatabase.keys()).filter((k) => k.startsWith(prefix));
33800
33877
  const targets = Array.from(filledSlots.keys());
33801
33878
  for (const timestamp of targets) {
33802
33879
  let bestKey;
@@ -33865,6 +33942,11 @@ var TimelineVideoInfo = ({ src, visualizationWidth, startFrom, durationInFrames
33865
33942
  const ref = useRef37(null);
33866
33943
  const [error, setError] = useState67(null);
33867
33944
  const aspectRatio = useRef37(getAspectRatioFromCache(src));
33945
+ useEffect67(() => {
33946
+ return () => {
33947
+ clearFramesForSrc(src);
33948
+ };
33949
+ }, [src]);
33868
33950
  useEffect67(() => {
33869
33951
  if (error) {
33870
33952
  return;
@@ -33926,7 +34008,8 @@ var TimelineVideoInfo = ({ src, visualizationWidth, startFrom, durationInFrames
33926
34008
  return Array.from(filledSlots.keys()).map((timestamp) => timestamp / WEBCODECS_TIMESCALE);
33927
34009
  },
33928
34010
  src,
33929
- onFrame: (frame2) => {
34011
+ onVideoSample: (sample) => {
34012
+ const frame2 = sample.toVideoFrame();
33930
34013
  const scale = HEIGHT / frame2.displayHeight * window.devicePixelRatio;
33931
34014
  const transformed = resizeVideoFrame({
33932
34015
  frame: frame2,
@@ -42410,23 +42493,28 @@ var Studio = ({ rootComponent, readOnly }) => {
42410
42493
  useLayoutEffect2(() => {
42411
42494
  injectCSS();
42412
42495
  }, []);
42413
- return /* @__PURE__ */ jsx274(Internals67.RemotionRoot, {
42414
- audioEnabled: window.remotion_audioEnabled,
42415
- videoEnabled: window.remotion_videoEnabled,
42416
- logLevel: window.remotion_logLevel,
42417
- numberOfAudioTags: window.remotion_numberOfAudioTags,
42496
+ return /* @__PURE__ */ jsx274(Internals67.CompositionManagerProvider, {
42418
42497
  onlyRenderComposition: null,
42419
42498
  currentCompositionMetadata: null,
42420
- audioLatencyHint: window.remotion_audioLatencyHint ?? "interactive",
42421
- children: /* @__PURE__ */ jsxs141(EditorContexts, {
42422
- readOnlyStudio: readOnly,
42423
- children: [
42424
- /* @__PURE__ */ jsx274(Editor, {
42425
- readOnlyStudio: readOnly,
42426
- Root: rootComponent
42427
- }),
42428
- readOnly ? null : createPortal(/* @__PURE__ */ jsx274(ServerDisconnected, {}), getServerDisconnectedDomElement())
42429
- ]
42499
+ initialCompositions: [],
42500
+ initialCanvasContent: null,
42501
+ children: /* @__PURE__ */ jsx274(Internals67.RemotionRoot, {
42502
+ frameState: null,
42503
+ audioEnabled: window.remotion_audioEnabled,
42504
+ videoEnabled: window.remotion_videoEnabled,
42505
+ logLevel: window.remotion_logLevel,
42506
+ numberOfAudioTags: window.remotion_numberOfAudioTags,
42507
+ audioLatencyHint: window.remotion_audioLatencyHint ?? "interactive",
42508
+ children: /* @__PURE__ */ jsxs141(EditorContexts, {
42509
+ readOnlyStudio: readOnly,
42510
+ children: [
42511
+ /* @__PURE__ */ jsx274(Editor, {
42512
+ readOnlyStudio: readOnly,
42513
+ Root: rootComponent
42514
+ }),
42515
+ readOnly ? null : createPortal(/* @__PURE__ */ jsx274(ServerDisconnected, {}), getServerDisconnectedDomElement())
42516
+ ]
42517
+ })
42430
42518
  })
42431
42519
  });
42432
42520
  };