@remotion/studio 4.0.465 → 4.0.467

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 (68) hide show
  1. package/dist/components/CompositionSelectorItem.js +16 -79
  2. package/dist/components/CurrentAsset.js +8 -54
  3. package/dist/components/EditorContent.js +1 -3
  4. package/dist/components/Menu/MenuItem.d.ts +1 -1
  5. package/dist/components/Modals.js +1 -1
  6. package/dist/components/NewComposition/MenuContent.js +1 -0
  7. package/dist/components/NewComposition/ValidationMessage.d.ts +1 -0
  8. package/dist/components/NewComposition/ValidationMessage.js +3 -3
  9. package/dist/components/RenderButton.js +1 -0
  10. package/dist/components/RenderModal/DataEditor.js +8 -2
  11. package/dist/components/RenderModal/RenderModalAdvanced.d.ts +1 -11
  12. package/dist/components/RenderModal/RenderModalAdvanced.js +4 -50
  13. package/dist/components/RenderModal/RenderModalEncoding.d.ts +37 -0
  14. package/dist/components/RenderModal/RenderModalEncoding.js +165 -0
  15. package/dist/components/RenderModal/RenderModalPicture.d.ts +1 -22
  16. package/dist/components/RenderModal/RenderModalPicture.js +6 -84
  17. package/dist/components/RenderModal/ServerRenderModal.d.ts +1 -0
  18. package/dist/components/RenderModal/ServerRenderModal.js +16 -4
  19. package/dist/components/RenderModal/get-render-modal-warnings.d.ts +7 -1
  20. package/dist/components/RenderModal/get-render-modal-warnings.js +21 -7
  21. package/dist/components/RenderQueue/actions.d.ts +2 -1
  22. package/dist/components/RenderQueue/actions.js +2 -1
  23. package/dist/components/SidebarRenderButton.js +1 -0
  24. package/dist/components/Timeline/Timeline.js +6 -4
  25. package/dist/components/Timeline/TimelineEffectFieldRow.js +2 -1
  26. package/dist/components/Timeline/TimelineEffectGroupRow.d.ts +1 -0
  27. package/dist/components/Timeline/TimelineEffectGroupRow.js +18 -2
  28. package/dist/components/Timeline/TimelineExpandedRow.d.ts +1 -1
  29. package/dist/components/Timeline/TimelineExpandedRow.js +1 -1
  30. package/dist/components/Timeline/TimelineExpandedSection.js +9 -2
  31. package/dist/components/Timeline/TimelineExpandedTrackKeyframes.d.ts +7 -0
  32. package/dist/components/Timeline/TimelineExpandedTrackKeyframes.js +124 -0
  33. package/dist/components/Timeline/TimelineListItem.js +21 -3
  34. package/dist/components/Timeline/TimelineMediaInfo.d.ts +5 -0
  35. package/dist/components/Timeline/TimelineMediaInfo.js +173 -0
  36. package/dist/components/Timeline/TimelineSchemaField.js +2 -1
  37. package/dist/components/Timeline/TimelineStack/index.js +7 -47
  38. package/dist/components/Timeline/TimelineTracks.js +2 -16
  39. package/dist/components/Timeline/TimelineVideoInfo.js +2 -2
  40. package/dist/components/Timeline/get-timeline-keyframes.d.ts +6 -0
  41. package/dist/components/Timeline/get-timeline-keyframes.js +22 -0
  42. package/dist/components/composition-menu-items.d.ts +12 -0
  43. package/dist/components/composition-menu-items.js +166 -0
  44. package/dist/esm/chunk-5gtx3pza.js +9 -0
  45. package/dist/esm/{chunk-pqk2qd0d.js → chunk-vwnse6c9.js} +4297 -3547
  46. package/dist/esm/index.mjs +0 -16
  47. package/dist/esm/internals.mjs +4295 -3560
  48. package/dist/esm/previewEntry.mjs +4049 -3314
  49. package/dist/esm/renderEntry.mjs +3 -4
  50. package/dist/helpers/format-media-duration.d.ts +1 -0
  51. package/dist/helpers/format-media-duration.js +14 -0
  52. package/dist/helpers/get-timeline-sequence-layout.js +4 -3
  53. package/dist/helpers/make-render-command.d.ts +2 -2
  54. package/dist/helpers/make-render-command.js +2 -1
  55. package/dist/helpers/open-in-editor.d.ts +8 -0
  56. package/dist/helpers/open-in-editor.js +58 -1
  57. package/dist/helpers/render-modal-sections.d.ts +1 -1
  58. package/dist/helpers/render-modal-sections.js +35 -5
  59. package/dist/helpers/retry-payload.js +3 -0
  60. package/dist/helpers/timeline-layout.d.ts +11 -6
  61. package/dist/helpers/timeline-layout.js +28 -8
  62. package/dist/helpers/use-max-media-duration.js +25 -28
  63. package/dist/helpers/use-media-metadata.d.ts +10 -0
  64. package/dist/helpers/use-media-metadata.js +135 -0
  65. package/dist/helpers/use-menu-structure.js +43 -0
  66. package/dist/state/modals.d.ts +1 -0
  67. package/package.json +10 -10
  68. package/dist/esm/chunk-6jf1natv.js +0 -25
@@ -1,7 +1,6 @@
1
1
  import {
2
- __require,
3
- __toESM
4
- } from "./chunk-6jf1natv.js";
2
+ __require
3
+ } from "./chunk-5gtx3pza.js";
5
4
 
6
5
  // src/renderEntry.tsx
7
6
  import { useContext, useEffect, useRef, useState } from "react";
