UNPKG

remotion

Version:

Make videos programmatically

446 lines (445 loc) • 25.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SequenceWithoutFrom = exports.Sequence = exports.SequenceWithoutSchema = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); /* eslint-disable @typescript-eslint/no-use-before-define */ const react_1 = require("react"); const AbsoluteFillElement_js_1 = require("./AbsoluteFillElement.js"); const enable_sequence_stack_traces_js_1 = require("./enable-sequence-stack-traces.js"); const freeze_js_1 = require("./freeze.js"); const interactivity_schema_js_1 = require("./interactivity-schema.js"); const sequence_crop_js_1 = require("./sequence-crop.js"); const sequence_order_marker_js_1 = require("./sequence-order-marker.js"); const SequenceContext_js_1 = require("./SequenceContext.js"); const SequenceManager_js_1 = require("./SequenceManager.js"); const is_inside_series_js_1 = require("./series/is-inside-series.js"); const timeline_position_state_js_1 = require("./timeline-position-state.js"); const use_premounting_js_1 = require("./use-premounting.js"); const use_remotion_environment_js_1 = require("./use-remotion-environment.js"); const use_sequence_registration_js_1 = require("./use-sequence-registration.js"); const use_video_config_js_1 = require("./use-video-config.js"); const v5_flag_js_1 = require("./v5-flag.js"); const with_interactivity_schema_js_1 = require("./with-interactivity-schema.js"); const EMPTY_EFFECTS = []; const RegularSequenceRefForwardingFunction = ({ from = 0, trimBefore = 0, freeze, durationInFrames = Infinity, children, name, height, width, showInTimeline = true, hidden = false, controls, _remotionInternalEffects, _remotionInternalLoopDisplay: loopDisplay, _remotionInternalStack: stack, _remotionInternalDocumentationLink: documentationLink, _remotionInternalSingleChildComponent: singleChildComponent, _remotionInternalPremountDisplay: premountDisplay, _remotionInternalPostmountDisplay: postmountDisplay, _remotionInternalIsMedia: isMedia, outlineRef: passedRefForOutline, cropLeft, cropRight, cropTop, cropBottom, ...other }, ref) => { var _a, _b, _c, _d; const { layout = 'absolute-fill' } = other; const [id] = (0, react_1.useState)(() => String(Math.random())); const parentSequence = (0, react_1.useContext)(SequenceContext_js_1.SequenceContext); const cumulatedFrom = parentSequence ? parentSequence.cumulatedFrom + parentSequence.relativeFrom : 0; if (layout !== 'absolute-fill' && layout !== 'none') { throw new TypeError(`The layout prop of <Sequence /> expects either "absolute-fill" or "none", but you passed: ${layout}`); } const cropProps = { cropLeft, cropRight, cropTop, cropBottom }; const hasCropProp = Object.values(cropProps).some((value) => value !== undefined); if (layout === 'none' && hasCropProp) { throw new TypeError('The cropLeft, cropRight, cropTop and cropBottom props of <Sequence /> are only supported with layout="absolute-fill".'); } (0, sequence_crop_js_1.validateSequenceCrop)(cropProps); const { left: resolvedCropLeft, right: resolvedCropRight, top: resolvedCropTop, bottom: resolvedCropBottom, } = (0, sequence_crop_js_1.resolveSequenceCrop)(cropProps); // @ts-expect-error if (layout === 'none' && typeof other.style !== 'undefined') { throw new TypeError('If layout="none", you may not pass a style. Passed: ' + // @ts-expect-error JSON.stringify(other.style)); } if (typeof durationInFrames !== 'number') { throw new TypeError(`You passed to durationInFrames an argument of type ${typeof durationInFrames}, but it must be a number.`); } if (durationInFrames <= 0) { throw new TypeError(`durationInFrames must be positive, but got ${durationInFrames}`); } if (typeof from !== 'number') { throw new TypeError(`You passed to the "from" props of your <Sequence> an argument of type ${typeof from}, but it must be a number.`); } if (!Number.isFinite(from)) { throw new TypeError(`The "from" prop of a sequence must be finite, but got ${from}.`); } if (typeof trimBefore !== 'number') { throw new TypeError(`You passed to the "trimBefore" prop of your <Sequence> an argument of type ${typeof trimBefore}, but it must be a number.`); } if (trimBefore < 0) { throw new TypeError(`The "trimBefore" prop of <Sequence /> must be greater than or equal to 0, but got ${trimBefore}.`); } if (Number.isNaN(trimBefore)) { throw new TypeError('The "trimBefore" prop of <Sequence /> must be a real number, but it is NaN.'); } if (!Number.isFinite(trimBefore)) { throw new TypeError(`The "trimBefore" prop of <Sequence /> must be finite, but it is ${trimBefore}.`); } if (typeof freeze !== 'undefined' && freeze !== null) { if (typeof freeze !== 'number') { throw new TypeError(`The "freeze" prop of <Sequence /> must be a number, but is of type ${typeof freeze}.`); } if (Number.isNaN(freeze)) { throw new TypeError(`The "freeze" prop of <Sequence /> must be a real number, but it is NaN.`); } if (!Number.isFinite(freeze)) { throw new TypeError(`The "freeze" prop of <Sequence /> must be finite, but it is ${freeze}.`); } } const absoluteFrame = (0, timeline_position_state_js_1.useTimelinePosition)(); const videoConfig = (0, use_video_config_js_1.useVideoConfig)(); const effectiveRelativeFrom = from - trimBefore; const absoluteFrom = ((_a = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.absoluteFrom) !== null && _a !== void 0 ? _a : 0) + effectiveRelativeFrom; const parentSequenceDuration = parentSequence ? Math.min(parentSequence.durationInFrames - effectiveRelativeFrom, durationInFrames) : durationInFrames; const actualDurationInFrames = Math.max(0, Math.min(videoConfig.durationInFrames - from, parentSequenceDuration)); const sequenceRegistrationEnabled = (0, react_1.useContext)(SequenceManager_js_1.SequenceRegistrationContext); const wrapperRefForOutline = (0, react_1.useRef)(null); const refForOutline = other.layout === 'none' ? (passedRefForOutline !== null && passedRefForOutline !== void 0 ? passedRefForOutline : null) : (passedRefForOutline !== null && passedRefForOutline !== void 0 ? passedRefForOutline : wrapperRefForOutline); const premounting = (0, react_1.useMemo)(() => { // || is intentional, ?? would not trigger on `false` return ((parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.premounting) || Boolean(other._remotionInternalIsPremounting)); }, [other._remotionInternalIsPremounting, parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.premounting]); const postmounting = (0, react_1.useMemo)(() => { // || is intentional, ?? would not trigger on `false` return ((parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.postmounting) || Boolean(other._remotionInternalIsPostmounting)); }, [other._remotionInternalIsPostmounting, parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.postmounting]); // `cumulatedNegativeFrom` answers: "How many frames of this media have // already elapsed before the first visible frame of this sequence?" // // This is intentionally based on the effective sequence start, not on adding // all negative `from` values. See the asset-calculation tests for: // - "Should calculate startFrom correctly with negative offset (Html5Audio)" // - "same as above, but with <Sequence from={0}> inbetween" // - "same as above, but a positive child offset cancels part of the negative parent offset" // // In particular, <Sequence from={-20}><Sequence from={10}> should have a // 10-frame pre-roll, because the positive child offset cancels part of the // negative parent offset. But <Sequence from={10}><Sequence from={-5}> // should still trim 5 frames from the media once the parent starts. const currentSequenceStart = cumulatedFrom + effectiveRelativeFrom; const parentSequenceStart = parentSequence ? parentSequence.cumulatedFrom + parentSequence.relativeFrom : 0; const parentFirstFrame = parentSequence ? parentSequenceStart - parentSequence.cumulatedNegativeFrom : 0; const firstFrame = Math.max(0, parentFirstFrame, currentSequenceStart); const cumulatedNegativeFrom = currentSequenceStart - firstFrame; const contextValue = (0, react_1.useMemo)(() => { var _a, _b, _c; return { absoluteFrom, cumulatedFrom, relativeFrom: effectiveRelativeFrom, cumulatedNegativeFrom, durationInFrames: actualDurationInFrames, parentFrom: (_a = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.relativeFrom) !== null && _a !== void 0 ? _a : 0, id, height: (_b = height !== null && height !== void 0 ? height : parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.height) !== null && _b !== void 0 ? _b : null, width: (_c = width !== null && width !== void 0 ? width : parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.width) !== null && _c !== void 0 ? _c : null, premounting, postmounting, premountDisplay: premountDisplay !== null && premountDisplay !== void 0 ? premountDisplay : null, postmountDisplay: postmountDisplay !== null && postmountDisplay !== void 0 ? postmountDisplay : null, }; }, [ cumulatedFrom, absoluteFrom, effectiveRelativeFrom, actualDurationInFrames, parentSequence, id, height, width, premounting, postmounting, premountDisplay, postmountDisplay, cumulatedNegativeFrom, ]); const timelineClipName = (0, react_1.useMemo)(() => { return name !== null && name !== void 0 ? name : ''; }, [name]); const resolvedDocumentationLink = documentationLink !== null && documentationLink !== void 0 ? documentationLink : 'https://www.remotion.dev/docs/sequence'; const env = (0, use_remotion_environment_js_1.useRemotionEnvironment)(); const isInsideSeries = (0, react_1.useContext)(is_inside_series_js_1.IsInsideSeriesContext); // Our assumption: Stack doesnt' change. After we symbolicate we assign it a nodePath // and if it changes, it would lead to-remounting of the sequence. const stackRef = (0, react_1.useRef)(null); stackRef.current = controls ? ((_c = (_b = (0, enable_sequence_stack_traces_js_1.getStackForControls)(controls)) !== null && _b !== void 0 ? _b : stack) !== null && _c !== void 0 ? _c : null) : (stack !== null && stack !== void 0 ? stack : null); const registeredFrozenFrame = typeof freeze === 'number' ? freeze : null; const registeredTrimBefore = trimBefore === 0 ? null : trimBefore; const parentCumulatedNegativeFrom = (_d = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.cumulatedNegativeFrom) !== null && _d !== void 0 ? _d : 0; const startMediaFrom = isMedia && isMedia.type !== 'image' ? isMedia.data.startMediaFrom + parentCumulatedNegativeFrom - cumulatedNegativeFrom : null; const mediaFrameAtSequenceZero = isMedia && isMedia.type !== 'image' ? isMedia.data.startMediaFrom + parentCumulatedNegativeFrom : null; const frozenMediaFrame = isMedia && isMedia.type !== 'image' && mediaFrameAtSequenceZero !== null ? registeredFrozenFrame === null ? null : mediaFrameAtSequenceZero + (loopDisplay ? registeredFrozenFrame % loopDisplay.durationInFrames : registeredFrozenFrame) * isMedia.data.playbackRate : null; const controlsSchema = controls === null || controls === void 0 ? void 0 : controls.schema; const controlsRuntimeValues = controls === null || controls === void 0 ? void 0 : controls.runtimeValues; const controlsOverrideId = controls === null || controls === void 0 ? void 0 : controls.overrideId; const controlsSupportsEffects = controls === null || controls === void 0 ? void 0 : controls.supportsEffects; const controlsComponentIdentity = controls === null || controls === void 0 ? void 0 : controls.componentIdentity; const controlsComponentName = controls === null || controls === void 0 ? void 0 : controls.componentName; const controlsVideoConfigValues = controls === null || controls === void 0 ? void 0 : controls.videoConfigValues; const effectRuntimeValues = (0, react_1.useMemo)(() => { var _a; return (_a = _remotionInternalEffects === null || _remotionInternalEffects === void 0 ? void 0 : _remotionInternalEffects.runtimeValues) !== null && _a !== void 0 ? _a : null; }, [_remotionInternalEffects]); const registrationControls = (0, react_1.useMemo)(() => { if (controlsSchema === undefined || controlsRuntimeValues === undefined || controlsOverrideId === undefined || controlsSupportsEffects === undefined || controlsComponentIdentity === undefined || controlsComponentName === undefined || controlsVideoConfigValues === undefined) { return null; } return { schema: controlsSchema, runtimeValues: controlsRuntimeValues, overrideId: controlsOverrideId, supportsEffects: controlsSupportsEffects, componentIdentity: controlsComponentIdentity, componentName: controlsComponentName, videoConfigValues: controlsVideoConfigValues, }; }, [ controlsComponentIdentity, controlsComponentName, controlsVideoConfigValues, controlsOverrideId, controlsRuntimeValues, controlsSchema, controlsSupportsEffects, ]); const getSequenceForRegistration = (0, react_1.useCallback)(() => { var _a, _b, _c; if (isMedia) { if (isMedia.type === 'image') { return { type: 'image', controls: registrationControls, effects: _remotionInternalEffects !== null && _remotionInternalEffects !== void 0 ? _remotionInternalEffects : EMPTY_EFFECTS, effectRuntimeValues, displayName: timelineClipName, documentationLink: resolvedDocumentationLink, duration: actualDurationInFrames, from, trimBefore: registeredTrimBefore, id, loopDisplay, parent: (_a = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.id) !== null && _a !== void 0 ? _a : null, postmountDisplay: postmountDisplay !== null && postmountDisplay !== void 0 ? postmountDisplay : null, premountDisplay: premountDisplay !== null && premountDisplay !== void 0 ? premountDisplay : null, showInTimeline, timelineOrder: null, src: isMedia.src, getStack: () => stackRef.current, refForOutline: refForOutline !== null && refForOutline !== void 0 ? refForOutline : null, isInsideSeries, frozenFrame: registeredFrozenFrame, singleChildComponent: singleChildComponent !== null && singleChildComponent !== void 0 ? singleChildComponent : null, }; } return { type: isMedia.type, controls: registrationControls, effects: _remotionInternalEffects !== null && _remotionInternalEffects !== void 0 ? _remotionInternalEffects : EMPTY_EFFECTS, effectRuntimeValues, displayName: timelineClipName, documentationLink: resolvedDocumentationLink, doesVolumeChange: isMedia.data.doesVolumeChange, duration: actualDurationInFrames, from, trimBefore: registeredTrimBefore, id, loopDisplay, parent: (_b = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.id) !== null && _b !== void 0 ? _b : null, playbackRate: isMedia.data.playbackRate, postmountDisplay: postmountDisplay !== null && postmountDisplay !== void 0 ? postmountDisplay : null, premountDisplay: premountDisplay !== null && premountDisplay !== void 0 ? premountDisplay : null, showInTimeline, timelineOrder: null, src: isMedia.data.src, getStack: () => stackRef.current, startMediaFrom: startMediaFrom !== null && startMediaFrom !== void 0 ? startMediaFrom : isMedia.data.startMediaFrom, mediaFrameAtSequenceZero, volume: isMedia.data.volumes, muted: isMedia.data.muted, refForOutline: refForOutline !== null && refForOutline !== void 0 ? refForOutline : null, isInsideSeries, frozenFrame: registeredFrozenFrame, frozenMediaFrame, singleChildComponent: singleChildComponent !== null && singleChildComponent !== void 0 ? singleChildComponent : null, }; } return { from, trimBefore: registeredTrimBefore, duration: actualDurationInFrames, id, displayName: timelineClipName, documentationLink: resolvedDocumentationLink, parent: (_c = parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.id) !== null && _c !== void 0 ? _c : null, type: 'sequence', showInTimeline, timelineOrder: null, loopDisplay, getStack: () => stackRef.current, premountDisplay: premountDisplay !== null && premountDisplay !== void 0 ? premountDisplay : null, postmountDisplay: postmountDisplay !== null && postmountDisplay !== void 0 ? postmountDisplay : null, controls: registrationControls, effects: _remotionInternalEffects !== null && _remotionInternalEffects !== void 0 ? _remotionInternalEffects : EMPTY_EFFECTS, effectRuntimeValues, refForOutline: refForOutline !== null && refForOutline !== void 0 ? refForOutline : null, isInsideSeries, frozenFrame: registeredFrozenFrame, singleChildComponent: singleChildComponent !== null && singleChildComponent !== void 0 ? singleChildComponent : null, }; }, [ id, timelineClipName, parentSequence === null || parentSequence === void 0 ? void 0 : parentSequence.id, actualDurationInFrames, from, registeredTrimBefore, showInTimeline, loopDisplay, premountDisplay, postmountDisplay, registrationControls, _remotionInternalEffects, effectRuntimeValues, isMedia, resolvedDocumentationLink, refForOutline, isInsideSeries, registeredFrozenFrame, startMediaFrom, mediaFrameAtSequenceZero, frozenMediaFrame, singleChildComponent, ]); (0, use_sequence_registration_js_1.useSequenceRegistration)({ getSequence: env.isStudio || sequenceRegistrationEnabled ? getSequenceForRegistration : null, id, }); // Ceil to support floats // https://github.com/remotion-dev/remotion/issues/2958 const endThreshold = Math.ceil(cumulatedFrom + from + durationInFrames - 1); const content = absoluteFrame < cumulatedFrom + from ? null : absoluteFrame > endThreshold ? null : children; const frozenContent = content === null || typeof freeze === 'undefined' || freeze === null ? (content) : (jsx_runtime_1.jsx(freeze_js_1.Freeze, { frame: freeze, children: content })); const styleIfThere = other.layout === 'none' ? undefined : other.style; const cropClipPath = (0, sequence_crop_js_1.getSequenceCropClipPath)({ left: resolvedCropLeft, right: resolvedCropRight, top: resolvedCropTop, bottom: resolvedCropBottom, style: styleIfThere, }); const sequenceRef = (0, react_1.useCallback)((node) => { wrapperRefForOutline.current = node; if (typeof ref === 'function') { ref(node); } else if (ref) { ref.current = node; } }, [ref]); const defaultStyle = (0, react_1.useMemo)(() => { return { flexDirection: undefined, ...(width ? { width } : {}), ...(height ? { height } : {}), ...(styleIfThere !== null && styleIfThere !== void 0 ? styleIfThere : {}), ...(cropClipPath ? { clipPath: cropClipPath, } : {}), }; }, [cropClipPath, height, styleIfThere, width]); if (ref !== null && layout === 'none') { throw new TypeError('It is not supported to pass both a `ref` and `layout="none"` to <Sequence />.'); } if (hidden) { return env.isStudio ? (jsx_runtime_1.jsx(sequence_order_marker_js_1.SequenceOrderMarker, { sequenceId: id, children: null })) : null; } const sequence = (jsx_runtime_1.jsx(SequenceContext_js_1.SequenceContext.Provider, { value: contextValue, children: frozenContent === null ? null : other.layout === 'none' ? (frozenContent) : (jsx_runtime_1.jsx(AbsoluteFillElement_js_1.AbsoluteFillElement, { ref: sequenceRef, style: defaultStyle, className: other.className, children: frozenContent })) })); return env.isStudio ? (jsx_runtime_1.jsx(sequence_order_marker_js_1.SequenceOrderMarker, { sequenceId: id, children: sequence })) : (sequence); }; const RegularSequence = (0, react_1.forwardRef)(RegularSequenceRefForwardingFunction); const PremountedPostmountedSequenceRefForwardingFunction = (props, ref) => { if (props.layout === 'none') { throw new Error('`<Sequence>` with `premountFor` and `postmountFor` props does not support layout="none"'); } const { style: passedStyle, from = 0, durationInFrames = Infinity, premountFor = 0, postmountFor = 0, styleWhilePremounted, styleWhilePostmounted, ...otherProps } = props; const { freezeFrame, isPremountingOrPostmounting, postmountingActive, premountingActive, premountingStyle, } = (0, use_premounting_js_1.usePremounting)({ from, durationInFrames, premountFor, postmountFor, style: passedStyle !== null && passedStyle !== void 0 ? passedStyle : null, styleWhilePremounted: styleWhilePremounted !== null && styleWhilePremounted !== void 0 ? styleWhilePremounted : null, styleWhilePostmounted: styleWhilePostmounted !== null && styleWhilePostmounted !== void 0 ? styleWhilePostmounted : null, hideWhilePremounted: 'opacity', }); return (jsx_runtime_1.jsx(freeze_js_1.Freeze, { frame: freezeFrame, active: isPremountingOrPostmounting, children: jsx_runtime_1.jsx(SequenceInner, { ref: ref, from: from, durationInFrames: durationInFrames, style: premountingStyle !== null && premountingStyle !== void 0 ? premountingStyle : undefined, _remotionInternalPremountDisplay: premountFor, _remotionInternalPostmountDisplay: postmountFor, _remotionInternalIsPremounting: premountingActive, _remotionInternalIsPostmounting: postmountingActive, ...otherProps }) })); }; const PremountedPostmountedSequence = (0, react_1.forwardRef)(PremountedPostmountedSequenceRefForwardingFunction); const SequenceRefForwardingFunction = (props, ref) => { var _a; const env = (0, use_remotion_environment_js_1.useRemotionEnvironment)(); const { fps } = (0, use_video_config_js_1.useVideoConfig)(); if (props.layout !== 'none' && !env.isRendering) { const effectivePremountFor = v5_flag_js_1.ENABLE_V5_BREAKING_CHANGES ? ((_a = props.premountFor) !== null && _a !== void 0 ? _a : fps) : props.premountFor; if (effectivePremountFor || props.postmountFor) { return (jsx_runtime_1.jsx(PremountedPostmountedSequence, { ref: ref, ...props, premountFor: effectivePremountFor })); } } return jsx_runtime_1.jsx(RegularSequence, { ...props, ref: ref }); }; const SequenceInner = (0, react_1.forwardRef)(SequenceRefForwardingFunction); exports.SequenceWithoutSchema = SequenceInner; /* * @description A component that time-shifts its children and wraps them in an absolutely positioned <div>. * @see [Documentation](https://www.remotion.dev/docs/sequence) */ exports.Sequence = (0, with_interactivity_schema_js_1.withInteractivitySchema)({ Component: SequenceInner, componentName: '<Sequence>', componentIdentity: 'dev.remotion.remotion.Sequence', schema: interactivity_schema_js_1.sequenceSchema, supportsEffects: false, }); exports.SequenceWithoutFrom = (0, with_interactivity_schema_js_1.withInteractivitySchema)({ Component: SequenceInner, componentName: '<Sequence>', componentIdentity: null, schema: interactivity_schema_js_1.sequenceSchemaWithoutFrom, supportsEffects: false, });