@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.
@@ -17615,7 +17615,7 @@ var loadPlaybackRate = () => {
17615
17615
 
17616
17616
  // src/components/PlaybackRatePersistor.tsx
17617
17617
  var PlaybackRatePersistor = () => {
17618
- const { setPlaybackRate, playbackRate } = useContext49(Internals40.Timeline.TimelineContext);
17618
+ const { setPlaybackRate, playbackRate } = useContext49(Internals40.TimelineContext);
17619
17619
  useEffect54(() => {
17620
17620
  setPlaybackRate(loadPlaybackRate());
17621
17621
  }, [setPlaybackRate]);
@@ -17924,7 +17924,35 @@ var TimelineZoomControls = () => {
17924
17924
  import ReactDOM8 from "react-dom/client";
17925
17925
  import { Internals as Internals44 } from "remotion";
17926
17926
  import { jsx as jsx176 } from "react/jsx-runtime";
17927
- var compose = ({
17927
+ var svgToImageBitmap = (svg) => {
17928
+ const computedStyle = getComputedStyle(svg);
17929
+ const { transform: originalTransform } = computedStyle;
17930
+ svg.style.transform = "none";
17931
+ const svgDimensions = svg.getBoundingClientRect();
17932
+ svg.style.transform = originalTransform;
17933
+ if (svgDimensions.width === 0 || svgDimensions.height === 0) {
17934
+ return Promise.resolve(null);
17935
+ }
17936
+ const svgData = new XMLSerializer().serializeToString(svg);
17937
+ return new Promise((resolve, reject) => {
17938
+ const image = new Image(svgDimensions.width, svgDimensions.height);
17939
+ const url = "data:image/svg+xml;base64," + window.btoa(svgData);
17940
+ image.onload = function() {
17941
+ resolve({
17942
+ image,
17943
+ width: svgDimensions.width,
17944
+ height: svgDimensions.height,
17945
+ left: svgDimensions.left,
17946
+ top: svgDimensions.top
17947
+ });
17948
+ };
17949
+ image.onerror = () => {
17950
+ reject(new Error("Failed to convert SVG to image"));
17951
+ };
17952
+ image.src = url;
17953
+ });
17954
+ };
17955
+ var compose = async ({
17928
17956
  composables,
17929
17957
  width,
17930
17958
  height
@@ -17935,7 +17963,15 @@ var compose = ({
17935
17963
  throw new Error("Could not get context");
17936
17964
  }
17937
17965
  for (const composable of composables) {
17938
- context.drawImage(composable, 0, 0);
17966
+ if (composable.type === "canvas") {
17967
+ const boundingClientRect = composable.element.getBoundingClientRect();
17968
+ context.drawImage(composable.element, boundingClientRect.left, boundingClientRect.top);
17969
+ } else if (composable.type === "svg") {
17970
+ const imageBitmap = await svgToImageBitmap(composable.element);
17971
+ if (imageBitmap) {
17972
+ context.drawImage(imageBitmap.image, imageBitmap.left, imageBitmap.top);
17973
+ }
17974
+ }
17939
17975
  }
17940
17976
  return canvas;
17941
17977
  };
@@ -17944,15 +17980,30 @@ var findCanvasElements = (element) => {
17944
17980
  const composables = [];
17945
17981
  Array.from(canvasElements).forEach((canvasElement) => {
17946
17982
  const canvas = canvasElement;
17947
- composables.push(canvas);
17983
+ composables.push({
17984
+ type: "canvas",
17985
+ element: canvas
17986
+ });
17987
+ });
17988
+ return composables;
17989
+ };
17990
+ var findSvgElements = (element) => {
17991
+ const svgElements = element.querySelectorAll("svg");
17992
+ const composables = [];
17993
+ Array.from(svgElements).forEach((svgElement) => {
17994
+ const svg = svgElement;
17995
+ composables.push({
17996
+ type: "svg",
17997
+ element: svg
17998
+ });
17948
17999
  });
17949
18000
  return composables;
17950
18001
  };
17951
- var waitForReady = (timeoutInMilliseconds) => {
18002
+ var waitForReady = (timeoutInMilliseconds, scope) => {
17952
18003
  const { promise, resolve, reject } = Promise.withResolvers();
17953
18004
  const start = Date.now();
17954
18005
  const interval = setInterval(() => {
17955
- if (window.remotion_renderReady === true) {
18006
+ if (scope.remotion_renderReady === true) {
17956
18007
  resolve(true);
17957
18008
  clearInterval(interval);
17958
18009
  return;
@@ -17963,67 +18014,41 @@ var waitForReady = (timeoutInMilliseconds) => {
17963
18014
  return;
17964
18015
  }
17965
18016
  if (Date.now() - start > timeoutInMilliseconds + 3000) {
17966
- reject(new Error(Object.values(window.remotion_delayRenderTimeouts).map((d) => d.label).join(", ")));
18017
+ reject(new Error(Object.values(scope.remotion_delayRenderTimeouts).map((d) => d.label).join(", ")));
17967
18018
  clearInterval(interval);
17968
18019
  }
17969
18020
  }, 50);
17970
18021
  return promise;
17971
18022
  };
17972
- var renderStillOnWeb = async ({
18023
+ var COMP_ID = "markup";
18024
+ var internalRenderStillOnWeb = async ({
17973
18025
  Component,
17974
18026
  width,
17975
18027
  height,
17976
18028
  fps,
17977
18029
  durationInFrames,
17978
- frame: frame2
18030
+ frame: frame2,
18031
+ delayRenderTimeoutInMilliseconds,
18032
+ logLevel
17979
18033
  }) => {
17980
18034
  const div = document.createElement("div");
17981
18035
  div.style.display = "flex";
17982
18036
  div.style.backgroundColor = "transparent";
17983
- div.style.position = "absolute";
18037
+ div.style.position = "fixed";
17984
18038
  div.style.width = `${width}px`;
17985
18039
  div.style.height = `${height}px`;
18040
+ div.style.zIndex = "-9999";
17986
18041
  document.body.appendChild(div);
17987
- const delayRenderTimeoutInMilliseconds = 1e4;
17988
18042
  if (!ReactDOM8.createRoot) {
17989
18043
  throw new Error("@remotion/web-renderer requires React 18 or higher");
17990
18044
  }
17991
- const compositionManagerContext = {
17992
- currentCompositionMetadata: {
17993
- durationInFrames,
17994
- fps,
17995
- height,
17996
- width,
17997
- props: {},
17998
- defaultCodec: null,
17999
- defaultOutName: null,
18000
- defaultVideoImageFormat: null,
18001
- defaultPixelFormat: null,
18002
- defaultProResProfile: null
18003
- },
18004
- folders: [],
18005
- compositions: [
18006
- {
18007
- id: "markup",
18008
- component: Component,
18009
- nonce: 0,
18010
- defaultProps: undefined,
18011
- folderName: null,
18012
- parentFolderName: null,
18013
- schema: null,
18014
- calculateMetadata: null,
18015
- durationInFrames,
18016
- fps,
18017
- height,
18018
- width
18019
- }
18020
- ],
18021
- canvasContent: {
18022
- type: "composition",
18023
- compositionId: "markup"
18024
- }
18025
- };
18026
18045
  const root = ReactDOM8.createRoot(div);
18046
+ const delayRenderScope = {
18047
+ remotion_renderReady: true,
18048
+ remotion_delayRenderTimeouts: {},
18049
+ remotion_puppeteerTimeout: delayRenderTimeoutInMilliseconds,
18050
+ remotion_attempt: 0
18051
+ };
18027
18052
  root.render(/* @__PURE__ */ jsx176(Internals44.RemotionEnvironmentContext, {
18028
18053
  value: {
18029
18054
  isStudio: false,
@@ -18032,54 +18057,80 @@ var renderStillOnWeb = async ({
18032
18057
  isReadOnlyStudio: false,
18033
18058
  isClientSideRendering: true
18034
18059
  },
18035
- children: /* @__PURE__ */ jsx176(Internals44.RemotionRoot, {
18036
- audioEnabled: true,
18037
- videoEnabled: true,
18038
- logLevel: "info",
18039
- numberOfAudioTags: 0,
18040
- onlyRenderComposition: null,
18041
- currentCompositionMetadata: {
18042
- props: {},
18043
- durationInFrames,
18044
- fps,
18045
- height,
18046
- width,
18047
- defaultCodec: null,
18048
- defaultOutName: null,
18049
- defaultVideoImageFormat: null,
18050
- defaultPixelFormat: null,
18051
- defaultProResProfile: null
18052
- },
18053
- audioLatencyHint: "interactive",
18054
- children: /* @__PURE__ */ jsx176(Internals44.CanUseRemotionHooks, {
18055
- value: true,
18056
- children: /* @__PURE__ */ jsx176(Internals44.CompositionManager.Provider, {
18057
- value: compositionManagerContext,
18058
- children: /* @__PURE__ */ jsx176(Component, {})
18060
+ children: /* @__PURE__ */ jsx176(Internals44.DelayRenderContextType.Provider, {
18061
+ value: delayRenderScope,
18062
+ children: /* @__PURE__ */ jsx176(Internals44.CompositionManagerProvider, {
18063
+ initialCanvasContent: {
18064
+ type: "composition",
18065
+ compositionId: COMP_ID
18066
+ },
18067
+ onlyRenderComposition: null,
18068
+ currentCompositionMetadata: {
18069
+ props: {},
18070
+ durationInFrames,
18071
+ fps,
18072
+ height,
18073
+ width,
18074
+ defaultCodec: null,
18075
+ defaultOutName: null,
18076
+ defaultVideoImageFormat: null,
18077
+ defaultPixelFormat: null,
18078
+ defaultProResProfile: null
18079
+ },
18080
+ initialCompositions: [
18081
+ {
18082
+ id: COMP_ID,
18083
+ component: Component,
18084
+ nonce: 0,
18085
+ defaultProps: undefined,
18086
+ folderName: null,
18087
+ parentFolderName: null,
18088
+ schema: null,
18089
+ calculateMetadata: null,
18090
+ durationInFrames,
18091
+ fps,
18092
+ height,
18093
+ width
18094
+ }
18095
+ ],
18096
+ children: /* @__PURE__ */ jsx176(Internals44.RemotionRoot, {
18097
+ audioEnabled: false,
18098
+ videoEnabled: true,
18099
+ logLevel,
18100
+ numberOfAudioTags: 0,
18101
+ audioLatencyHint: "interactive",
18102
+ frameState: {
18103
+ [COMP_ID]: frame2
18104
+ },
18105
+ children: /* @__PURE__ */ jsx176(Internals44.CanUseRemotionHooks, {
18106
+ value: true,
18107
+ children: /* @__PURE__ */ jsx176(Component, {})
18108
+ })
18059
18109
  })
18060
18110
  })
18061
18111
  })
18062
18112
  }));
18063
- window.remotion_setFrame(frame2, "markup", frame2);
18064
- await waitForReady(delayRenderTimeoutInMilliseconds);
18113
+ await waitForReady(delayRenderTimeoutInMilliseconds, delayRenderScope);
18065
18114
  const canvasElements = findCanvasElements(div);
18066
- const composed = compose({
18067
- composables: canvasElements,
18115
+ const svgElements = findSvgElements(div);
18116
+ const composed = await compose({
18117
+ composables: [...canvasElements, ...svgElements],
18068
18118
  width,
18069
18119
  height
18070
18120
  });
18071
18121
  const imageData = await composed.convertToBlob({
18072
18122
  type: "image/png"
18073
18123
  });
18074
- const blob = new Blob([imageData], { type: "image/png" });
18075
- const url = URL.createObjectURL(blob);
18076
- const a = document.createElement("a");
18077
- a.href = url;
18078
- a.download = "composed.png";
18079
- a.click();
18080
- URL.revokeObjectURL(url);
18081
18124
  root.unmount();
18082
18125
  div.remove();
18126
+ return imageData;
18127
+ };
18128
+ var renderStillOnWeb = (options) => {
18129
+ return internalRenderStillOnWeb({
18130
+ ...options,
18131
+ delayRenderTimeoutInMilliseconds: options.delayRenderTimeoutInMilliseconds ?? 30000,
18132
+ logLevel: options.logLevel ?? "info"
18133
+ });
18083
18134
  };
18084
18135
 
18085
18136
  // src/components/WebRender/TriggerWebRender.tsx
@@ -18106,6 +18157,13 @@ var TriggerWebRender = () => {
18106
18157
  fps: video.fps,
18107
18158
  durationInFrames: video.durationInFrames,
18108
18159
  frame: frame2
18160
+ }).then((blob) => {
18161
+ const url = URL.createObjectURL(blob);
18162
+ const a = document.createElement("a");
18163
+ a.href = url;
18164
+ a.download = "composed.png";
18165
+ a.click();
18166
+ URL.revokeObjectURL(url);
18109
18167
  });
18110
18168
  }, [video, frame2]);
18111
18169
  return /* @__PURE__ */ jsx177(Button, {
@@ -18168,7 +18226,7 @@ var padding2 = {
18168
18226
  width: TIMELINE_PADDING
18169
18227
  };
18170
18228
  var PreviewToolbar = ({ readOnlyStudio, bufferStateDelayInMilliseconds }) => {
18171
- const { playbackRate, setPlaybackRate } = useContext53(Internals46.Timeline.TimelineContext);
18229
+ const { playbackRate, setPlaybackRate } = useContext53(Internals46.TimelineContext);
18172
18230
  const { mediaMuted } = useContext53(Internals46.MediaVolumeContext);
18173
18231
  const { setMediaMuted } = useContext53(Internals46.SetMediaVolumeContext);
18174
18232
  const isVideoComposition = useIsVideoComposition();
@@ -20449,7 +20507,7 @@ import { Internals as Internals52 } from "remotion";
20449
20507
  var lastTimelinePositionWhileScrolling = null;
20450
20508
  var TimelinePlayCursorSyncer = () => {
20451
20509
  const video = Internals52.useVideo();
20452
- const timelineContext = useContext65(Internals52.Timeline.TimelineContext);
20510
+ const timelineContext = useContext65(Internals52.TimelineContext);
20453
20511
  const timelinePosition = Internals52.Timeline.useTimelinePosition();
20454
20512
  const { canvasContent } = useContext65(Internals52.CompositionManager);
20455
20513
  const { zoom: zoomMap } = useContext65(TimelineZoomCtx);
@@ -31804,6 +31862,12 @@ class FlacDemuxer extends Demuxer {
31804
31862
  slice.skip(-1);
31805
31863
  continue;
31806
31864
  }
31865
+ if (nextIsLegit.num - frameHeader.num !== 1) {
31866
+ console.log("skipping", nextIsLegit.num, frameHeader.num);
31867
+ slice.skip(-1);
31868
+ continue;
31869
+ }
31870
+ console.log(frameHeader.num, nextIsLegit.num, slice.filePos);
31807
31871
  return {
31808
31872
  num: frameHeader.num,
31809
31873
  blockSize: frameHeader.blockSize,
@@ -32469,7 +32533,7 @@ class UrlSource extends Source {
32469
32533
  return this._orchestrator.read(start, end);
32470
32534
  }
32471
32535
  async _runWorker(worker) {
32472
- while (true) {
32536
+ while (!worker.aborted) {
32473
32537
  const existing = this._existingResponses.get(worker);
32474
32538
  this._existingResponses.delete(worker);
32475
32539
  let abortController = existing?.abortController;
@@ -32518,9 +32582,6 @@ class UrlSource extends Source {
32518
32582
  throw error;
32519
32583
  }
32520
32584
  }
32521
- if (worker.aborted) {
32522
- break;
32523
- }
32524
32585
  const { done, value } = readResult;
32525
32586
  if (done) {
32526
32587
  this._orchestrator.forgetWorker(worker);
@@ -32533,9 +32594,6 @@ class UrlSource extends Source {
32533
32594
  this.onread?.(worker.currentPos, worker.currentPos + value.length);
32534
32595
  this._orchestrator.supplyWorkerData(worker, value);
32535
32596
  }
32536
- if (worker.aborted) {
32537
- break;
32538
- }
32539
32597
  }
32540
32598
  worker.running = false;
32541
32599
  }
@@ -32727,7 +32785,7 @@ class ReadOrchestrator {
32727
32785
  currentPos: startPos,
32728
32786
  targetPos,
32729
32787
  running: false,
32730
- aborted: this.disposed,
32788
+ aborted: false,
32731
32789
  pendingSlices: [],
32732
32790
  age: this.nextAge++
32733
32791
  };
@@ -32766,7 +32824,9 @@ class ReadOrchestrator {
32766
32824
  });
32767
32825
  }
32768
32826
  supplyWorkerData(worker, bytes) {
32769
- assert(!worker.aborted);
32827
+ if (this.disposed) {
32828
+ return;
32829
+ }
32770
32830
  const start = worker.currentPos;
32771
32831
  const end = start + bytes.length;
32772
32832
  this.insertIntoCache({
@@ -33553,7 +33613,7 @@ import { useVideoConfig as useVideoConfig5 } from "remotion";
33553
33613
  async function extractFrames({
33554
33614
  src,
33555
33615
  timestampsInSeconds,
33556
- onFrame,
33616
+ onVideoSample,
33557
33617
  signal
33558
33618
  }) {
33559
33619
  const input2 = new Input({
@@ -33594,8 +33654,7 @@ async function extractFrames({
33594
33654
  if (!videoSample) {
33595
33655
  continue;
33596
33656
  }
33597
- const videoFrame = videoSample.toVideoFrame();
33598
- onFrame(videoFrame);
33657
+ onVideoSample(videoSample);
33599
33658
  }
33600
33659
  } catch (error) {
33601
33660
  if (error instanceof InputDisposedError) {
@@ -33611,11 +33670,15 @@ async function extractFrames({
33611
33670
  }
33612
33671
 
33613
33672
  // src/helpers/frame-database.ts
33614
- var makeFrameDatabaseKey = (src, timestamp) => `${src}|${timestamp}`;
33673
+ var KEY_SEPARATOR = "|";
33674
+ var makeFrameDatabaseKey = (src, timestamp) => `${src}${KEY_SEPARATOR}${timestamp}`;
33675
+ var getFrameDatabaseKeyPrefix = (src) => {
33676
+ return `${src}${KEY_SEPARATOR}`;
33677
+ };
33615
33678
  var frameDatabase = new Map;
33616
33679
  var aspectRatioCache = new Map;
33617
33680
  var getTimestampFromFrameDatabaseKey = (key4) => {
33618
- const split = key4.split("|");
33681
+ const split = key4.split(KEY_SEPARATOR);
33619
33682
  return Number(split[split.length - 1]);
33620
33683
  };
33621
33684
  var getAspectRatioFromCache = (src) => {
@@ -33636,6 +33699,19 @@ var clearOldFrames = () => {
33636
33699
  frameDatabase.delete(key4);
33637
33700
  }
33638
33701
  };
33702
+ var clearFramesForSrc = (src) => {
33703
+ const keysToRemove = [];
33704
+ const prefix = getFrameDatabaseKeyPrefix(src);
33705
+ for (const [key4, frame2] of frameDatabase.entries()) {
33706
+ if (key4.startsWith(prefix)) {
33707
+ frame2.frame.close();
33708
+ keysToRemove.push(key4);
33709
+ }
33710
+ }
33711
+ for (const key4 of keysToRemove) {
33712
+ frameDatabase.delete(key4);
33713
+ }
33714
+ };
33639
33715
 
33640
33716
  // src/helpers/resize-video-frame.ts
33641
33717
  var calculateNewDimensionsFromScale = ({
@@ -33777,7 +33853,8 @@ var fillWithCachedFrames = ({
33777
33853
  segmentDuration,
33778
33854
  fromSeconds
33779
33855
  }) => {
33780
- const keys = Array.from(frameDatabase.keys()).filter((k) => k.startsWith(src));
33856
+ const prefix = getFrameDatabaseKeyPrefix(src);
33857
+ const keys = Array.from(frameDatabase.keys()).filter((k) => k.startsWith(prefix));
33781
33858
  const targets = Array.from(filledSlots.keys());
33782
33859
  for (const timestamp of targets) {
33783
33860
  let bestKey;
@@ -33846,6 +33923,11 @@ var TimelineVideoInfo = ({ src, visualizationWidth, startFrom, durationInFrames
33846
33923
  const ref = useRef37(null);
33847
33924
  const [error, setError] = useState67(null);
33848
33925
  const aspectRatio = useRef37(getAspectRatioFromCache(src));
33926
+ useEffect67(() => {
33927
+ return () => {
33928
+ clearFramesForSrc(src);
33929
+ };
33930
+ }, [src]);
33849
33931
  useEffect67(() => {
33850
33932
  if (error) {
33851
33933
  return;
@@ -33907,7 +33989,8 @@ var TimelineVideoInfo = ({ src, visualizationWidth, startFrom, durationInFrames
33907
33989
  return Array.from(filledSlots.keys()).map((timestamp) => timestamp / WEBCODECS_TIMESCALE);
33908
33990
  },
33909
33991
  src,
33910
- onFrame: (frame2) => {
33992
+ onVideoSample: (sample) => {
33993
+ const frame2 = sample.toVideoFrame();
33911
33994
  const scale = HEIGHT / frame2.displayHeight * window.devicePixelRatio;
33912
33995
  const transformed = resizeVideoFrame({
33913
33996
  frame: frame2,
@@ -42391,23 +42474,28 @@ var Studio = ({ rootComponent, readOnly }) => {
42391
42474
  useLayoutEffect2(() => {
42392
42475
  injectCSS();
42393
42476
  }, []);
42394
- return /* @__PURE__ */ jsx274(Internals67.RemotionRoot, {
42395
- audioEnabled: window.remotion_audioEnabled,
42396
- videoEnabled: window.remotion_videoEnabled,
42397
- logLevel: window.remotion_logLevel,
42398
- numberOfAudioTags: window.remotion_numberOfAudioTags,
42477
+ return /* @__PURE__ */ jsx274(Internals67.CompositionManagerProvider, {
42399
42478
  onlyRenderComposition: null,
42400
42479
  currentCompositionMetadata: null,
42401
- audioLatencyHint: window.remotion_audioLatencyHint ?? "interactive",
42402
- children: /* @__PURE__ */ jsxs141(EditorContexts, {
42403
- readOnlyStudio: readOnly,
42404
- children: [
42405
- /* @__PURE__ */ jsx274(Editor, {
42406
- readOnlyStudio: readOnly,
42407
- Root: rootComponent
42408
- }),
42409
- readOnly ? null : createPortal(/* @__PURE__ */ jsx274(ServerDisconnected, {}), getServerDisconnectedDomElement())
42410
- ]
42480
+ initialCompositions: [],
42481
+ initialCanvasContent: null,
42482
+ children: /* @__PURE__ */ jsx274(Internals67.RemotionRoot, {
42483
+ frameState: null,
42484
+ audioEnabled: window.remotion_audioEnabled,
42485
+ videoEnabled: window.remotion_videoEnabled,
42486
+ logLevel: window.remotion_logLevel,
42487
+ numberOfAudioTags: window.remotion_numberOfAudioTags,
42488
+ audioLatencyHint: window.remotion_audioLatencyHint ?? "interactive",
42489
+ children: /* @__PURE__ */ jsxs141(EditorContexts, {
42490
+ readOnlyStudio: readOnly,
42491
+ children: [
42492
+ /* @__PURE__ */ jsx274(Editor, {
42493
+ readOnlyStudio: readOnly,
42494
+ Root: rootComponent
42495
+ }),
42496
+ readOnly ? null : createPortal(/* @__PURE__ */ jsx274(ServerDisconnected, {}), getServerDisconnectedDomElement())
42497
+ ]
42498
+ })
42411
42499
  })
42412
42500
  });
42413
42501
  };