@@ -207,7 +206,7 @@ var renderContent = (Root) => {
207
206
  renderToDOM(/* @__PURE__ */ jsx("div", {
208
207
  children: /* @__PURE__ */ jsx(DelayedSpinner, {})
209
208
  }));
210
- import("./chunk-pqk2qd0d.js").then(({ StudioInternals }) => {
209
+ import("./chunk-vwnse6c9.js").then(({ StudioInternals }) => {
211
210
  window.remotion_isStudio = true;
212
211
  window.remotion_isReadOnlyStudio = true;
213
212
  window.remotion_inputProps = "{}";
@@ -0,0 +1 @@
1
+ export declare const formatMediaDuration: (seconds: number) => string;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatMediaDuration = void 0;
4
+ const formatMediaDuration = (seconds) => {
5
+ const h = Math.floor(seconds / 3600);
6
+ const m = Math.floor((seconds % 3600) / 60);
7
+ const s = seconds % 60;
8
+ const sFixed = s.toFixed(2).padStart(5, '0');
9
+ if (h > 0) {
10
+ return `${h}:${String(m).padStart(2, '0')}:${sFixed}`;
11
+ }
12
+ return `${String(m).padStart(2, '0')}:${sFixed}`;
13
+ };
14
+ exports.formatMediaDuration = formatMediaDuration;
@@ -13,10 +13,11 @@ const getWidthOfTrack = ({ durationInFrames, lastFrame, windowWidth, spatialDura
13
13
  const getTimelineSequenceLayout = ({ durationInFrames, startFrom, maxMediaDuration, startFromMedia, video, windowWidth, premountDisplay, postmountDisplay, }) => {
14
14
  var _a;
15
15
  const maxMediaSequenceDuration = (maxMediaDuration !== null && maxMediaDuration !== void 0 ? maxMediaDuration : Infinity) - startFromMedia;
16
- const lastFrame = ((_a = video.durationInFrames) !== null && _a !== void 0 ? _a : 1) - 1;
17
- const spatialDuration = Math.max(0, Math.min(maxMediaSequenceDuration, durationInFrames - 1, lastFrame - startFrom));
16
+ const timelineDuration = (_a = video.durationInFrames) !== null && _a !== void 0 ? _a : 1;
17
+ const lastFrame = timelineDuration - 1;
18
+ const spatialDuration = Math.max(0, Math.min(maxMediaSequenceDuration, durationInFrames, timelineDuration - startFrom));
18
19
  // Unclipped spatial duration: without the lastFrame - startFrom constraint
19
- const naturalSpatialDuration = Math.max(0, Math.min(maxMediaSequenceDuration, durationInFrames - 1));
20
+ const naturalSpatialDuration = Math.max(0, Math.min(maxMediaSequenceDuration, durationInFrames));
20
21
  const marginLeft = lastFrame === 0
21
22
  ? 0
22
23
  : (startFrom / lastFrame) * (windowWidth - timeline_layout_1.TIMELINE_PADDING * 2);
@@ -10,7 +10,7 @@ export declare const normalizeServeUrlForRenderCommand: ({ locationHref, composi
10
10
  type StrictRequired<T> = {
11
11
  [K in keyof T]-?: Exclude<T[K], undefined>;
12
12
  };
13
- type RenderMediaCommandOptions = Omit<StrictRequired<Pick<RenderMediaOptions, 'codec' | 'crf' | 'concurrency' | 'disallowParallelEncoding' | 'muted' | 'enforceAudioTrack' | 'everyNthFrame' | 'numberOfGifLoops' | 'colorSpace' | 'scale' | 'logLevel' | 'repro' | 'metadata' | 'jpegQuality' | 'pixelFormat' | 'proResProfile' | 'x264Preset' | 'audioCodec' | 'forSeamlessAacConcatenation' | 'separateAudioTo' | 'hardwareAcceleration' | 'chromeMode' | 'offthreadVideoCacheSizeInBytes' | 'offthreadVideoThreads' | 'mediaCacheSizeInBytes' | 'audioBitrate' | 'videoBitrate' | 'encodingMaxRate' | 'encodingBufferSize' | 'sampleRate'>>, 'audioBitrate' | 'videoBitrate' | 'encodingMaxRate' | 'encodingBufferSize' | 'jpegQuality' | 'proResProfile'> & {
13
+ type RenderMediaCommandOptions = Omit<StrictRequired<Pick<RenderMediaOptions, 'codec' | 'crf' | 'concurrency' | 'disallowParallelEncoding' | 'muted' | 'enforceAudioTrack' | 'everyNthFrame' | 'numberOfGifLoops' | 'colorSpace' | 'scale' | 'logLevel' | 'repro' | 'metadata' | 'jpegQuality' | 'pixelFormat' | 'proResProfile' | 'x264Preset' | 'gopSize' | 'audioCodec' | 'forSeamlessAacConcatenation' | 'separateAudioTo' | 'hardwareAcceleration' | 'chromeMode' | 'offthreadVideoCacheSizeInBytes' | 'offthreadVideoThreads' | 'mediaCacheSizeInBytes' | 'audioBitrate' | 'videoBitrate' | 'encodingMaxRate' | 'encodingBufferSize' | 'sampleRate'>>, 'audioBitrate' | 'videoBitrate' | 'encodingMaxRate' | 'encodingBufferSize' | 'jpegQuality' | 'proResProfile'> & {
14
14
  audioBitrate: string | null;
15
15
  videoBitrate: string | null;
16
16
  encodingMaxRate: string | null;
@@ -48,5 +48,5 @@ type ReadOnlyStudioRenderCommandInput = RenderMediaCommandOptions & RenderStillC
48
48
  envVariables: Record<string, string>;
49
49
  inputProps: Record<string, unknown>;
50
50
  };
51
- export declare const makeReadOnlyStudioRenderCommand: ({ remotionVersion, locationHref, compositionId, outName, renderMode, renderDefaults, durationInFrames, concurrency, frame, startFrame, endFrame, stillImageFormat, sequenceImageFormat, videoImageFormat, jpegQuality, codec, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, crf, videoBitrate, audioBitrate, audioCodec, everyNthFrame, numberOfGifLoops, disallowParallelEncoding, encodingBufferSize, encodingMaxRate, forSeamlessAacConcatenation, separateAudioTo, colorSpace, scale, logLevel, delayRenderTimeout, hardwareAcceleration, chromeMode, headless, disableWebSecurity, ignoreCertificateErrors, gl, userAgent, multiProcessOnLinux, darkMode, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, mediaCacheSizeInBytes, beepOnFinish, repro, metadata, sampleRate, envVariables, inputProps, }: ReadOnlyStudioRenderCommandInput) => string;
51
+ export declare const makeReadOnlyStudioRenderCommand: ({ remotionVersion, locationHref, compositionId, outName, renderMode, renderDefaults, durationInFrames, concurrency, frame, startFrame, endFrame, stillImageFormat, sequenceImageFormat, videoImageFormat, jpegQuality, codec, muted, enforceAudioTrack, proResProfile, x264Preset, gopSize, pixelFormat, crf, videoBitrate, audioBitrate, audioCodec, everyNthFrame, numberOfGifLoops, disallowParallelEncoding, encodingBufferSize, encodingMaxRate, forSeamlessAacConcatenation, separateAudioTo, colorSpace, scale, logLevel, delayRenderTimeout, hardwareAcceleration, chromeMode, headless, disableWebSecurity, ignoreCertificateErrors, gl, userAgent, multiProcessOnLinux, darkMode, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, mediaCacheSizeInBytes, beepOnFinish, repro, metadata, sampleRate, envVariables, inputProps, }: ReadOnlyStudioRenderCommandInput) => string;
52
52
  export {};
@@ -80,7 +80,7 @@ const renderMediaValueFlag = (option, value, defaultValue) => {
80
80
  const renderMediaBooleanFlag = (option, value, defaultValue) => {
81
81
  return booleanFlag(getRenderMediaFlag(option), value, defaultValue);
82
82
  };
83
- const makeReadOnlyStudioRenderCommand = ({ remotionVersion, locationHref, compositionId, outName, renderMode, renderDefaults, durationInFrames, concurrency, frame, startFrame, endFrame, stillImageFormat, sequenceImageFormat, videoImageFormat, jpegQuality, codec, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, crf, videoBitrate, audioBitrate, audioCodec, everyNthFrame, numberOfGifLoops, disallowParallelEncoding, encodingBufferSize, encodingMaxRate, forSeamlessAacConcatenation, separateAudioTo, colorSpace, scale, logLevel, delayRenderTimeout, hardwareAcceleration, chromeMode, headless, disableWebSecurity, ignoreCertificateErrors, gl, userAgent, multiProcessOnLinux, darkMode, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, mediaCacheSizeInBytes, beepOnFinish, repro, metadata, sampleRate, envVariables, inputProps, }) => {
83
+ const makeReadOnlyStudioRenderCommand = ({ remotionVersion, locationHref, compositionId, outName, renderMode, renderDefaults, durationInFrames, concurrency, frame, startFrame, endFrame, stillImageFormat, sequenceImageFormat, videoImageFormat, jpegQuality, codec, muted, enforceAudioTrack, proResProfile, x264Preset, gopSize, pixelFormat, crf, videoBitrate, audioBitrate, audioCodec, everyNthFrame, numberOfGifLoops, disallowParallelEncoding, encodingBufferSize, encodingMaxRate, forSeamlessAacConcatenation, separateAudioTo, colorSpace, scale, logLevel, delayRenderTimeout, hardwareAcceleration, chromeMode, headless, disableWebSecurity, ignoreCertificateErrors, gl, userAgent, multiProcessOnLinux, darkMode, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, mediaCacheSizeInBytes, beepOnFinish, repro, metadata, sampleRate, envVariables, inputProps, }) => {
84
84
  var _a;
85
85
  const serveUrl = (0, exports.normalizeServeUrlForRenderCommand)({
86
86
  locationHref,
@@ -154,6 +154,7 @@ const makeReadOnlyStudioRenderCommand = ({ remotionVersion, locationHref, compos
154
154
  renderMediaValueFlag('colorSpace', colorSpace, renderDefaults.colorSpace),
155
155
  valueFlag(options.proResProfileOption.cliFlag, proResProfile, renderDefaults.proResProfile),
156
156
  renderMediaValueFlag('x264Preset', x264Preset, renderDefaults.x264Preset),
157
+ renderMediaValueFlag('gopSize', gopSize, renderDefaults.gopSize),
157
158
  valueFlag(options.crfOption.cliFlag, crf, null),
158
159
  valueFlag(options.jpegQualityOption.cliFlag, jpegQuality, renderDefaults.jpegQuality),
159
160
  renderMediaValueFlag('videoBitrate', videoBitrate, renderDefaults.videoBitrate),
@@ -2,3 +2,11 @@ import type { SymbolicatedStackFrame } from '@remotion/studio-shared';
2
2
  import type { OriginalPosition } from '../error-overlay/react-overlay/utils/get-source-map';
3
3
  export declare const openInEditor: (stack: SymbolicatedStackFrame) => Promise<Response>;
4
4
  export declare const openOriginalPositionInEditor: (originalPosition: OriginalPosition) => Promise<void>;
5
+ export declare const preloadCompositionComponentInfo: ({ compositionFile, compositionId, }: {
6
+ compositionFile: string;
7
+ compositionId: string;
8
+ }) => void;
9
+ export declare const openCompositionComponentInEditor: ({ compositionFile, compositionId, }: {
10
+ compositionFile: string;
11
+ compositionId: string;
12
+ }) => Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openOriginalPositionInEditor = exports.openInEditor = void 0;
3
+ exports.openCompositionComponentInEditor = exports.preloadCompositionComponentInfo = exports.openOriginalPositionInEditor = exports.openInEditor = void 0;
4
4
  const openInEditor = (stack) => {
5
5
  const { originalFileName, originalLineNumber, originalColumnNumber, originalFunctionName, originalScriptCode, } = stack;
6
6
  return fetch(`/api/open-in-editor`, {
@@ -30,3 +30,60 @@ const openOriginalPositionInEditor = async (originalPosition) => {
30
30
  });
31
31
  };
32
32
  exports.openOriginalPositionInEditor = openOriginalPositionInEditor;
33
+ const componentResolutionCache = new Map();
34
+ const getComponentResolutionCacheKey = ({ compositionFile, compositionId, }) => {
35
+ return `${compositionFile}::${compositionId}`;
36
+ };
37
+ const loadCompositionComponentInfo = async ({ compositionFile, compositionId, }) => {
38
+ const cacheKey = getComponentResolutionCacheKey({
39
+ compositionFile,
40
+ compositionId,
41
+ });
42
+ const existing = componentResolutionCache.get(cacheKey);
43
+ if (existing) {
44
+ return existing;
45
+ }
46
+ const promise = (async () => {
47
+ const response = await fetch(`/api/composition-component-info`, {
48
+ method: 'post',
49
+ headers: {
50
+ 'content-type': 'application/json',
51
+ },
52
+ body: JSON.stringify({
53
+ compositionFile,
54
+ compositionId,
55
+ }),
56
+ });
57
+ const body = (await response.json());
58
+ if (!body.success) {
59
+ throw new Error(body.error);
60
+ }
61
+ return {
62
+ location: body.location,
63
+ canAddSequence: body.canAddSequence,
64
+ };
65
+ })();
66
+ componentResolutionCache.set(cacheKey, promise);
67
+ try {
68
+ return await promise;
69
+ }
70
+ catch (err) {
71
+ componentResolutionCache.delete(cacheKey);
72
+ throw err;
73
+ }
74
+ };
75
+ const preloadCompositionComponentInfo = ({ compositionFile, compositionId, }) => {
76
+ loadCompositionComponentInfo({
77
+ compositionFile,
78
+ compositionId,
79
+ }).catch(() => undefined);
80
+ };
81
+ exports.preloadCompositionComponentInfo = preloadCompositionComponentInfo;
82
+ const openCompositionComponentInEditor = async ({ compositionFile, compositionId, }) => {
83
+ const info = await loadCompositionComponentInfo({
84
+ compositionFile,
85
+ compositionId,
86
+ });
87
+ await (0, exports.openOriginalPositionInEditor)(info.location);
88
+ };
89
+ exports.openCompositionComponentInEditor = openCompositionComponentInEditor;
@@ -1,5 +1,5 @@
1
1
  import type { RenderType } from '../components/RenderModal/RenderModalAdvanced';
2
- type Section = 'general' | 'picture' | 'advanced' | 'data' | 'gif' | 'audio';
2
+ type Section = 'general' | 'data' | 'picture' | 'audio' | 'gif' | 'encoding' | 'environment' | 'advanced';
3
3
  export declare const useRenderModalSections: (renderMode: RenderType, codec: "aac" | "av1" | "gif" | "h264" | "h264-mkv" | "h264-ts" | "h265" | "mp3" | "prores" | "vp8" | "vp9" | "wav") => {
4
4
  tab: Section;
5
5
  setTab: import("react").Dispatch<import("react").SetStateAction<Section>>;
@@ -6,19 +6,49 @@ const useRenderModalSections = (renderMode, codec) => {
6
6
  const [selectedTab, setTab] = (0, react_1.useState)('general');
7
7
  const shownTabs = (0, react_1.useMemo)(() => {
8
8
  if (renderMode === 'audio') {
9
- return ['general', 'data', 'audio', 'advanced'];
9
+ return [
10
+ 'general',
11
+ 'data',
12
+ 'audio',
13
+ 'encoding',
14
+ 'environment',
15
+ 'advanced',
16
+ ];
10
17
  }
11
18
  if (renderMode === 'still') {
12
- return ['general', 'data', 'picture', 'advanced'];
19
+ return ['general', 'data', 'picture', 'environment', 'advanced'];
13
20
  }
14
21
  if (renderMode === 'sequence') {
15
- return ['general', 'data', 'picture', 'advanced'];
22
+ return [
23
+ 'general',
24
+ 'data',
25
+ 'picture',
26
+ 'encoding',
27
+ 'environment',
28
+ 'advanced',
29
+ ];
16
30
  }
17
31
  if (renderMode === 'video') {
18
32
  if (codec === 'gif') {
19
- return ['general', 'data', 'picture', 'gif', 'advanced'];
33
+ return [
34
+ 'general',
35
+ 'data',
36
+ 'picture',
37
+ 'gif',
38
+ 'encoding',
39
+ 'environment',
40
+ 'advanced',
41
+ ];
20
42
  }
21
- return ['general', 'data', 'picture', 'audio', 'advanced'];
43
+ return [
44
+ 'general',
45
+ 'data',
46
+ 'picture',
47
+ 'audio',
48
+ 'encoding',
49
+ 'environment',
50
+ 'advanced',
51
+ ];
22
52
  }
23
53
  throw new TypeError('Unknown render mode');
24
54
  }, [codec, renderMode]);
@@ -25,6 +25,7 @@ const makeRetryPayload = (job) => {
25
25
  initialEnforceAudioTrack: defaults.enforceAudioTrack,
26
26
  initialProResProfile: null,
27
27
  initialx264Preset: defaults.x264Preset,
28
+ initialGopSize: defaults.gopSize,
28
29
  initialPixelFormat: defaults.pixelFormat,
29
30
  initialAudioBitrate: defaults.audioBitrate,
30
31
  initialVideoBitrate: defaults.videoBitrate,
@@ -78,6 +79,7 @@ const makeRetryPayload = (job) => {
78
79
  initialEnforceAudioTrack: defaults.enforceAudioTrack,
79
80
  initialProResProfile: null,
80
81
  initialx264Preset: defaults.x264Preset,
82
+ initialGopSize: defaults.gopSize,
81
83
  initialPixelFormat: defaults.pixelFormat,
82
84
  initialAudioBitrate: defaults.audioBitrate,
83
85
  initialVideoBitrate: defaults.videoBitrate,
@@ -133,6 +135,7 @@ const makeRetryPayload = (job) => {
133
135
  initialEnforceAudioTrack: job.enforceAudioTrack,
134
136
  initialProResProfile: (_d = job.proResProfile) !== null && _d !== void 0 ? _d : null,
135
137
  initialx264Preset: (_e = job.x264Preset) !== null && _e !== void 0 ? _e : defaults.x264Preset,
138
+ initialGopSize: job.gopSize,
136
139
  initialPixelFormat: job.pixelFormat,
137
140
  initialAudioBitrate: job.audioBitrate,
138
141
  initialVideoBitrate: job.videoBitrate,
@@ -1,9 +1,9 @@
1
1
  import { type AnySchemaFieldInfo, type CodeValues, type DragOverrides, type EffectSchemaFieldInfo, type SchemaFieldInfo, type SequenceControls, type SequenceSchemaFieldInfo } from '@remotion/studio-shared';
2
- import type { GetDragOverrides, SequenceSchema as SequenceSchemaShape, TSequence } from 'remotion';
2
+ import type { GetDragOverrides, GetEffectDragOverrides, SequenceSchema as SequenceSchemaShape, TSequence } from 'remotion';
3
3
  import type { GetIsExpanded } from '../components/ExpandedTracksProvider';
4
4
  import type { SequenceNodePathInfo } from './get-timeline-sequence-sort-key';
5
+ export { getEffectFieldsToShow, getFieldsToShow, SCHEMA_FIELD_ROW_HEIGHT, } from '@remotion/studio-shared';
5
6
  export type { AnySchemaFieldInfo, CodeValues, DragOverrides, EffectSchemaFieldInfo, SchemaFieldInfo, SequenceControls, SequenceSchemaFieldInfo, };
6
- export { SCHEMA_FIELD_ROW_HEIGHT, getEffectFieldsToShow, getFieldsToShow, } from '@remotion/studio-shared';
7
7
  export declare const TIMELINE_PADDING = 16;
8
8
  export declare const TIMELINE_BORDER = 1;
9
9
  export declare const TIMELINE_ITEM_BORDER_BOTTOM = 1;
@@ -15,6 +15,7 @@ export type TimelineFieldOnDragValueChange = (value: unknown) => void;
15
15
  export type TimelineEffectGroupInfo = {
16
16
  readonly effectIndex: number;
17
17
  readonly effectSchema: SequenceSchemaShape;
18
+ readonly documentationLink: string | null;
18
19
  };
19
20
  export type TimelineTreeNode = {
20
21
  readonly kind: 'group';
@@ -28,10 +29,11 @@ export type TimelineTreeNode = {
28
29
  readonly label: string;
29
30
  readonly field: AnySchemaFieldInfo | null;
30
31
  };
31
- export declare const buildTimelineTree: ({ sequence, nodePathInfo, getDragOverrides, codeValues, }: {
32
+ export declare const buildTimelineTree: ({ sequence, nodePathInfo, getDragOverrides, getEffectDragOverrides, codeValues, }: {
32
33
  sequence: TSequence;
33
34
  nodePathInfo: SequenceNodePathInfo;
34
35
  getDragOverrides: GetDragOverrides;
36
+ getEffectDragOverrides: GetEffectDragOverrides;
35
37
  codeValues: CodeValues;
36
38
  }) => TimelineTreeNode[];
37
39
  export type FlatTreeRow = {
@@ -51,6 +53,9 @@ export declare const getExpandedTrackHeight: ({ sequence, nodePathInfo, getIsExp
51
53
  codeValues: CodeValues;
52
54
  }) => number;
53
55
  export declare const TIMELINE_LAYER_HEIGHT_VIDEO = 75;
54
- export declare const TIMELINE_LAYER_HEIGHT_IMAGE = 50;
55
- export declare const TIMELINE_LAYER_HEIGHT_AUDIO = 25;
56
- export declare const getTimelineLayerHeight: (type: "audio" | "image" | "other" | "sequence" | "video") => 25 | 50 | 75;
56
+ export declare const TIMELINE_LAYER_HEIGHT_IMAGE = 58;
57
+ export declare const TIMELINE_LAYER_HEIGHT_AUDIO = 58;
58
+ export declare const TIMELINE_LAYER_HEIGHT_DEFAULT = 25;
59
+ export declare const TIMELINE_LIST_ITEM_ROW_HEIGHT = 25;
60
+ export declare const TIMELINE_VIDEO_INFO_WAVEFORM_HEIGHT = 25;
61
+ export declare const getTimelineLayerHeight: (type: "audio" | "image" | "other" | "sequence" | "video") => 25 | 58 | 75;
@@ -1,18 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getTimelineLayerHeight = exports.TIMELINE_LAYER_HEIGHT_AUDIO = exports.TIMELINE_LAYER_HEIGHT_IMAGE = exports.TIMELINE_LAYER_HEIGHT_VIDEO = exports.getExpandedTrackHeight = exports.getTreeRowHeight = exports.flattenVisibleTreeNodes = exports.buildTimelineTree = exports.EXPANDED_SECTION_PADDING_RIGHT = exports.TREE_GROUP_ROW_HEIGHT = exports.TIMELINE_TRACK_EXPANDED_HEIGHT = exports.TIMELINE_ITEM_BORDER_BOTTOM = exports.TIMELINE_BORDER = exports.TIMELINE_PADDING = exports.getFieldsToShow = exports.getEffectFieldsToShow = exports.SCHEMA_FIELD_ROW_HEIGHT = void 0;
3
+ exports.getTimelineLayerHeight = exports.TIMELINE_VIDEO_INFO_WAVEFORM_HEIGHT = exports.TIMELINE_LIST_ITEM_ROW_HEIGHT = exports.TIMELINE_LAYER_HEIGHT_DEFAULT = exports.TIMELINE_LAYER_HEIGHT_AUDIO = exports.TIMELINE_LAYER_HEIGHT_IMAGE = exports.TIMELINE_LAYER_HEIGHT_VIDEO = exports.getExpandedTrackHeight = exports.getTreeRowHeight = exports.flattenVisibleTreeNodes = exports.buildTimelineTree = exports.EXPANDED_SECTION_PADDING_RIGHT = exports.TREE_GROUP_ROW_HEIGHT = exports.TIMELINE_TRACK_EXPANDED_HEIGHT = exports.TIMELINE_ITEM_BORDER_BOTTOM = exports.TIMELINE_BORDER = exports.TIMELINE_PADDING = exports.SCHEMA_FIELD_ROW_HEIGHT = exports.getFieldsToShow = exports.getEffectFieldsToShow = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const studio_shared_2 = require("@remotion/studio-shared");
6
- Object.defineProperty(exports, "SCHEMA_FIELD_ROW_HEIGHT", { enumerable: true, get: function () { return studio_shared_2.SCHEMA_FIELD_ROW_HEIGHT; } });
7
6
  Object.defineProperty(exports, "getEffectFieldsToShow", { enumerable: true, get: function () { return studio_shared_2.getEffectFieldsToShow; } });
8
7
  Object.defineProperty(exports, "getFieldsToShow", { enumerable: true, get: function () { return studio_shared_2.getFieldsToShow; } });
8
+ Object.defineProperty(exports, "SCHEMA_FIELD_ROW_HEIGHT", { enumerable: true, get: function () { return studio_shared_2.SCHEMA_FIELD_ROW_HEIGHT; } });
9
9
  exports.TIMELINE_PADDING = 16;
10
10
  exports.TIMELINE_BORDER = 1;
11
11
  exports.TIMELINE_ITEM_BORDER_BOTTOM = 1;
12
12
  exports.TIMELINE_TRACK_EXPANDED_HEIGHT = 100;
13
13
  exports.TREE_GROUP_ROW_HEIGHT = 22;
14
14
  exports.EXPANDED_SECTION_PADDING_RIGHT = 10;
15
- const buildTimelineTree = ({ sequence, nodePathInfo, getDragOverrides, codeValues, }) => {
15
+ const buildTimelineTree = ({ sequence, nodePathInfo, getDragOverrides, getEffectDragOverrides, codeValues, }) => {
16
16
  var _a;
17
17
  const roots = [];
18
18
  const { sequenceSubscriptionKey, index, auxiliaryKeys } = nodePathInfo;
@@ -50,7 +50,14 @@ const buildTimelineTree = ({ sequence, nodePathInfo, getDragOverrides, codeValue
50
50
  label: 'Effects',
51
51
  effectInfo: null,
52
52
  children: sequence.effects.map((effect, i) => {
53
- const effectFields = (0, studio_shared_1.getEffectFieldsToShow)(effect, i);
53
+ var _a;
54
+ const effectFields = (0, studio_shared_1.getEffectFieldsToShow)({
55
+ effect,
56
+ effectIndex: i,
57
+ nodePath: sequenceSubscriptionKey,
58
+ codeValues,
59
+ getEffectDragOverrides,
60
+ });
54
61
  return {
55
62
  kind: 'group',
56
63
  nodePathInfo: {
@@ -60,7 +67,11 @@ const buildTimelineTree = ({ sequence, nodePathInfo, getDragOverrides, codeValue
60
67
  numberOfSequencesWithThisNodePath: 0,
61
68
  },
62
69
  label: effect.label,
63
- effectInfo: { effectIndex: i, effectSchema: effect.schema },
70
+ effectInfo: {
71
+ effectIndex: i,
72
+ effectSchema: effect.schema,
73
+ documentationLink: (_a = effect.documentationLink) !== null && _a !== void 0 ? _a : null,
74
+ },
64
75
  children: effectFields.map((f) => {
65
76
  var _a;
66
77
  return ({
@@ -115,6 +126,7 @@ const getExpandedTrackHeight = ({ sequence, nodePathInfo, getIsExpanded, codeVal
115
126
  nodePathInfo,
116
127
  // We assume that no drag overrides can change the timeline layout
117
128
  getDragOverrides: () => ({}),
129
+ getEffectDragOverrides: () => ({}),
118
130
  codeValues,
119
131
  });
120
132
  const flat = (0, exports.flattenVisibleTreeNodes)({ nodes: tree, getIsExpanded });
@@ -127,8 +139,13 @@ const getExpandedTrackHeight = ({ sequence, nodePathInfo, getIsExpanded, codeVal
127
139
  };
128
140
  exports.getExpandedTrackHeight = getExpandedTrackHeight;
129
141
  exports.TIMELINE_LAYER_HEIGHT_VIDEO = 75;
130
- exports.TIMELINE_LAYER_HEIGHT_IMAGE = 50;
131
- exports.TIMELINE_LAYER_HEIGHT_AUDIO = 25;
142
+ exports.TIMELINE_LAYER_HEIGHT_IMAGE = 58;
143
+ exports.TIMELINE_LAYER_HEIGHT_AUDIO = 58;
144
+ exports.TIMELINE_LAYER_HEIGHT_DEFAULT = 25;
145
+ // The horizontal row inside a timeline list item (eye + arrow + label).
146
+ exports.TIMELINE_LIST_ITEM_ROW_HEIGHT = 25;
147
+ // The waveform stripe rendered underneath the filmstrip in TimelineVideoInfo.
148
+ exports.TIMELINE_VIDEO_INFO_WAVEFORM_HEIGHT = 25;
132
149
  const getTimelineLayerHeight = (type) => {
133
150
  if (type === 'video') {
134
151
  return exports.TIMELINE_LAYER_HEIGHT_VIDEO;
@@ -136,6 +153,9 @@ const getTimelineLayerHeight = (type) => {
136
153
  if (type === 'image') {
137
154
  return exports.TIMELINE_LAYER_HEIGHT_IMAGE;
138
155
  }
139
- return exports.TIMELINE_LAYER_HEIGHT_AUDIO;
156
+ if (type === 'audio') {
157
+ return exports.TIMELINE_LAYER_HEIGHT_AUDIO;
158
+ }
159
+ return exports.TIMELINE_LAYER_HEIGHT_DEFAULT;
140
160
  };
141
161
  exports.getTimelineLayerHeight = getTimelineLayerHeight;
@@ -1,11 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useMaxMediaDuration = void 0;
4
- const media_utils_1 = require("@remotion/media-utils");
5
- const mediabunny_1 = require("mediabunny");
6
4
  const react_1 = require("react");
7
- const get_duration_or_compute_1 = require("./get-duration-or-compute");
5
+ const use_media_metadata_1 = require("./use-media-metadata");
8
6
  const cache = new Map();
7
+ const getCacheKey = (src, fps) => JSON.stringify([src, fps]);
9
8
  const getSrc = (s) => {
10
9
  if (s.type === 'video') {
11
10
  return s.src;
@@ -18,40 +17,38 @@ const getSrc = (s) => {
18
17
  const useMaxMediaDuration = (s, fps) => {
19
18
  var _a;
20
19
  const src = getSrc(s);
21
- const [maxMediaDuration, setMaxMediaDuration] = (0, react_1.useState)(src ? ((_a = cache.get(src)) !== null && _a !== void 0 ? _a : null) : Infinity);
20
+ const cacheKey = src ? getCacheKey(src, fps) : null;
21
+ const [maxMediaDuration, setMaxMediaDuration] = (0, react_1.useState)(cacheKey ? ((_a = cache.get(cacheKey)) !== null && _a !== void 0 ? _a : null) : Infinity);
22
22
  (0, react_1.useEffect)(() => {
23
- if (!src) {
23
+ var _a;
24
+ if (!src || !cacheKey) {
24
25
  return;
25
26
  }
26
- const input = new mediabunny_1.Input({
27
- formats: mediabunny_1.ALL_FORMATS,
28
- source: new mediabunny_1.UrlSource(src),
29
- });
30
- (0, get_duration_or_compute_1.getDurationOrCompute)(input)
31
- .then((duration) => {
32
- cache.set(src, Math.floor(duration * fps));
33
- setMaxMediaDuration(Math.floor(duration * fps));
27
+ const cached = (_a = cache.get(cacheKey)) !== null && _a !== void 0 ? _a : null;
28
+ setMaxMediaDuration(cached);
29
+ if (cached !== null) {
30
+ return;
31
+ }
32
+ let cancelled = false;
33
+ (0, use_media_metadata_1.getMediaMetadata)(src)
34
+ .then((metadata) => {
35
+ if (cancelled || !metadata) {
36
+ return;
37
+ }
38
+ const duration = Math.floor(metadata.duration * fps);
39
+ cache.set(cacheKey, duration);
40
+ setMaxMediaDuration(duration);
34
41
  })
35
- .catch((e) => {
36
- if (e instanceof mediabunny_1.InputDisposedError) {
42
+ .catch(() => {
43
+ if (cancelled) {
37
44
  return;
38
45
  }
39
- // In case of CORS errors, fall back to getVideoMetadata
40
- return (0, media_utils_1.getVideoMetadata)(src)
41
- .then((metadata) => {
42
- var _a;
43
- const durationOrInfinity = (_a = metadata.durationInSeconds) !== null && _a !== void 0 ? _a : Infinity;
44
- cache.set(src, Math.floor(durationOrInfinity * fps));
45
- setMaxMediaDuration(Math.floor(durationOrInfinity * fps));
46
- })
47
- .catch(() => {
48
- // Silently handle getVideoMetadata failures to prevent unhandled rejections
49
- });
46
+ setMaxMediaDuration(null);
50
47
  });
51
48
  return () => {
52
- input.dispose();
49
+ cancelled = true;
53
50
  };
54
- }, [src, fps]);
51
+ }, [cacheKey, fps, src]);
55
52
  if (maxMediaDuration !== null && (s.type === 'audio' || s.type === 'video')) {
56
53
  return maxMediaDuration;
57
54
  }
@@ -0,0 +1,10 @@
1
+ export type MediaMetadata = {
2
+ duration: number;
3
+ format: string | null;
4
+ width: number | null;
5
+ height: number | null;
6
+ videoCodec: string | null;
7
+ audioCodec: string | null;
8
+ };
9
+ export declare const getMediaMetadata: (src: string) => Promise<MediaMetadata | null>;
10
+ export declare const useMediaMetadata: (src: string | null) => MediaMetadata | null;
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useMediaMetadata = exports.getMediaMetadata = void 0;
4
+ const media_utils_1 = require("@remotion/media-utils");
5
+ const mediabunny_1 = require("mediabunny");
6
+ const react_1 = require("react");
7
+ const get_duration_or_compute_1 = require("./get-duration-or-compute");
8
+ const cache = new Map();
9
+ const pendingRequests = new Map();
10
+ const safeCall = async (fn) => {
11
+ try {
12
+ return await fn();
13
+ }
14
+ catch (_a) {
15
+ return null;
16
+ }
17
+ };
18
+ const getMediabunnyMetadata = async (src) => {
19
+ var _a;
20
+ let input;
21
+ try {
22
+ input = new mediabunny_1.Input({
23
+ formats: mediabunny_1.ALL_FORMATS,
24
+ source: new mediabunny_1.UrlSource(src),
25
+ });
26
+ }
27
+ catch (_b) {
28
+ return null;
29
+ }
30
+ try {
31
+ const [duration, format, videoTrack, audioTrack] = await Promise.all([
32
+ safeCall(() => (0, get_duration_or_compute_1.getDurationOrCompute)(input)),
33
+ safeCall(() => input.getFormat()),
34
+ safeCall(() => input.getPrimaryVideoTrack()),
35
+ safeCall(() => input.getPrimaryAudioTrack()),
36
+ ]);
37
+ if (duration === null) {
38
+ return null;
39
+ }
40
+ const [width, height, videoCodec, audioCodec] = await Promise.all([
41
+ videoTrack ? safeCall(() => videoTrack.getDisplayWidth()) : null,
42
+ videoTrack ? safeCall(() => videoTrack.getDisplayHeight()) : null,
43
+ videoTrack ? safeCall(() => videoTrack.getCodec()) : null,
44
+ audioTrack ? safeCall(() => audioTrack.getCodec()) : null,
45
+ ]);
46
+ return {
47
+ duration,
48
+ format: (_a = format === null || format === void 0 ? void 0 : format.name) !== null && _a !== void 0 ? _a : null,
49
+ width,
50
+ height,
51
+ videoCodec,
52
+ audioCodec,
53
+ };
54
+ }
55
+ finally {
56
+ try {
57
+ input.dispose();
58
+ }
59
+ catch (_c) {
60
+ // ignore
61
+ }
62
+ }
63
+ };
64
+ const getFallbackVideoMetadata = async (src) => {
65
+ try {
66
+ const metadata = await (0, media_utils_1.getVideoMetadata)(src);
67
+ return {
68
+ duration: metadata.durationInSeconds,
69
+ format: null,
70
+ width: metadata.width,
71
+ height: metadata.height,
72
+ videoCodec: null,
73
+ audioCodec: null,
74
+ };
75
+ }
76
+ catch (_a) {
77
+ return null;
78
+ }
79
+ };
80
+ const getMediaMetadata = (src) => {
81
+ const cached = cache.get(src);
82
+ if (cached) {
83
+ return Promise.resolve(cached);
84
+ }
85
+ const pendingRequest = pendingRequests.get(src);
86
+ if (pendingRequest) {
87
+ return pendingRequest;
88
+ }
89
+ const request = getMediabunnyMetadata(src)
90
+ .catch(() => null)
91
+ .then((metadata) => metadata !== null && metadata !== void 0 ? metadata : getFallbackVideoMetadata(src))
92
+ .then((metadata) => {
93
+ if (metadata) {
94
+ cache.set(src, metadata);
95
+ }
96
+ return metadata;
97
+ })
98
+ .finally(() => {
99
+ pendingRequests.delete(src);
100
+ });
101
+ pendingRequests.set(src, request);
102
+ return request;
103
+ };
104
+ exports.getMediaMetadata = getMediaMetadata;
105
+ const useMediaMetadata = (src) => {
106
+ var _a;
107
+ const [mediaMetadata, setMediaMetadata] = (0, react_1.useState)(src ? ((_a = cache.get(src)) !== null && _a !== void 0 ? _a : null) : null);
108
+ (0, react_1.useEffect)(() => {
109
+ var _a;
110
+ const cached = src ? ((_a = cache.get(src)) !== null && _a !== void 0 ? _a : null) : null;
111
+ setMediaMetadata(cached);
112
+ if (!src || cached) {
113
+ return;
114
+ }
115
+ let cancelled = false;
116
+ (0, exports.getMediaMetadata)(src)
117
+ .then((metadata) => {
118
+ if (cancelled) {
119
+ return;
120
+ }
121
+ setMediaMetadata(metadata);
122
+ })
123
+ .catch(() => {
124
+ if (cancelled) {
125
+ return;
126
+ }
127
+ setMediaMetadata(null);
128
+ });
129
+ return () => {
130
+ cancelled = true;
131
+ };
132
+ }, [src]);
133
+ return mediaMetadata;
134
+ };
135
+ exports.useMediaMetadata = useMediaMetadata;