From 79e6186b238e494218522fcebc8769f3d61f0911 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 12 Jun 2024 16:33:48 +0200 Subject: [PATCH 01/36] Start working on captions --- packages/core/src/CompositionManager.tsx | 9 ++++- packages/core/src/index.ts | 1 + packages/core/src/no-react.ts | 6 +++- .../src/assets/calculate-asset-positions.ts | 17 +++++++--- .../src/assets/convert-assets-to-file-urls.ts | 6 ++-- .../assets/download-and-map-assets-to-file.ts | 6 ++-- packages/renderer/src/assets/types.ts | 10 +++--- packages/renderer/src/compress-assets.ts | 8 ++--- packages/renderer/src/filter-asset-types.ts | 17 ++++++++++ packages/renderer/src/render-frames.ts | 33 +++++++++++-------- .../src/test/asset-calculation.test.tsx | 7 +++- .../src/test/asset-compression.test.ts | 7 ++-- .../src/test/dont-skip-assets.test.ts | 7 +++- 13 files changed, 94 insertions(+), 40 deletions(-) create mode 100644 packages/renderer/src/filter-asset-types.ts diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx index bc83c93337a..1381d103b34 100644 --- a/packages/core/src/CompositionManager.tsx +++ b/packages/core/src/CompositionManager.tsx @@ -126,7 +126,7 @@ export type TSequence = { premountDisplay: number | null; } & EnhancedTSequenceData; -export type TRenderAsset = { +export type AudioOrVideoAsset = { type: 'audio' | 'video'; src: string; id: string; @@ -139,6 +139,13 @@ export type TRenderAsset = { audioStartFrame: number; }; +export type ArtifactAsset = { + type: 'artifact'; + id: string; +}; + +export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset; + export const compositionsRef = React.createRef<{ getCompositions: () => AnyComposition[]; }>(); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index a083bc3af17..461e2711673 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -103,6 +103,7 @@ export { export { AnyCompMetadata, AnyComposition, + AudioOrVideoAsset, SmallTCompMetadata, TCompMetadata, TRenderAsset, diff --git a/packages/core/src/no-react.ts b/packages/core/src/no-react.ts index 6a81eed547d..4559b5f4f72 100644 --- a/packages/core/src/no-react.ts +++ b/packages/core/src/no-react.ts @@ -1,4 +1,8 @@ -export type {TRenderAsset} from './CompositionManager'; +export type { + ArtifactAsset, + AudioOrVideoAsset, + TRenderAsset, +} from './CompositionManager'; export type {ClipRegion} from './NativeLayers'; export { EasingFunction, diff --git a/packages/renderer/src/assets/calculate-asset-positions.ts b/packages/renderer/src/assets/calculate-asset-positions.ts index b8f7fa1d3d4..ecadc15a266 100644 --- a/packages/renderer/src/assets/calculate-asset-positions.ts +++ b/packages/renderer/src/assets/calculate-asset-positions.ts @@ -1,4 +1,5 @@ -import type {TRenderAsset} from 'remotion/no-react'; +import type {AudioOrVideoAsset, TRenderAsset} from 'remotion/no-react'; +import {onlyAudioAndVideoAssets} from '../filter-asset-types'; import {resolveAssetSrc} from '../resolve-asset-src'; import {convertAssetToFlattenedVolume} from './flatten-volume-array'; import type {Assets, MediaAsset, UnsafeAsset} from './types'; @@ -18,9 +19,13 @@ const findFrom = (target: TRenderAsset[], renderAsset: TRenderAsset) => { return true; }; -const copyAndDeduplicateAssets = (renderAssets: TRenderAsset[]) => { - const deduplicated: TRenderAsset[] = []; - for (const renderAsset of renderAssets) { +const copyAndDeduplicateAssets = ( + renderAssets: TRenderAsset[], +): AudioOrVideoAsset[] => { + const onlyAudioAndVideo = onlyAudioAndVideoAssets(renderAssets); + const deduplicated: AudioOrVideoAsset[] = []; + + for (const renderAsset of onlyAudioAndVideo) { if (!deduplicated.find((d) => d.id === renderAsset.id)) { deduplicated.push(renderAsset); } @@ -29,7 +34,9 @@ const copyAndDeduplicateAssets = (renderAssets: TRenderAsset[]) => { return deduplicated; }; -export const calculateAssetPositions = (frames: TRenderAsset[][]): Assets => { +export const calculateAssetPositions = ( + frames: AudioOrVideoAsset[][], +): Assets => { const assets: UnsafeAsset[] = []; for (let frame = 0; frame < frames.length; frame++) { diff --git a/packages/renderer/src/assets/convert-assets-to-file-urls.ts b/packages/renderer/src/assets/convert-assets-to-file-urls.ts index 1d75a95bec1..3f90b22ac82 100644 --- a/packages/renderer/src/assets/convert-assets-to-file-urls.ts +++ b/packages/renderer/src/assets/convert-assets-to-file-urls.ts @@ -1,4 +1,4 @@ -import type {TRenderAsset} from 'remotion/no-react'; +import type {AudioOrVideoAsset} from 'remotion/no-react'; import type {LogLevel} from '../log-level'; import type {FrameAndAssets} from '../render-frames'; import type {RenderMediaOnDownload} from './download-and-map-assets-to-file'; @@ -25,9 +25,9 @@ export const convertAssetsToFileUrls = async ({ downloadMap: DownloadMap; indent: boolean; logLevel: LogLevel; -}): Promise<TRenderAsset[][]> => { +}): Promise<AudioOrVideoAsset[][]> => { const chunks = chunk(assets, 1000); - const results: TRenderAsset[][][] = []; + const results: AudioOrVideoAsset[][][] = []; for (const ch of chunks) { const assetPromises = ch.map((frame) => { diff --git a/packages/renderer/src/assets/download-and-map-assets-to-file.ts b/packages/renderer/src/assets/download-and-map-assets-to-file.ts index a34fa32217a..db8c3ebaa11 100644 --- a/packages/renderer/src/assets/download-and-map-assets-to-file.ts +++ b/packages/renderer/src/assets/download-and-map-assets-to-file.ts @@ -1,6 +1,6 @@ import fs from 'node:fs'; import path, {extname} from 'node:path'; -import type {TRenderAsset} from 'remotion/no-react'; +import type {AudioOrVideoAsset} from 'remotion/no-react'; import {random} from 'remotion/no-react'; import {isAssetCompressed} from '../compress-assets'; import {ensureOutputDirectory} from '../ensure-output-directory'; @@ -350,12 +350,12 @@ export const downloadAndMapAssetsToFileUrl = async ({ logLevel, indent, }: { - renderAsset: TRenderAsset; + renderAsset: AudioOrVideoAsset; onDownload: RenderMediaOnDownload | null; downloadMap: DownloadMap; logLevel: LogLevel; indent: boolean; -}): Promise<TRenderAsset> => { +}): Promise<AudioOrVideoAsset> => { const cleanup = attachDownloadListenerToEmitter(downloadMap, onDownload); const newSrc = await downloadAsset({ src: renderAsset.src, diff --git a/packages/renderer/src/assets/types.ts b/packages/renderer/src/assets/types.ts index 22624f43918..f22289f6aa6 100644 --- a/packages/renderer/src/assets/types.ts +++ b/packages/renderer/src/assets/types.ts @@ -1,10 +1,10 @@ -import type {TRenderAsset} from 'remotion/no-react'; +import type {AudioOrVideoAsset} from 'remotion/no-react'; // An unsafe asset is an asset with looser types, which occurs // during construction of the asset list. Prefer the MediaAsset // type instead. export type UnsafeAsset = Omit< - TRenderAsset, + AudioOrVideoAsset, 'frame' | 'id' | 'volume' | 'mediaFrame' | 'audioStartFrom' > & { startInVideo: number; @@ -27,9 +27,9 @@ export type MediaAsset = Omit<UnsafeAsset, 'duration' | 'volume'> & { }; export const uncompressMediaAsset = ( - allRenderAssets: TRenderAsset[], - assetToUncompress: TRenderAsset, -): TRenderAsset => { + allRenderAssets: AudioOrVideoAsset[], + assetToUncompress: AudioOrVideoAsset, +): AudioOrVideoAsset => { const isCompressed = assetToUncompress.src.match(/same-as-(.*)-([0-9]+)$/); if (!isCompressed) { return assetToUncompress; diff --git a/packages/renderer/src/compress-assets.ts b/packages/renderer/src/compress-assets.ts index b0d6c28aaf0..d2ce0fe50d3 100644 --- a/packages/renderer/src/compress-assets.ts +++ b/packages/renderer/src/compress-assets.ts @@ -3,12 +3,12 @@ * Since we track the `src` property for every frame, Node.JS can run out of memory easily. Instead of duplicating the src for every frame, we save memory by replacing the full base 64 encoded data with a string `same-as-[asset-id]-[frame]` referencing a previous asset with the same src. */ -import type {TRenderAsset} from 'remotion/no-react'; +import type {AudioOrVideoAsset} from 'remotion/no-react'; export const compressAsset = ( - previousRenderAssets: TRenderAsset[], - newRenderAsset: TRenderAsset, -): TRenderAsset => { + previousRenderAssets: AudioOrVideoAsset[], + newRenderAsset: AudioOrVideoAsset, +): AudioOrVideoAsset => { if (newRenderAsset.src.length < 400) { return newRenderAsset; } diff --git a/packages/renderer/src/filter-asset-types.ts b/packages/renderer/src/filter-asset-types.ts new file mode 100644 index 00000000000..b7fe68b32e8 --- /dev/null +++ b/packages/renderer/src/filter-asset-types.ts @@ -0,0 +1,17 @@ +import type { + ArtifactAsset, + AudioOrVideoAsset, + TRenderAsset, +} from 'remotion/no-react'; + +export const onlyAudioAndVideoAssets = ( + assets: TRenderAsset[], +): AudioOrVideoAsset[] => { + return assets.filter( + (asset) => asset.type === 'audio' || asset.type === 'video', + ) as AudioOrVideoAsset[]; +}; + +export const onlyArtifact = (assets: TRenderAsset[]): ArtifactAsset[] => { + return assets.filter((asset) => asset.type === 'artifact') as ArtifactAsset[]; +}; diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index b1885eee45b..d29f1f06664 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -2,8 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import {performance} from 'perf_hooks'; // eslint-disable-next-line no-restricted-imports -import type {TAsset} from 'remotion'; -import type {VideoConfig} from 'remotion/no-react'; +import type {AudioOrVideoAsset, VideoConfig} from 'remotion/no-react'; import {NoReactInternals} from 'remotion/no-react'; import type {RenderMediaOnDownload} from './assets/download-and-map-assets-to-file'; import {downloadAndMapAssetsToFileUrl} from './assets/download-and-map-assets-to-file'; @@ -23,6 +22,7 @@ import type {Compositor} from './compositor/compositor'; import {compressAsset} from './compress-assets'; import {cycleBrowserTabs} from './cycle-browser-tabs'; import {handleJavascriptException} from './error-handling/handle-javascript-exception'; +import {onlyArtifact, onlyAudioAndVideoAssets} from './filter-asset-types'; import {findRemotionRoot} from './find-closest-package-json'; import type {FrameRange} from './frame-range'; import {getActualConcurrency} from './get-concurrency'; @@ -144,7 +144,7 @@ type InnerRenderFramesOptions = { export type FrameAndAssets = { frame: number; - assets: TAsset[]; + assets: AudioOrVideoAsset[]; }; export type RenderFramesOptions = { @@ -488,20 +488,24 @@ const innerRenderFrames = async ({ stopPerfMeasure(id); - const compressedAssets = collectedAssets.map((asset) => - compressAsset( - assets - .filter(truthy) - .map((a) => a.assets) - .flat(2), - asset, - ), - ); + const previousRenderAssets = assets + .filter(truthy) + .map((a) => a.assets) + .flat(2); + + const audioAndVideoAssets = onlyAudioAndVideoAssets(collectedAssets); + const artifactAssets = onlyArtifact(collectedAssets); + console.log(artifactAssets); + + const compressedAssets = audioAndVideoAssets.map((asset) => { + return compressAsset(previousRenderAssets, asset); + }); + assets.push({ assets: compressedAssets, frame, }); - compressedAssets.forEach((renderAsset) => { + for (const renderAsset of compressedAssets) { downloadAndMapAssetsToFileUrl({ renderAsset, onDownload, @@ -518,7 +522,8 @@ const innerRenderFrames = async ({ ), ); }); - }); + } + if (!assetsOnly) { framesRendered++; onFrameUpdate?.(framesRendered, frame, performance.now() - startTime); diff --git a/packages/renderer/src/test/asset-calculation.test.tsx b/packages/renderer/src/test/asset-calculation.test.tsx index d1bfc76a42d..1f40f9f33cb 100644 --- a/packages/renderer/src/test/asset-calculation.test.tsx +++ b/packages/renderer/src/test/asset-calculation.test.tsx @@ -7,6 +7,7 @@ import {Audio, interpolate, Sequence, useCurrentFrame, Video} from 'remotion'; import {expect, test} from 'vitest'; import {calculateAssetPositions} from '../assets/calculate-asset-positions'; import type {MediaAsset} from '../assets/types'; +import {onlyAudioAndVideoAssets} from '../filter-asset-types'; import {getAssetsForMarkup} from './get-assets-for-markup'; const basicConfig = { @@ -19,7 +20,11 @@ const basicConfig = { const getPositions = async (Markup: React.FC) => { const assets = await getAssetsForMarkup(Markup, basicConfig); - return calculateAssetPositions(assets); + const onlyAudioAndVideo = assets.map((ass) => { + return onlyAudioAndVideoAssets(ass); + }); + + return calculateAssetPositions(onlyAudioAndVideo); }; const withoutId = (asset: MediaAsset) => { diff --git a/packages/renderer/src/test/asset-compression.test.ts b/packages/renderer/src/test/asset-compression.test.ts index 36ee4d08fc6..0e22d40d577 100644 --- a/packages/renderer/src/test/asset-compression.test.ts +++ b/packages/renderer/src/test/asset-compression.test.ts @@ -2,6 +2,7 @@ import type {TRenderAsset} from 'remotion'; import {expect, test} from 'vitest'; import {calculateAssetPositions} from '../assets/calculate-asset-positions'; import {compressAsset} from '../compress-assets'; +import {onlyAudioAndVideoAssets} from '../filter-asset-types'; test('Should compress and uncompress assets', () => { const uncompressed: TRenderAsset[] = [ @@ -35,8 +36,10 @@ test('Should compress and uncompress assets', () => { ], ].flat(1); - const compressedAssets = uncompressed.map((asset, i) => { - return compressAsset(uncompressed.slice(0, i), asset); + const onlyAudioAndVideo = onlyAudioAndVideoAssets(uncompressed); + + const compressedAssets = onlyAudioAndVideo.map((asset, i) => { + return compressAsset(onlyAudioAndVideo.slice(0, i), asset); }); expect(compressedAssets[0].src).toBe(String('x').repeat(1000)); diff --git a/packages/renderer/src/test/dont-skip-assets.test.ts b/packages/renderer/src/test/dont-skip-assets.test.ts index 84ac0290bab..3f12d1cbae6 100644 --- a/packages/renderer/src/test/dont-skip-assets.test.ts +++ b/packages/renderer/src/test/dont-skip-assets.test.ts @@ -1,6 +1,7 @@ import type {TRenderAsset} from 'remotion'; import {expect, test} from 'vitest'; import {calculateAssetPositions} from '../assets/calculate-asset-positions'; +import {onlyAudioAndVideoAssets} from '../filter-asset-types'; type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T; // from lodash @@ -9,7 +10,11 @@ function truthy<T>(value: T): value is Truthy<T> { } test('Dont skip assets', () => { - const assetPositions = calculateAssetPositions(mock); + const onlyAudioAndVideo = mock.map((m) => { + return onlyAudioAndVideoAssets(m); + }); + + const assetPositions = calculateAssetPositions(onlyAudioAndVideo); expect(assetPositions).toEqual([ { src: 'http://localhost:3000/4793bac32f610ffba8197b8a3422456f.mp3', From e769c8204fb4a3798e167891fd368ce0957ac179 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 12 Jun 2024 16:38:37 +0200 Subject: [PATCH 02/36] hmm --- .../src/assets/convert-assets-to-file-urls.ts | 2 +- packages/renderer/src/render-frames.ts | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/renderer/src/assets/convert-assets-to-file-urls.ts b/packages/renderer/src/assets/convert-assets-to-file-urls.ts index 3f90b22ac82..ef3b6ad1b4a 100644 --- a/packages/renderer/src/assets/convert-assets-to-file-urls.ts +++ b/packages/renderer/src/assets/convert-assets-to-file-urls.ts @@ -31,7 +31,7 @@ export const convertAssetsToFileUrls = async ({ for (const ch of chunks) { const assetPromises = ch.map((frame) => { - const frameAssetPromises = frame.assets.map((a) => { + const frameAssetPromises = frame.audioAndVideoAssets.map((a) => { return downloadAndMapAssetsToFileUrl({ renderAsset: a, onDownload, diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index d29f1f06664..e587592ecb9 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -2,7 +2,11 @@ import fs from 'node:fs'; import path from 'node:path'; import {performance} from 'perf_hooks'; // eslint-disable-next-line no-restricted-imports -import type {AudioOrVideoAsset, VideoConfig} from 'remotion/no-react'; +import type { + ArtifactAsset, + AudioOrVideoAsset, + VideoConfig, +} from 'remotion/no-react'; import {NoReactInternals} from 'remotion/no-react'; import type {RenderMediaOnDownload} from './assets/download-and-map-assets-to-file'; import {downloadAndMapAssetsToFileUrl} from './assets/download-and-map-assets-to-file'; @@ -144,7 +148,8 @@ type InnerRenderFramesOptions = { export type FrameAndAssets = { frame: number; - assets: AudioOrVideoAsset[]; + audioAndVideoAssets: AudioOrVideoAsset[]; + artifactAssets: ArtifactAsset[]; }; export type RenderFramesOptions = { @@ -488,22 +493,22 @@ const innerRenderFrames = async ({ stopPerfMeasure(id); - const previousRenderAssets = assets + const previousAudioRenderAssets = assets .filter(truthy) - .map((a) => a.assets) + .map((a) => a.audioAndVideoAssets) .flat(2); const audioAndVideoAssets = onlyAudioAndVideoAssets(collectedAssets); const artifactAssets = onlyArtifact(collectedAssets); - console.log(artifactAssets); const compressedAssets = audioAndVideoAssets.map((asset) => { - return compressAsset(previousRenderAssets, asset); + return compressAsset(previousAudioRenderAssets, asset); }); assets.push({ - assets: compressedAssets, + audioAndVideoAssets: compressedAssets, frame, + artifactAssets, }); for (const renderAsset of compressedAssets) { downloadAndMapAssetsToFileUrl({ From ef5552d490e65916035e73727d95329139da0855 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 12 Jun 2024 16:47:22 +0200 Subject: [PATCH 03/36] Artifact API --- packages/core/src/Artifact.tsx | 32 ++++++++++++++++++++++++ packages/core/src/CompositionManager.tsx | 2 ++ packages/core/src/index.ts | 2 ++ 3 files changed, 36 insertions(+) create mode 100644 packages/core/src/Artifact.tsx diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx new file mode 100644 index 00000000000..9c38bf60d7f --- /dev/null +++ b/packages/core/src/Artifact.tsx @@ -0,0 +1,32 @@ +import type React from 'react'; +import {useContext, useEffect, useState} from 'react'; +import {RenderAssetManager} from './RenderAssetManager'; + +// TODO: Not register in development? +export const Artifact: React.FC<{ + filename: string; + content: string; +}> = ({filename, content}) => { + // TODO: Validate filename and content + const {registerRenderAsset, unregisterRenderAsset} = + useContext(RenderAssetManager); + + const [id] = useState(() => { + return String(Math.random()); + }); + + useEffect(() => { + registerRenderAsset({ + type: 'artifact', + id, + content, + filename, + }); + + return () => { + return unregisterRenderAsset(id); + }; + }, [content, filename, id, registerRenderAsset, unregisterRenderAsset]); + + return null; +}; diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx index 1381d103b34..b21e617883a 100644 --- a/packages/core/src/CompositionManager.tsx +++ b/packages/core/src/CompositionManager.tsx @@ -142,6 +142,8 @@ export type AudioOrVideoAsset = { export type ArtifactAsset = { type: 'artifact'; id: string; + filename: string; + content: string; }; export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 461e2711673..f52ac894315 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,4 @@ +import {Artifact} from './Artifact.js'; import './asset-types.js'; import {Clipper} from './Clipper.js'; import type {Codec} from './codec.js'; @@ -170,6 +171,7 @@ export const Experimental = { * @see [Documentation](https://www.remotion.dev/docs/null) */ Null, + Artifact, useIsPlayer, }; From b95253e2a5f1e1f7091301a3fa3adeeb8989a62c Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 12 Jun 2024 17:07:45 +0200 Subject: [PATCH 04/36] artifact --- packages/core/src/Artifact.tsx | 16 +++++++++++++--- packages/core/src/CompositionManager.tsx | 1 + packages/example/src/Root.tsx | 19 +++++++++++++++---- .../src/SubtitleArtifact/SubtitleArtifact.tsx | 6 ++++++ packages/renderer/src/render-frames.ts | 18 ++++++++++++++++++ 5 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx index 9c38bf60d7f..b2fd295a435 100644 --- a/packages/core/src/Artifact.tsx +++ b/packages/core/src/Artifact.tsx @@ -1,15 +1,17 @@ import type React from 'react'; import {useContext, useEffect, useState} from 'react'; import {RenderAssetManager} from './RenderAssetManager'; +import {useCurrentFrame} from './use-current-frame'; // TODO: Not register in development? export const Artifact: React.FC<{ - filename: string; - content: string; + readonly filename: string; + readonly content: string; }> = ({filename, content}) => { // TODO: Validate filename and content const {registerRenderAsset, unregisterRenderAsset} = useContext(RenderAssetManager); + const frame = useCurrentFrame(); const [id] = useState(() => { return String(Math.random()); @@ -21,12 +23,20 @@ export const Artifact: React.FC<{ id, content, filename, + frame, }); return () => { return unregisterRenderAsset(id); }; - }, [content, filename, id, registerRenderAsset, unregisterRenderAsset]); + }, [ + content, + filename, + frame, + id, + registerRenderAsset, + unregisterRenderAsset, + ]); return null; }; diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx index b21e617883a..7bed4510d9d 100644 --- a/packages/core/src/CompositionManager.tsx +++ b/packages/core/src/CompositionManager.tsx @@ -144,6 +144,7 @@ export type ArtifactAsset = { id: string; filename: string; content: string; + frame: number; }; export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset; diff --git a/packages/example/src/Root.tsx b/packages/example/src/Root.tsx index 20e943ef7f0..ee5ed32611f 100644 --- a/packages/example/src/Root.tsx +++ b/packages/example/src/Root.tsx @@ -4,9 +4,9 @@ import { CalculateMetadataFunction, Composition, Folder, - Still, getInputProps, staticFile, + Still, } from 'remotion'; import {z} from 'zod'; import {TwentyTwoKHzAudio} from './22KhzAudio'; @@ -32,8 +32,8 @@ import {Layers} from './Layers'; import {ManyAudio} from './ManyAudio'; import {MissingImg} from './MissingImg'; import { - OffthreadRemoteVideo, calculateMetadataFn, + OffthreadRemoteVideo, } from './OffthreadRemoteVideo/OffthreadRemoteVideo'; import {OrbScene} from './Orb'; import {ShapesMorph} from './Paths/ShapesMorph'; @@ -47,8 +47,8 @@ import RiveVehicle from './Rive/RiveExample'; import {ScalePath} from './ScalePath'; import { ArrayTest, - SchemaTest, schemaArrayTestSchema, + SchemaTest, schemaTestSchema, } from './SchemaTest'; import {Scripts} from './Scripts'; @@ -74,6 +74,7 @@ import { } from './StudioApis/SaveDefaultProps'; import {TriggerCalculateMetadata} from './StudioApis/TriggerCalculateMetadata'; import {WriteStaticFile} from './StudioApis/WriteStaticFile'; +import './style.css'; import {Tailwind} from './Tailwind'; import {TenFrameTester} from './TenFrameTester'; import {TextStroke} from './TextStroke'; @@ -90,7 +91,6 @@ import {VideoSpeed} from './VideoSpeed'; import {VideoTesting} from './VideoTesting'; import {WarpDemoOuter} from './WarpText'; import {WarpDemo2} from './WarpText/demo2'; -import './style.css'; import {WatchStaticDemo} from './watch-static'; if (alias !== 'alias') { throw new Error('should support TS aliases'); @@ -98,6 +98,7 @@ if (alias !== 'alias') { // @ts-expect-error no types import styles from './styles.module.scss'; +import {SubtitleArtifact} from './SubtitleArtifact/SubtitleArtifact'; if (!styles.hithere) { throw new Error('should support SCSS modules'); @@ -1309,6 +1310,16 @@ export const Index: React.FC = () => { defaultProps={{color: 'green'}} /> </Folder> + <Folder name="Artifacts"> + <Composition + id="subtitle" + component={SubtitleArtifact} + fps={30} + height={1000} + width={1000} + durationInFrames={10} + /> + </Folder> </> ); }; diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx new file mode 100644 index 00000000000..56e892fdcfb --- /dev/null +++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import {Experimental} from 'remotion'; + +export const SubtitleArtifact: React.FC = () => { + return <Experimental.Artifact content="Hello World!" filename="hello.txt" />; +}; diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index e587592ecb9..34c5746f487 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -498,9 +498,27 @@ const innerRenderFrames = async ({ .map((a) => a.audioAndVideoAssets) .flat(2); + const previousArtifactAssets = assets + .filter(truthy) + .map((a) => a.artifactAssets) + .flat(2); + const audioAndVideoAssets = onlyAudioAndVideoAssets(collectedAssets); const artifactAssets = onlyArtifact(collectedAssets); + for (const artifact of artifactAssets) { + for (const previousArtifact of previousArtifactAssets) { + if (artifact.filename === previousArtifact.filename) { + reject( + new Error( + `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}.`, + ), + ); + return; + } + } + } + const compressedAssets = audioAndVideoAssets.map((asset) => { return compressAsset(previousAudioRenderAssets, asset); }); From 8b0550b1d3d82158f3a9ee9a3bbecc3b3d8e5ca8 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 12 Jun 2024 17:10:13 +0200 Subject: [PATCH 05/36] Update SubtitleArtifact.tsx --- .../example/src/SubtitleArtifact/SubtitleArtifact.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx index 56e892fdcfb..fa36b1dc18a 100644 --- a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx +++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx @@ -1,6 +1,10 @@ import React from 'react'; -import {Experimental} from 'remotion'; +import {Experimental, useCurrentFrame} from 'remotion'; export const SubtitleArtifact: React.FC = () => { - return <Experimental.Artifact content="Hello World!" filename="hello.txt" />; + const frame = useCurrentFrame(); + + return frame === 0 ? ( + <Experimental.Artifact content="Hello World!" filename="hello.txt" /> + ) : null; }; From 367224479d83a5031173bbf51e3da3bbe48df5ab Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 12 Jun 2024 17:55:44 +0200 Subject: [PATCH 06/36] scaffold our artifact --- packages/cli/src/benchmark.ts | 1 + packages/cli/src/render-flows/render.ts | 7 ++++ .../functions/render-media-single-thread.ts | 12 +++++- packages/lambda/src/functions/renderer.ts | 12 +++++- packages/renderer/src/index.ts | 2 +- packages/renderer/src/render-frames.ts | 39 ++++++++++++++----- packages/renderer/src/render-media.ts | 7 ++++ 7 files changed, 68 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/benchmark.ts b/packages/cli/src/benchmark.ts index 83967681f41..2db1ea5cca4 100644 --- a/packages/cli/src/benchmark.ts +++ b/packages/cli/src/benchmark.ts @@ -489,6 +489,7 @@ export const benchmarkCommand = async ( }).value, compositionStart: 0, onBrowserDownload, + onArtifact: () => undefined, }, (run, progress) => { benchmarkProgress.update( diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index f3493511577..2e61f875f1d 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -11,6 +11,7 @@ import type { FrameRange, LogLevel, NumberOfGifLoops, + OnArtifact, PixelFormat, ProResProfile, RenderMediaOnDownload, @@ -402,6 +403,10 @@ export const renderVideoFlow = async ({ logLevel, ); + const onArtifact: OnArtifact = (artifact) => { + console.log('artifact', artifact); + }; + const absoluteSeparateAudioTo = separateAudioTo === null ? null : path.resolve(separateAudioTo); const exists = existsSync(absoluteOutputFile); @@ -491,6 +496,7 @@ export const renderVideoFlow = async ({ compositionStart: 0, forSeamlessAacConcatenation, onBrowserDownload, + onArtifact, }); Log.info({indent, logLevel}, chalk.blue(`▶ ${absoluteOutputFile}`)); @@ -580,6 +586,7 @@ export const renderVideoFlow = async ({ forSeamlessAacConcatenation, compositionStart: 0, onBrowserDownload, + onArtifact, }); if (!updatesDontOverwrite) { updateRenderProgress({newline: true, printToConsole: true}); diff --git a/packages/cloudrun/src/functions/render-media-single-thread.ts b/packages/cloudrun/src/functions/render-media-single-thread.ts index cd6b677f066..fcdc80d1531 100644 --- a/packages/cloudrun/src/functions/render-media-single-thread.ts +++ b/packages/cloudrun/src/functions/render-media-single-thread.ts @@ -1,6 +1,10 @@ import type * as ff from '@google-cloud/functions-framework'; import {Storage} from '@google-cloud/storage'; -import type {ChromiumOptions, RenderMediaOnProgress} from '@remotion/renderer'; +import type { + ChromiumOptions, + OnArtifact, + RenderMediaOnProgress, +} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {NoReactInternals} from 'remotion/no-react'; import {VERSION} from 'remotion/version'; @@ -59,6 +63,11 @@ export const renderMediaSingleThread = async ( enableMultiProcessOnLinux: true, }; + const onArtifact: OnArtifact = (artifact) => { + // TODO: Do something with the artifact + console.log(artifact); + }; + await RenderInternals.internalRenderMedia({ composition: { ...composition, @@ -126,6 +135,7 @@ export const renderMediaSingleThread = async ( onBrowserDownload: () => { throw new Error('Should not download a browser in Cloud Run'); }, + onArtifact, }); const storage = new Storage(); diff --git a/packages/lambda/src/functions/renderer.ts b/packages/lambda/src/functions/renderer.ts index 8e15291dfa3..ac45bec1aa2 100644 --- a/packages/lambda/src/functions/renderer.ts +++ b/packages/lambda/src/functions/renderer.ts @@ -1,4 +1,9 @@ -import type {AudioCodec, BrowserLog, Codec} from '@remotion/renderer'; +import type { + AudioCodec, + BrowserLog, + Codec, + OnArtifact, +} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import fs from 'node:fs'; import path from 'node:path'; @@ -167,6 +172,10 @@ const renderHandler = async ({ params.everyNthFrame, ); + const onArtifact: OnArtifact = (artifact) => { + console.log(artifact); + }; + await new Promise<void>((resolve, reject) => { RenderInternals.internalRenderMedia({ repro: false, @@ -271,6 +280,7 @@ const renderHandler = async ({ onBrowserDownload: () => { throw new Error('Should not download a browser in Lambda'); }, + onArtifact, }) .then(({slowestFrames}) => { RenderInternals.Log.verbose( diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts index d30ac4b6e7b..ac072fa0339 100644 --- a/packages/renderer/src/index.ts +++ b/packages/renderer/src/index.ts @@ -108,7 +108,7 @@ export {X264Preset} from './options/x264-preset'; export {PixelFormat} from './pixel-format'; export {RemotionServer} from './prepare-server'; export {ProResProfile} from './prores-profile'; -export {RenderFramesOptions, renderFrames} from './render-frames'; +export {OnArtifact, RenderFramesOptions, renderFrames} from './render-frames'; export { InternalRenderMediaOptions, RenderMediaOnProgress, diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index 34c5746f487..8ac752ef349 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -2,11 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import {performance} from 'perf_hooks'; // eslint-disable-next-line no-restricted-imports -import type { - ArtifactAsset, - AudioOrVideoAsset, - VideoConfig, -} from 'remotion/no-react'; +import type {AudioOrVideoAsset, VideoConfig} from 'remotion/no-react'; import {NoReactInternals} from 'remotion/no-react'; import type {RenderMediaOnDownload} from './assets/download-and-map-assets-to-file'; import {downloadAndMapAssetsToFileUrl} from './assets/download-and-map-assets-to-file'; @@ -71,7 +67,9 @@ import {wrapWithErrorHandling} from './wrap-with-error-handling'; const MAX_RETRIES_PER_FRAME = 1; -export type InternalRenderFramesOptions = { +export type OnArtifact = (asset: EmittedAsset) => void; + +type InternalRenderFramesOptions = { onStart: null | ((data: OnStartData) => void); onFrameUpdate: | null @@ -105,8 +103,15 @@ export type InternalRenderFramesOptions = { serializedResolvedPropsWithCustomSchema: string; parallelEncodingEnabled: boolean; compositionStart: number; + onArtifact: OnArtifact | null; } & ToOptions<typeof optionsMap.renderFrames>; +type EmittedAsset = { + filename: string; + content: string | Buffer; + frame: number; +}; + type InnerRenderFramesOptions = { onStart: null | ((data: OnStartData) => void); onFrameUpdate: @@ -123,6 +128,7 @@ type InnerRenderFramesOptions = { everyNthFrame: number; onBrowserLog: null | ((log: BrowserLog) => void); onFrameBuffer: null | ((buffer: Buffer, frame: number) => void); + onArtifact: OnArtifact | null; onDownload: RenderMediaOnDownload | null; timeoutInMilliseconds: number; scale: number; @@ -146,10 +152,15 @@ type InnerRenderFramesOptions = { compositionStart: number; } & ToOptions<typeof optionsMap.renderFrames>; +type ArtifactWithoutContent = { + frame: number; + filename: string; +}; + export type FrameAndAssets = { frame: number; audioAndVideoAssets: AudioOrVideoAsset[]; - artifactAssets: ArtifactAsset[]; + artifactAssets: ArtifactWithoutContent[]; }; export type RenderFramesOptions = { @@ -190,6 +201,7 @@ export type RenderFramesOptions = { composition: VideoConfig; muted?: boolean; concurrency?: number | string | null; + onArtifact?: OnArtifact | null; serveUrl: string; } & Partial<ToOptions<typeof optionsMap.renderFrames>>; @@ -511,7 +523,7 @@ const innerRenderFrames = async ({ if (artifact.filename === previousArtifact.filename) { reject( new Error( - `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}.`, + `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`, ), ); return; @@ -526,7 +538,12 @@ const innerRenderFrames = async ({ assets.push({ audioAndVideoAssets: compressedAssets, frame, - artifactAssets, + artifactAssets: artifactAssets.map((a) => { + return { + frame: a.frame, + filename: a.filename, + }; + }), }); for (const renderAsset of compressedAssets) { downloadAndMapAssetsToFileUrl({ @@ -784,6 +801,7 @@ const internalRenderFramesRaw = ({ forSeamlessAacConcatenation, compositionStart, onBrowserDownload, + onArtifact, }: InternalRenderFramesOptions): Promise<RenderFramesOutput> => { validateDimension( composition.height, @@ -913,6 +931,7 @@ const internalRenderFramesRaw = ({ forSeamlessAacConcatenation, compositionStart, onBrowserDownload, + onArtifact, }); }), ]) @@ -1007,6 +1026,7 @@ export const renderFrames = ( offthreadVideoCacheSizeInBytes, binariesDirectory, onBrowserDownload, + onArtifact, } = options; if (!composition) { @@ -1078,5 +1098,6 @@ export const renderFrames = ( onBrowserDownload: onBrowserDownload ?? defaultBrowserDownloadProgress({indent, logLevel, api: 'renderFrames()'}), + onArtifact: onArtifact ?? null, }); }; diff --git a/packages/renderer/src/render-media.ts b/packages/renderer/src/render-media.ts index fce7039b5a7..191035cdc7f 100644 --- a/packages/renderer/src/render-media.ts +++ b/packages/renderer/src/render-media.ts @@ -53,6 +53,7 @@ import {prespawnFfmpeg} from './prespawn-ffmpeg'; import {shouldUseParallelEncoding} from './prestitcher-memory-usage'; import type {ProResProfile} from './prores-profile'; import {validateSelectedCodecAndProResCombination} from './prores-profile'; +import type {OnArtifact} from './render-frames'; import {internalRenderFrames} from './render-frames'; import { disableRepro, @@ -125,6 +126,7 @@ export type InternalRenderMediaOptions = { concurrency: number | string | null; binariesDirectory: string | null; compositionStart: number; + onArtifact: OnArtifact | null; } & MoreRenderMediaOptions; type Prettify<T> = { @@ -179,6 +181,7 @@ export type RenderMediaOptions = Prettify<{ colorSpace?: ColorSpace; repro?: boolean; binariesDirectory?: string | null; + onArtifact?: OnArtifact; }> & Partial<MoreRenderMediaOptions>; @@ -241,6 +244,7 @@ const internalRenderMediaRaw = ({ forSeamlessAacConcatenation, compositionStart, onBrowserDownload, + onArtifact, }: InternalRenderMediaOptions): Promise<RenderMediaResult> => { if (repro) { enableRepro({ @@ -649,6 +653,7 @@ const internalRenderMediaRaw = ({ compositionStart, forSeamlessAacConcatenation, onBrowserDownload, + onArtifact, }); return renderFramesProc; @@ -863,6 +868,7 @@ export const renderMedia = ({ separateAudioTo, forSeamlessAacConcatenation, onBrowserDownload, + onArtifact, }: RenderMediaOptions): Promise<RenderMediaResult> => { if (quality !== undefined) { console.warn( @@ -937,6 +943,7 @@ export const renderMedia = ({ onBrowserDownload: onBrowserDownload ?? defaultBrowserDownloadProgress({indent, logLevel, api: 'renderMedia()'}), + onArtifact: onArtifact ?? null, // TODO: In the future, introduce this as a public API when launching the distributed rendering API compositionStart: 0, }); From d654062a8d6d3b1184b7b9a6b6d5e79c8c9c6ac2 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 12 Jun 2024 17:58:45 +0200 Subject: [PATCH 07/36] do something with the artifact --- packages/cli/src/render-flows/render.ts | 4 ++-- packages/renderer/src/render-frames.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index 2e61f875f1d..6fbb8faa795 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -29,7 +29,7 @@ import type { RenderingProgressInput, StitchingProgressInput, } from '@remotion/studio-server'; -import fs, {existsSync} from 'node:fs'; +import fs, {existsSync, writeFileSync} from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import {NoReactInternals} from 'remotion/no-react'; @@ -404,7 +404,7 @@ export const renderVideoFlow = async ({ ); const onArtifact: OnArtifact = (artifact) => { - console.log('artifact', artifact); + writeFileSync(artifact.filename, artifact.content); }; const absoluteSeparateAudioTo = diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index 8ac752ef349..15ef6b4ea3a 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -239,6 +239,7 @@ const innerRenderFrames = async ({ parallelEncodingEnabled, compositionStart, forSeamlessAacConcatenation, + onArtifact, }: Omit< InnerRenderFramesOptions, 'offthreadVideoCacheSizeInBytes' @@ -529,6 +530,8 @@ const innerRenderFrames = async ({ return; } } + + onArtifact?.(artifact); } const compressedAssets = audioAndVideoAssets.map((asset) => { From c2bb5e99535a2f999e397601aaf7ec7b0d4b8346 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 13 Jun 2024 10:50:04 +0200 Subject: [PATCH 08/36] general CLI handler --- packages/cli/src/on-artifact.ts | 6 ++++++ packages/cli/src/render-flows/render.ts | 12 ++++-------- packages/core/src/index.ts | 3 +-- .../src/SubtitleArtifact/SubtitleArtifact.tsx | 4 ++-- packages/renderer/src/render-frames.ts | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 packages/cli/src/on-artifact.ts diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts new file mode 100644 index 00000000000..869d7d40f35 --- /dev/null +++ b/packages/cli/src/on-artifact.ts @@ -0,0 +1,6 @@ +import type {OnArtifact} from '@remotion/renderer'; +import {writeFileSync} from 'fs'; + +export const onArtifactOnCli: OnArtifact = (artifact) => { + writeFileSync(artifact.filename, artifact.content); +}; diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index 6fbb8faa795..bbb22fbe550 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -11,7 +11,6 @@ import type { FrameRange, LogLevel, NumberOfGifLoops, - OnArtifact, PixelFormat, ProResProfile, RenderMediaOnDownload, @@ -29,7 +28,7 @@ import type { RenderingProgressInput, StitchingProgressInput, } from '@remotion/studio-server'; -import fs, {existsSync, writeFileSync} from 'node:fs'; +import fs, {existsSync} from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import {NoReactInternals} from 'remotion/no-react'; @@ -43,6 +42,7 @@ import {makeHyperlink} from '../hyperlinks/make-link'; import {getVideoImageFormat} from '../image-formats'; import {Log} from '../log'; import {makeOnDownload} from '../make-on-download'; +import {onArtifactOnCli} from '../on-artifact'; import {parsedCli, quietFlagProvided} from '../parsed-cli'; import { LABEL_WIDTH, @@ -403,10 +403,6 @@ export const renderVideoFlow = async ({ logLevel, ); - const onArtifact: OnArtifact = (artifact) => { - writeFileSync(artifact.filename, artifact.content); - }; - const absoluteSeparateAudioTo = separateAudioTo === null ? null : path.resolve(separateAudioTo); const exists = existsSync(absoluteOutputFile); @@ -496,7 +492,7 @@ export const renderVideoFlow = async ({ compositionStart: 0, forSeamlessAacConcatenation, onBrowserDownload, - onArtifact, + onArtifact: onArtifactOnCli, }); Log.info({indent, logLevel}, chalk.blue(`▶ ${absoluteOutputFile}`)); @@ -586,7 +582,7 @@ export const renderVideoFlow = async ({ forSeamlessAacConcatenation, compositionStart: 0, onBrowserDownload, - onArtifact, + onArtifact: onArtifactOnCli, }); if (!updatesDontOverwrite) { updateRenderProgress({newline: true, printToConsole: true}); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f52ac894315..88ed8ff801b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,3 @@ -import {Artifact} from './Artifact.js'; import './asset-types.js'; import {Clipper} from './Clipper.js'; import type {Codec} from './codec.js'; @@ -92,6 +91,7 @@ export type BundleState = checkMultipleRemotionVersions(); export * from './AbsoluteFill.js'; +export {Artifact} from './Artifact.js'; export * from './audio/index.js'; export {cancelRender} from './cancel-render.js'; export { @@ -171,7 +171,6 @@ export const Experimental = { * @see [Documentation](https://www.remotion.dev/docs/null) */ Null, - Artifact, useIsPlayer, }; diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx index fa36b1dc18a..14ca160c160 100644 --- a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx +++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import {Experimental, useCurrentFrame} from 'remotion'; +import {Artifact, useCurrentFrame} from 'remotion'; export const SubtitleArtifact: React.FC = () => { const frame = useCurrentFrame(); return frame === 0 ? ( - <Experimental.Artifact content="Hello World!" filename="hello.txt" /> + <Artifact content="Hello World!" filename="hello.txt" /> ) : null; }; diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index 15ef6b4ea3a..6e753eddffc 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -108,7 +108,7 @@ type InternalRenderFramesOptions = { type EmittedAsset = { filename: string; - content: string | Buffer; + content: string | Uint8Array; frame: number; }; From fcbcac71bd289c77474aceacfa011d1d4c4b56b9 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 13 Jun 2024 12:59:47 +0200 Subject: [PATCH 09/36] artifact state --- packages/cli/src/on-artifact.ts | 30 ++++++++++++++++++++++-- packages/cli/src/progress-types.ts | 3 +++ packages/cli/src/render-flows/render.ts | 15 +++++++++--- packages/studio-shared/src/index.ts | 2 ++ packages/studio-shared/src/render-job.ts | 11 +++++++++ 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts index 869d7d40f35..715d025cd55 100644 --- a/packages/cli/src/on-artifact.ts +++ b/packages/cli/src/on-artifact.ts @@ -1,6 +1,32 @@ import type {OnArtifact} from '@remotion/renderer'; +import type {ArtifactProgress} from '@remotion/studio-shared'; import {writeFileSync} from 'fs'; +import path from 'path'; -export const onArtifactOnCli: OnArtifact = (artifact) => { - writeFileSync(artifact.filename, artifact.content); +export const handleOnArtifact = ( + onProgress: (artifact: ArtifactProgress) => void, +) => { + const progress: ArtifactProgress = {received: []}; + + const onArtifact: OnArtifact = (artifact) => { + // TODO: Normalize backslashes on Windows + const absoluteOutputDestination = path.join( + process.cwd(), + artifact.filename, + ); + writeFileSync(artifact.filename, artifact.content); + + progress.received.push({ + absoluteOutputDestination, + filename: artifact.filename, + sizeInBytes: artifact.content.length, + }); + }; + + onProgress(progress); + + return { + onArtifact, + initialProgress: progress, + }; }; diff --git a/packages/cli/src/progress-types.ts b/packages/cli/src/progress-types.ts index 58fe3bb4a5d..497f4d07662 100644 --- a/packages/cli/src/progress-types.ts +++ b/packages/cli/src/progress-types.ts @@ -12,4 +12,7 @@ export const initialAggregateRenderProgress = (): AggregateRenderProgress => ({ bytes: 0, doneIn: null, }, + artifactState: { + received: [], + }, }); diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index bbb22fbe550..82c82600850 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -42,7 +42,7 @@ import {makeHyperlink} from '../hyperlinks/make-link'; import {getVideoImageFormat} from '../image-formats'; import {Log} from '../log'; import {makeOnDownload} from '../make-on-download'; -import {onArtifactOnCli} from '../on-artifact'; +import {handleOnArtifact} from '../on-artifact'; import {parsedCli, quietFlagProvided} from '../parsed-cli'; import { LABEL_WIDTH, @@ -220,6 +220,13 @@ export const renderVideoFlow = async ({ doneIn: null, }; + let {onArtifact, initialProgress: artifactState} = handleOnArtifact( + (progress) => { + artifactState = progress; + updateRenderProgress({newline: false, printToConsole: true}); + }, + ); + const updateRenderProgress = ({ newline, printToConsole, @@ -233,6 +240,7 @@ export const renderVideoFlow = async ({ downloads, bundling: bundlingProgress, copyingState, + artifactState, }; const {output, message, progress} = makeRenderingAndStitchingProgress({ @@ -430,6 +438,7 @@ export const renderVideoFlow = async ({ codec: shouldOutputImageSequence ? undefined : codec, uiImageFormat, }); + if (shouldOutputImageSequence) { fs.mkdirSync(absoluteOutputFile, { recursive: true, @@ -492,7 +501,7 @@ export const renderVideoFlow = async ({ compositionStart: 0, forSeamlessAacConcatenation, onBrowserDownload, - onArtifact: onArtifactOnCli, + onArtifact, }); Log.info({indent, logLevel}, chalk.blue(`▶ ${absoluteOutputFile}`)); @@ -582,7 +591,7 @@ export const renderVideoFlow = async ({ forSeamlessAacConcatenation, compositionStart: 0, onBrowserDownload, - onArtifact: onArtifactOnCli, + onArtifact, }); if (!updatesDontOverwrite) { updateRenderProgress({newline: true, printToConsole: true}); diff --git a/packages/studio-shared/src/index.ts b/packages/studio-shared/src/index.ts index 11bf724e0d7..7e66eac1070 100644 --- a/packages/studio-shared/src/index.ts +++ b/packages/studio-shared/src/index.ts @@ -49,10 +49,12 @@ export {ProjectInfo} from './project-info'; export type {RenderDefaults} from './render-defaults'; export { AggregateRenderProgress, + ArtifactProgress, BundlingState, CopyingState, DownloadProgress, JobProgressCallback, + ReceivedAsset, RenderJob, RenderJobWithCleanup, RenderingProgressInput, diff --git a/packages/studio-shared/src/render-job.ts b/packages/studio-shared/src/render-job.ts index 6ff3b0c1f8b..86eefd9e7e2 100644 --- a/packages/studio-shared/src/render-job.ts +++ b/packages/studio-shared/src/render-job.ts @@ -56,6 +56,17 @@ export type AggregateRenderProgress = { downloads: DownloadProgress[]; bundling: BundlingState; copyingState: CopyingState; + artifactState: ArtifactProgress; +}; + +export type ReceivedAsset = { + filename: string; + absoluteOutputDestination: string; + sizeInBytes: number; +}; + +export type ArtifactProgress = { + received: ReceivedAsset[]; }; export type JobProgressCallback = ( From dd1710fede5b099d0442d4cb6a6223f04e79bf4f Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Fri, 14 Jun 2024 17:25:46 +0200 Subject: [PATCH 10/36] add some tests to it --- packages/core/src/Artifact.tsx | 1 + packages/core/src/RenderAssetManager.tsx | 2 + packages/core/src/internals.ts | 2 + .../core/src/validation/validate-artifact.ts | 41 +++++++++ packages/renderer/src/test/artifacts.test.tsx | 88 +++++++++++++++++++ .../src/test/get-assets-for-markup.tsx | 1 + 6 files changed, 135 insertions(+) create mode 100644 packages/core/src/validation/validate-artifact.ts create mode 100644 packages/renderer/src/test/artifacts.test.tsx diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx index b2fd295a435..489e47685d4 100644 --- a/packages/core/src/Artifact.tsx +++ b/packages/core/src/Artifact.tsx @@ -11,6 +11,7 @@ export const Artifact: React.FC<{ // TODO: Validate filename and content const {registerRenderAsset, unregisterRenderAsset} = useContext(RenderAssetManager); + const frame = useCurrentFrame(); const [id] = useState(() => { diff --git a/packages/core/src/RenderAssetManager.tsx b/packages/core/src/RenderAssetManager.tsx index 43588b15e6a..87b41d67dad 100644 --- a/packages/core/src/RenderAssetManager.tsx +++ b/packages/core/src/RenderAssetManager.tsx @@ -6,6 +6,7 @@ import { useState, } from 'react'; import type {TRenderAsset} from './CompositionManager.js'; +import {validateRenderAsset} from './validation/validate-artifact.js'; export type RenderAssetManagerContext = { registerRenderAsset: (renderAsset: TRenderAsset) => void; @@ -26,6 +27,7 @@ export const RenderAssetManagerProvider: React.FC<{ const [renderAssets, setRenderAssets] = useState<TRenderAsset[]>([]); const registerRenderAsset = useCallback((renderAsset: TRenderAsset) => { + validateRenderAsset(renderAsset); setRenderAssets((assets) => { return [...assets, renderAsset]; }); diff --git a/packages/core/src/internals.ts b/packages/core/src/internals.ts index 656c475d3f8..3a9342b779d 100644 --- a/packages/core/src/internals.ts +++ b/packages/core/src/internals.ts @@ -75,6 +75,7 @@ import { import {useLazyComponent} from './use-lazy-component.js'; import {useUnsafeVideoConfig} from './use-unsafe-video-config.js'; import {useVideo} from './use-video.js'; +import {validateRenderAsset} from './validation/validate-artifact.js'; import { invalidCompositionErrorMessage, isCompositionIdValid, @@ -161,6 +162,7 @@ export const Internals = { calculateScale, editorPropsProviderRef, PROPS_UPDATED_EXTERNALLY, + validateRenderAsset, } as const; export type { diff --git a/packages/core/src/validation/validate-artifact.ts b/packages/core/src/validation/validate-artifact.ts new file mode 100644 index 00000000000..162e177b093 --- /dev/null +++ b/packages/core/src/validation/validate-artifact.ts @@ -0,0 +1,41 @@ +import type {TRenderAsset} from '../CompositionManager'; + +export const validateArtifactFilename = (filename: unknown) => { + if (typeof filename !== 'string') { + throw new TypeError( + `The "filename" must be a string, but you passed a value of type ${typeof filename}`, + ); + } + + if (filename.trim() === '') { + throw new Error('The `filename` must not be empty'); + } + + if (!filename.match(/^([0-9a-zA-Z-!_.*'()/:&$@=;+,?]+)/g)) { + throw new Error( + 'The `filename` must match "/^([0-9a-zA-Z-!_.*\'()/:&$@=;+,?]+)/g". Use forward slashes only, even on Windows.', + ); + } +}; + +const validateContent = (content: unknown) => { + if (typeof content !== 'string') { + throw new TypeError( + `The "content" must be a string, but you passed a value of type ${typeof content}`, + ); + } + + if (content.trim() === '') { + throw new Error('The `content` must not be empty'); + } +}; + +export const validateRenderAsset = (artifact: TRenderAsset) => { + // We don't have validation for it yet + if (artifact.type !== 'artifact') { + return; + } + + validateArtifactFilename(artifact.filename); + validateContent(artifact.content); +}; diff --git a/packages/renderer/src/test/artifacts.test.tsx b/packages/renderer/src/test/artifacts.test.tsx new file mode 100644 index 00000000000..879787c735c --- /dev/null +++ b/packages/renderer/src/test/artifacts.test.tsx @@ -0,0 +1,88 @@ +/* eslint-disable no-restricted-imports */ +/** + * @vitest-environment jsdom + */ +import React from 'react'; +import {Artifact, useCurrentFrame} from 'remotion'; +import {expect, test} from 'vitest'; +import {getAssetsForMarkup} from './get-assets-for-markup'; + +const basicConfig = { + width: 1080, + height: 1080, + fps: 30, + durationInFrames: 60, + id: 'hithere', +}; + +test('Should be able to collect artifacts', async () => { + const Markup: React.FC = () => { + const frame = useCurrentFrame(); + + return ( + <div> + {frame === 1 ? ( + <Artifact filename="hi.txt" content="hi there"></Artifact> + ) : null} + </div> + ); + }; + + const [first, second] = await getAssetsForMarkup(Markup, basicConfig); + expect(first).toEqual([]); + expect(second.length).toEqual(1); + const sec = second[0]; + if (sec.type !== 'artifact') { + throw new Error('Expected artifact'); + } + + expect(sec.filename).toEqual('hi.txt'); + expect(sec.content).toEqual('hi there'); +}); + +test('Should throw on invalid artifact', async () => { + const Markup: React.FC = () => { + const frame = useCurrentFrame(); + + return ( + <div> + {frame === 1 ? ( + <Artifact + filename="backslash\\hithere.com" + content="hi there" + ></Artifact> + ) : null} + </div> + ); + }; + + try { + await getAssetsForMarkup(Markup, basicConfig); + } catch (err) { + expect(err).toMatch(/The "filename" must match/); + } +}); + +test('Should throw on when missing content', async () => { + const Markup: React.FC = () => { + const frame = useCurrentFrame(); + + return ( + <div> + {frame === 1 ? ( + <Artifact + filename="backslash\\hithere.com" + // @ts-expect-error + content={undefined} + ></Artifact> + ) : null} + </div> + ); + }; + + try { + await getAssetsForMarkup(Markup, basicConfig); + } catch (err) { + expect(err).toMatch(/The "content" must be a string/); + } +}); diff --git a/packages/renderer/src/test/get-assets-for-markup.tsx b/packages/renderer/src/test/get-assets-for-markup.tsx index 93000ae7195..6b935fbecd5 100644 --- a/packages/renderer/src/test/get-assets-for-markup.tsx +++ b/packages/renderer/src/test/get-assets-for-markup.tsx @@ -52,6 +52,7 @@ export const getAssetsForMarkup = async ( const [renderAssets, setAssets] = useState<TRenderAsset[]>([]); const registerRenderAsset = useCallback((renderAsset: TRenderAsset) => { + Internals.validateRenderAsset(renderAsset); setAssets((assts) => { return [...assts, renderAsset]; }); From 2fc2eb9ab431e6d1905e3000ef19133088faa161 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Fri, 14 Jun 2024 17:42:41 +0200 Subject: [PATCH 11/36] cool artifacts --- packages/cli/src/on-artifact.ts | 8 +++---- packages/cli/src/progress-bar.ts | 28 ++++++++++++++++++++++++- packages/cli/src/render-flows/render.ts | 13 ++++++------ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts index 715d025cd55..c93f287faa2 100644 --- a/packages/cli/src/on-artifact.ts +++ b/packages/cli/src/on-artifact.ts @@ -4,9 +4,10 @@ import {writeFileSync} from 'fs'; import path from 'path'; export const handleOnArtifact = ( + artifactState: ArtifactProgress, onProgress: (artifact: ArtifactProgress) => void, ) => { - const progress: ArtifactProgress = {received: []}; + const initialProgress = {...artifactState}; const onArtifact: OnArtifact = (artifact) => { // TODO: Normalize backslashes on Windows @@ -16,17 +17,16 @@ export const handleOnArtifact = ( ); writeFileSync(artifact.filename, artifact.content); - progress.received.push({ + initialProgress.received.push({ absoluteOutputDestination, filename: artifact.filename, sizeInBytes: artifact.content.length, }); }; - onProgress(progress); + onProgress(initialProgress); return { onArtifact, - initialProgress: progress, }; }; diff --git a/packages/cli/src/progress-bar.ts b/packages/cli/src/progress-bar.ts index cc57ecd88ea..20cc2ca1b18 100644 --- a/packages/cli/src/progress-bar.ts +++ b/packages/cli/src/progress-bar.ts @@ -8,6 +8,7 @@ import type { StitchingProgressInput, } from '@remotion/studio-server'; import {StudioServerInternals} from '@remotion/studio-server'; +import {formatBytes, type ArtifactProgress} from '@remotion/studio-shared'; import {chalk} from './chalk'; import { getFileSizeDownloadBar, @@ -192,6 +193,30 @@ const makeRenderingProgress = ({ .join(' '); }; +const makeArtifactProgress = (artifactState: ArtifactProgress) => { + const {received} = artifactState; + if (received.length === 0) { + return null; + } + + return received + .map((artifact) => { + return [ + chalk.blue('+'.padEnd(LABEL_WIDTH)), + chalk.blue( + makeHyperlink({ + url: 'file://' + artifact.absoluteOutputDestination, + fallback: artifact.filename, + text: artifact.filename, + }), + ), + chalk.gray(`${formatBytes(artifact.sizeInBytes)}`), + ].join(' '); + }) + .filter(truthy) + .join('\n'); +}; + export const getRightLabelWidth = (totalFrames: number) => { return `${totalFrames}/${totalFrames}`.length; }; @@ -237,7 +262,7 @@ export const makeRenderingAndStitchingProgress = ({ progress: number; message: string; } => { - const {rendering, stitching, downloads, bundling} = prog; + const {rendering, stitching, downloads, bundling, artifactState} = prog; const output = [ rendering ? makeRenderingProgress(rendering) : null, makeMultiDownloadProgress(downloads, rendering?.totalFrames ?? 0), @@ -247,6 +272,7 @@ export const makeRenderingAndStitchingProgress = ({ stitchingProgress: stitching, isUsingParallelEncoding, }), + makeArtifactProgress(artifactState), ] .filter(truthy) .join('\n'); diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index 82c82600850..89983aeb578 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -28,6 +28,7 @@ import type { RenderingProgressInput, StitchingProgressInput, } from '@remotion/studio-server'; +import type {ArtifactProgress} from '@remotion/studio-shared'; import fs, {existsSync} from 'node:fs'; import os from 'node:os'; import path from 'node:path'; @@ -220,12 +221,7 @@ export const renderVideoFlow = async ({ doneIn: null, }; - let {onArtifact, initialProgress: artifactState} = handleOnArtifact( - (progress) => { - artifactState = progress; - updateRenderProgress({newline: false, printToConsole: true}); - }, - ); + let artifactState: ArtifactProgress = {received: []}; const updateRenderProgress = ({ newline, @@ -254,6 +250,11 @@ export const renderVideoFlow = async ({ } }; + const {onArtifact} = handleOnArtifact(artifactState, (progress) => { + artifactState = progress; + updateRenderProgress({newline: false, printToConsole: true}); + }); + const {urlOrBundle, cleanup: cleanupBundle} = await bundleOnCliOrTakeServeUrl( { fullPath: fullEntryPoint, From d7a75ec463dd3dfb17fe7e5f5e071e5c736b0949 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Mon, 17 Jun 2024 17:08:11 +0200 Subject: [PATCH 12/36] artifact name --- packages/cli/src/on-artifact.ts | 3 +- packages/lambda/src/functions/helpers/io.ts | 2 +- .../src/functions/helpers/stream-renderer.ts | 27 ++++++++- packages/lambda/src/functions/launch.ts | 56 ++++++++++++++++++- packages/lambda/src/functions/renderer.ts | 27 ++++++++- .../src/functions/streaming/streaming.ts | 10 ++++ packages/lambda/src/shared/constants.ts | 2 + packages/renderer/src/index.ts | 7 ++- packages/renderer/src/render-frames.ts | 4 +- 9 files changed, 128 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts index c93f287faa2..7fa19e4c811 100644 --- a/packages/cli/src/on-artifact.ts +++ b/packages/cli/src/on-artifact.ts @@ -10,10 +10,9 @@ export const handleOnArtifact = ( const initialProgress = {...artifactState}; const onArtifact: OnArtifact = (artifact) => { - // TODO: Normalize backslashes on Windows const absoluteOutputDestination = path.join( process.cwd(), - artifact.filename, + artifact.filename.replace('/', path.sep), ); writeFileSync(artifact.filename, artifact.content); diff --git a/packages/lambda/src/functions/helpers/io.ts b/packages/lambda/src/functions/helpers/io.ts index 09efc03e7a5..adc8d2f1909 100644 --- a/packages/lambda/src/functions/helpers/io.ts +++ b/packages/lambda/src/functions/helpers/io.ts @@ -99,7 +99,7 @@ export const lambdaDeleteFile = async ({ type LambdaWriteFileInput = { bucketName: string; key: string; - body: ReadStream | string; + body: ReadStream | string | Uint8Array; region: AwsRegion; privacy: Privacy; expectedBucketOwner: string | null; diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts index 94fe98cada5..e7a041aaaa3 100644 --- a/packages/lambda/src/functions/helpers/stream-renderer.ts +++ b/packages/lambda/src/functions/helpers/stream-renderer.ts @@ -1,4 +1,4 @@ -import type {LogLevel} from '@remotion/renderer'; +import type {EmittedAsset, LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {writeFileSync} from 'fs'; import {join} from 'path'; @@ -26,6 +26,7 @@ const streamRenderer = ({ overallProgress, files, logLevel, + onArtifact, }: { payload: LambdaPayload; functionName: string; @@ -33,6 +34,7 @@ const streamRenderer = ({ overallProgress: OverallProgressHelper; files: string[]; logLevel: LogLevel; + onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean}; }) => { if (payload.type !== LambdaRoutines.renderer) { throw new Error('Expected renderer type'); @@ -97,6 +99,25 @@ const streamRenderer = ({ return; } + if (message.type === 'artifact-emitted') { + RenderInternals.Log.info( + {indent: false, logLevel}, + `Received artifact on frame ${message.payload.artifact.frame}:`, + message.payload.artifact.filename, + message.payload.artifact.content.length + 'bytes.', + ); + const {alreadyExisted} = onArtifact(message.payload.artifact); + if (alreadyExisted) { + return resolve({ + type: 'error', + error: `Chunk ${payload.chunk} emitted an asset filename ${message.payload.artifact.filename} at frame ${message.payload.artifact.frame} but there is already another artifact with the same name.`, + shouldRetry: false, + }); + } + + return; + } + if (message.type === 'error-occurred') { overallProgress.addErrorWithoutUpload(message.payload.errorInfo); overallProgress.setFrames({ @@ -162,6 +183,7 @@ export const streamRendererFunctionWithRetry = async ({ outdir, overallProgress, logLevel, + onArtifact, }: { payload: LambdaPayload; functionName: string; @@ -169,6 +191,7 @@ export const streamRendererFunctionWithRetry = async ({ overallProgress: OverallProgressHelper; files: string[]; logLevel: LogLevel; + onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean}; }): Promise<unknown> => { if (payload.type !== LambdaRoutines.renderer) { throw new Error('Expected renderer type'); @@ -181,6 +204,7 @@ export const streamRendererFunctionWithRetry = async ({ overallProgress, payload, logLevel, + onArtifact, }); if (result.type === 'error') { @@ -205,6 +229,7 @@ export const streamRendererFunctionWithRetry = async ({ retriesLeft: payload.retriesLeft - 1, }, logLevel, + onArtifact, }); } }; diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts index 78c8d2e58ae..078d0def3db 100644 --- a/packages/lambda/src/functions/launch.ts +++ b/packages/lambda/src/functions/launch.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ -import type {LogOptions} from '@remotion/renderer'; +import type {EmittedAsset, LogOptions} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {existsSync, mkdirSync, rmSync} from 'fs'; import {join} from 'path'; import {VERSION} from 'remotion/version'; -import type {EventEmitter} from 'stream'; +import {type EventEmitter} from 'stream'; import { compressInputProps, decompressInputProps, @@ -20,6 +20,7 @@ import { CONCAT_FOLDER_TOKEN, LambdaRoutines, MAX_FUNCTIONS_PER_RENDER, + artifactName, } from '../shared/constants'; import {DOCS_URL} from '../shared/docs-url'; import {invokeWebhook} from '../shared/invoke-webhook'; @@ -41,6 +42,7 @@ import { getBrowserInstance, } from './helpers/get-browser-instance'; import {getCurrentRegionInFunction} from './helpers/get-current-region'; +import {lambdaWriteFile} from './helpers/io'; import {mergeChunksAndFinishRender} from './helpers/merge-chunks'; import type {OverallProgressHelper} from './helpers/overall-render-progress'; import {makeOverallRenderProgress} from './helpers/overall-render-progress'; @@ -54,6 +56,11 @@ type Options = { getRemainingTimeInMillis: () => number; }; +type ReceivedAsset = { + filename: string; + sizeInBytes: number; +}; + const innerLaunchHandler = async ({ functionName, params, @@ -354,6 +361,50 @@ const innerLaunchHandler = async ({ const files: string[] = []; + const artifacts: ReceivedAsset[] = []; + + const onArtifact = (artifact: EmittedAsset): {alreadyExisted: boolean} => { + if (artifacts.find((a) => a.filename === artifact.filename)) { + return {alreadyExisted: true}; + } + + artifacts.push({ + filename: artifact.filename, + sizeInBytes: artifact.content.length, + }); + + const start = Date.now(); + RenderInternals.Log.info( + {indent: false, logLevel: params.logLevel}, + 'Writing artifact ' + artifact.filename + ' to S3', + ); + lambdaWriteFile({ + bucketName: renderBucketName, + key: artifactName(renderMetadata.renderId, artifact.filename), + body: artifact.content, + region: getCurrentRegionInFunction(), + privacy: params.privacy, + expectedBucketOwner: options.expectedBucketOwner, + downloadBehavior: params.downloadBehavior, + customCredentials, + }) + .then(() => { + RenderInternals.Log.info( + {indent: false, logLevel: params.logLevel}, + `Wrote artifact to S3 in ${Date.now() - start}ms`, + ); + }) + .catch((err) => { + // TODO: Handle error + RenderInternals.Log.error( + {indent: false, logLevel: params.logLevel}, + 'Failed to write artifact to S3', + err, + ); + }); + return {alreadyExisted: false}; + }; + await Promise.all( lambdaPayloads.map(async (payload) => { await streamRendererFunctionWithRetry({ @@ -363,6 +414,7 @@ const innerLaunchHandler = async ({ overallProgress, payload, logLevel: params.logLevel, + onArtifact, }); }), ); diff --git a/packages/lambda/src/functions/renderer.ts b/packages/lambda/src/functions/renderer.ts index 8bae9be4b9b..439c63136d5 100644 --- a/packages/lambda/src/functions/renderer.ts +++ b/packages/lambda/src/functions/renderer.ts @@ -173,7 +173,32 @@ const renderHandler = async ({ ); const onArtifact: OnArtifact = (artifact) => { - console.log(artifact); + RenderInternals.Log.info( + {indent: false, logLevel: params.logLevel}, + `Received artifact on frame ${artifact.frame}:`, + artifact.filename, + artifact.content.length + 'bytes. Streaming to main function', + ); + const startTimestamp = Date.now(); + onStream({ + type: 'artifact-emitted', + payload: { + artifact, + }, + }) + .then(() => { + RenderInternals.Log.info( + {indent: false, logLevel: params.logLevel}, + `Streaming artifact ${artifact.filename} to main function took ${Date.now() - startTimestamp}ms`, + ); + }) + .catch((e) => { + RenderInternals.Log.error( + {indent: false, logLevel: params.logLevel}, + `Error streaming artifact ${artifact.filename} to main function`, + e, + ); + }); }; await new Promise<void>((resolve, reject) => { diff --git a/packages/lambda/src/functions/streaming/streaming.ts b/packages/lambda/src/functions/streaming/streaming.ts index f7d8eea4e13..71966376ef3 100644 --- a/packages/lambda/src/functions/streaming/streaming.ts +++ b/packages/lambda/src/functions/streaming/streaming.ts @@ -1,3 +1,4 @@ +import type {EmittedAsset} from '@remotion/renderer'; import {makeStreamPayloadMessage} from '@remotion/streaming'; import type {LambdaErrorInfo} from '../helpers/write-lambda-error'; import type {RenderStillLambdaResponsePayload} from '../still'; @@ -10,6 +11,7 @@ const audioChunkRendered = 'audio-chunk-rendered' as const; const chunkComplete = 'chunk-complete' as const; const stillRendered = 'still-rendered' as const; const lambdaInvoked = 'lambda-invoked' as const; +const artifactEmitted = 'artifact-emitted' as const; const messageTypes = { '1': {type: framesRendered}, @@ -20,6 +22,7 @@ const messageTypes = { '6': {type: stillRendered}, '7': {type: chunkComplete}, '8': {type: lambdaInvoked}, + '9': {type: artifactEmitted}, } as const; export type MessageTypeId = keyof typeof messageTypes; @@ -34,6 +37,7 @@ export const formatMap: {[key in MessageType]: 'json' | 'binary'} = { [stillRendered]: 'json', [chunkComplete]: 'json', [lambdaInvoked]: 'json', + [artifactEmitted]: 'json', }; export type StreamingPayload = @@ -82,6 +86,12 @@ export type StreamingPayload = payload: { attempt: number; }; + } + | { + type: typeof artifactEmitted; + payload: { + artifact: EmittedAsset; + }; }; export const messageTypeIdToMessageType = ( diff --git a/packages/lambda/src/shared/constants.ts b/packages/lambda/src/shared/constants.ts index 8abd7bd221d..3591297884c 100644 --- a/packages/lambda/src/shared/constants.ts +++ b/packages/lambda/src/shared/constants.ts @@ -85,6 +85,8 @@ export const outName = (renderId: string, extension: string) => `${rendersPrefix(renderId)}/out.${extension}`; export const outStillName = (renderId: string, imageFormat: StillImageFormat) => `${rendersPrefix(renderId)}/out.${imageFormat}`; +export const artifactName = (renderId: string, name: string) => + `${rendersPrefix(renderId)}/artifacts/${name}`; export const customOutName = ( renderId: string, bucketName: string, diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts index ac072fa0339..9b3973414ee 100644 --- a/packages/renderer/src/index.ts +++ b/packages/renderer/src/index.ts @@ -108,7 +108,12 @@ export {X264Preset} from './options/x264-preset'; export {PixelFormat} from './pixel-format'; export {RemotionServer} from './prepare-server'; export {ProResProfile} from './prores-profile'; -export {OnArtifact, RenderFramesOptions, renderFrames} from './render-frames'; +export { + EmittedArtifact as EmittedAsset, + OnArtifact, + RenderFramesOptions, + renderFrames, +} from './render-frames'; export { InternalRenderMediaOptions, RenderMediaOnProgress, diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index 6e753eddffc..5b8dc820233 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -67,7 +67,7 @@ import {wrapWithErrorHandling} from './wrap-with-error-handling'; const MAX_RETRIES_PER_FRAME = 1; -export type OnArtifact = (asset: EmittedAsset) => void; +export type OnArtifact = (asset: EmittedArtifact) => void; type InternalRenderFramesOptions = { onStart: null | ((data: OnStartData) => void); @@ -106,7 +106,7 @@ type InternalRenderFramesOptions = { onArtifact: OnArtifact | null; } & ToOptions<typeof optionsMap.renderFrames>; -type EmittedAsset = { +export type EmittedArtifact = { filename: string; content: string | Uint8Array; frame: number; From e6f27c263d35b7a161e4ed75cd3903e7a2df3c12 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Mon, 17 Jun 2024 17:29:20 +0200 Subject: [PATCH 13/36] let's go --- packages/example/runlambda.sh | 2 +- .../src/cli/commands/render/progress.ts | 27 +++++++++++++++++++ .../helpers/create-post-render-data.ts | 1 + .../src/functions/helpers/get-progress.ts | 2 ++ .../helpers/overall-render-progress.ts | 17 ++++++++++++ packages/lambda/src/functions/launch.ts | 22 +++++++-------- packages/lambda/src/shared/constants.ts | 3 +++ 7 files changed, 62 insertions(+), 12 deletions(-) diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh index 43c2eb94300..935c37f18bd 100644 --- a/packages/example/runlambda.sh +++ b/packages/example/runlambda.sh @@ -7,4 +7,4 @@ cd example bunx remotion lambda functions rmall -f bunx remotion lambda functions deploy --memory=3000 --disk=10000 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry -bunx remotion lambda render testbed-v6 OffthreadRemoteVideo --log=verbose --delete-after="1-day" +bunx remotion lambda render testbed-v6 subtitle --log=verbose --delete-after="1-day" diff --git a/packages/lambda/src/cli/commands/render/progress.ts b/packages/lambda/src/cli/commands/render/progress.ts index 2ee2a50c77f..316a6a14fe4 100644 --- a/packages/lambda/src/cli/commands/render/progress.ts +++ b/packages/lambda/src/cli/commands/render/progress.ts @@ -200,6 +200,32 @@ const makeTopRow = (overall: RenderProgress) => { return CliInternals.chalk.gray(str); }; +const makeArtifactProgress = (progress: RenderProgress) => { + const {artifactProgress} = progress; + if (artifactProgress.length === 0) { + return null; + } + + return artifactProgress + .map((artifact) => { + return [ + CliInternals.chalk.blue('+'.padEnd(CliInternals.LABEL_WIDTH)), + CliInternals.chalk.blue( + CliInternals.makeHyperlink({ + url: artifact.s3Url, + fallback: artifact.filename, + text: artifact.filename, + }), + ), + CliInternals.chalk.gray( + `${CliInternals.formatBytes(artifact.sizeInBytes)}`, + ), + ].join(' '); + }) + .filter(truthy) + .join('\n'); +}; + export const makeProgressString = ({ downloadInfo, overall, @@ -213,6 +239,7 @@ export const makeProgressString = ({ ...makeInvokeProgress(overall), ...makeRenderProgress(overall), makeCombinationProgress(overall), + makeArtifactProgress(overall), downloadInfo ? makeDownloadProgress(downloadInfo) : null, ] .filter(NoReactInternals.truthy) diff --git a/packages/lambda/src/functions/helpers/create-post-render-data.ts b/packages/lambda/src/functions/helpers/create-post-render-data.ts index ba9ffd508d0..f5a550063ed 100644 --- a/packages/lambda/src/functions/helpers/create-post-render-data.ts +++ b/packages/lambda/src/functions/helpers/create-post-render-data.ts @@ -103,5 +103,6 @@ export const createPostRenderData = ({ deleteAfter: renderMetadata.deleteAfter, estimatedBillingDurationInMilliseconds, timeToCombine: timeToCombine ?? null, + artifactProgress: overallProgress.receivedAssets, }; }; diff --git a/packages/lambda/src/functions/helpers/get-progress.ts b/packages/lambda/src/functions/helpers/get-progress.ts index 0f3928027ce..0a77693adc6 100644 --- a/packages/lambda/src/functions/helpers/get-progress.ts +++ b/packages/lambda/src/functions/helpers/get-progress.ts @@ -110,6 +110,7 @@ export const getProgress = async ({ compositionValidated: overallProgress.compositionValidated, functionLaunched: overallProgress.functionLaunched, serveUrlOpened: overallProgress.serveUrlOpened, + artifactProgress: overallProgress.receivedAssets, }; } @@ -257,5 +258,6 @@ export const getProgress = async ({ compositionValidated: overallProgress.compositionValidated, functionLaunched: overallProgress.functionLaunched, serveUrlOpened: overallProgress.serveUrlOpened, + artifactProgress: overallProgress.receivedAssets, }; }; diff --git a/packages/lambda/src/functions/helpers/overall-render-progress.ts b/packages/lambda/src/functions/helpers/overall-render-progress.ts index ecc6557e89c..68a793d98c2 100644 --- a/packages/lambda/src/functions/helpers/overall-render-progress.ts +++ b/packages/lambda/src/functions/helpers/overall-render-progress.ts @@ -26,6 +26,13 @@ export type OverallRenderProgress = { functionLaunched: number; serveUrlOpened: number | null; compositionValidated: number | null; + receivedAssets: ReceivedAsset[]; +}; + +export type ReceivedAsset = { + filename: string; + sizeInBytes: number; + s3Url: string; }; export type OverallProgressHelper = { @@ -55,6 +62,8 @@ export type OverallProgressHelper = { get: () => OverallRenderProgress; setServeUrlOpened: (timestamp: number) => void; setCompositionValidated: (timestamp: number) => void; + addReceivedAsset: (asset: ReceivedAsset) => void; + getReceivedAssets: () => ReceivedAsset[]; }; export const makeInitialOverallRenderProgress = ( @@ -78,6 +87,7 @@ export const makeInitialOverallRenderProgress = ( functionLaunched: Date.now(), serveUrlOpened: null, compositionValidated: null, + receivedAssets: [], }; }; @@ -274,6 +284,13 @@ export const makeOverallRenderProgress = ({ renderProgress.retries.push(retry); upload(); }, + addReceivedAsset(asset) { + renderProgress.receivedAssets.push(asset); + upload(); + }, + getReceivedAssets() { + return renderProgress.receivedAssets; + }, get: () => renderProgress, }; }; diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts index 078d0def3db..f42a3873c29 100644 --- a/packages/lambda/src/functions/launch.ts +++ b/packages/lambda/src/functions/launch.ts @@ -56,11 +56,6 @@ type Options = { getRemainingTimeInMillis: () => number; }; -type ReceivedAsset = { - filename: string; - sizeInBytes: number; -}; - const innerLaunchHandler = async ({ functionName, params, @@ -361,16 +356,21 @@ const innerLaunchHandler = async ({ const files: string[] = []; - const artifacts: ReceivedAsset[] = []; - const onArtifact = (artifact: EmittedAsset): {alreadyExisted: boolean} => { - if (artifacts.find((a) => a.filename === artifact.filename)) { + if ( + overallProgress + .getReceivedAssets() + .find((a) => a.filename === artifact.filename) + ) { return {alreadyExisted: true}; } - artifacts.push({ + const region = getCurrentRegionInFunction(); + const s3Key = artifactName(renderMetadata.renderId, artifact.filename); + overallProgress.addReceivedAsset({ filename: artifact.filename, sizeInBytes: artifact.content.length, + s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, }); const start = Date.now(); @@ -380,9 +380,9 @@ const innerLaunchHandler = async ({ ); lambdaWriteFile({ bucketName: renderBucketName, - key: artifactName(renderMetadata.renderId, artifact.filename), + key: s3Key, body: artifact.content, - region: getCurrentRegionInFunction(), + region, privacy: params.privacy, expectedBucketOwner: options.expectedBucketOwner, downloadBehavior: params.downloadBehavior, diff --git a/packages/lambda/src/shared/constants.ts b/packages/lambda/src/shared/constants.ts index 3591297884c..6418b7b2979 100644 --- a/packages/lambda/src/shared/constants.ts +++ b/packages/lambda/src/shared/constants.ts @@ -14,6 +14,7 @@ import type { import type {BrowserSafeApis} from '@remotion/renderer/client'; import type {ChunkRetry} from '../functions/helpers/get-retry-stats'; import type {DeleteAfter} from '../functions/helpers/lifecycle'; +import type {ReceivedAsset} from '../functions/helpers/overall-render-progress'; import type {EnhancedErrorInfo} from '../functions/helpers/write-lambda-error'; import type {AwsRegion} from '../pricing/aws-regions'; import type { @@ -414,6 +415,7 @@ export type PostRenderData = { estimatedBillingDurationInMilliseconds: number; deleteAfter: DeleteAfter | null; timeToCombine: number | null; + artifactProgress: ReceivedAsset[]; }; export type CostsInfo = { @@ -469,6 +471,7 @@ export type RenderProgress = { functionLaunched: number; serveUrlOpened: number | null; compositionValidated: number | null; + artifactProgress: ReceivedAsset[]; }; export type Privacy = 'public' | 'private' | 'no-acl'; From fb2e525d44a0d42183c0f2727483f1aa7f6f87e2 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Tue, 18 Jun 2024 17:41:52 +0200 Subject: [PATCH 14/36] artifact --- packages/core/src/Artifact.tsx | 10 +- packages/docs/docs/artifact.mdx | 62 + .../docs/miscellaneous/linux-dependencies.mdx | 1 + packages/docs/sidebars.js | 1 + packages/docs/src/data/articles.ts | 5046 +++++++++-------- .../generated/articles-docs-artifact.png | Bin 0 -> 32218 bytes ...-docs-miscellaneous-linux-dependencies.png | Bin 0 -> 37868 bytes 7 files changed, 2602 insertions(+), 2518 deletions(-) create mode 100644 packages/docs/docs/artifact.mdx create mode 100644 packages/docs/static/generated/articles-docs-artifact.png create mode 100644 packages/docs/static/generated/articles-docs-miscellaneous-linux-dependencies.png diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx index 489e47685d4..cca65033b33 100644 --- a/packages/core/src/Artifact.tsx +++ b/packages/core/src/Artifact.tsx @@ -1,17 +1,18 @@ import type React from 'react'; import {useContext, useEffect, useState} from 'react'; import {RenderAssetManager} from './RenderAssetManager'; +import {getRemotionEnvironment} from './get-remotion-environment'; import {useCurrentFrame} from './use-current-frame'; -// TODO: Not register in development? export const Artifact: React.FC<{ readonly filename: string; readonly content: string; }> = ({filename, content}) => { - // TODO: Validate filename and content const {registerRenderAsset, unregisterRenderAsset} = useContext(RenderAssetManager); + const [env] = useState(() => getRemotionEnvironment()); + const frame = useCurrentFrame(); const [id] = useState(() => { @@ -19,6 +20,10 @@ export const Artifact: React.FC<{ }); useEffect(() => { + if (!env.isRendering) { + return; + } + registerRenderAsset({ type: 'artifact', id, @@ -32,6 +37,7 @@ export const Artifact: React.FC<{ }; }, [ content, + env.isRendering, filename, frame, id, diff --git a/packages/docs/docs/artifact.mdx b/packages/docs/docs/artifact.mdx new file mode 100644 index 00000000000..146c33e5068 --- /dev/null +++ b/packages/docs/docs/artifact.mdx @@ -0,0 +1,62 @@ +--- +image: /generated/articles-docs-artifact.png +title: "<Artifact>" +crumb: API +--- + +# `<Artifact>`<AvailableFrom v="4.0.176"/> + +By rendering an `<Artifact>` tag in your Remotion markup, an extra file will get emitted during rendering. + +```tsx twoslash title="MyComp.tsx" +import { Artifact, useCurrentFrame } from "remotion"; + +export const MyComp: React.FC = () => { + const frame = useCurrentFrame(); + + return frame === 0 ? ( + <Artifact filename="my-file.txt" content="Hello World!" /> + ) : null; +}; +``` + +If rendered on the CLI or via the Studio, this will emit an additional file: + +``` +$ npx remotion render MyComp ++ out/MyComp.mp4 ++ my-file.txt (12B) +``` + +It is allowed for a composition to emit multiple files. +However, the file names must be unique. + +The component will get evaluated on every frame, which means if you want to emit just one file, only render it on one frame. + +```tsx twoslash title="❌ Will generate an asset on every frame and throw an error because the file name is not unique" +import { Artifact, useCurrentFrame } from "remotion"; + +export const MyComp: React.FC = () => { + const frame = useCurrentFrame(); + + return frame === 0 ? ( + <Artifact filename="my-file.txt" content="Hello World!" /> + ) : null; +}; +``` + +## API + +### `filename` + +A string that is the name of the file that will be emitted. +Use forward slashes only, even on Windows. +Must match the regex `/^([0-9a-zA-Z-!_.*'()/:&$@=;+,?]+)/g`. + +### `content` + +A string or `UInt8Array` that is the content of the file that will be emitted. + +## See also + +- [Source code for this component](https://github.com/remotion-dev/remotion/blob/main/packages/core/src/components/Artifact.tsx) diff --git a/packages/docs/docs/miscellaneous/linux-dependencies.mdx b/packages/docs/docs/miscellaneous/linux-dependencies.mdx index 16721224e49..91dd7421e7e 100644 --- a/packages/docs/docs/miscellaneous/linux-dependencies.mdx +++ b/packages/docs/docs/miscellaneous/linux-dependencies.mdx @@ -1,4 +1,5 @@ --- +image: /generated/articles-docs-miscellaneous-linux-dependencies.png sidebar_label: Linux Dependencies title: Linux Dependencies crumb: "FAQ" diff --git a/packages/docs/sidebars.js b/packages/docs/sidebars.js index bd1c4e1aca2..3f56d1f2350 100644 --- a/packages/docs/sidebars.js +++ b/packages/docs/sidebars.js @@ -84,6 +84,7 @@ module.exports = { items: [ "absolute-fill", "audio", + "artifact", "calculate-metadata", "cancel-render", "composition", diff --git a/packages/docs/src/data/articles.ts b/packages/docs/src/data/articles.ts index ceb3701af32..42e185e56a3 100644 --- a/packages/docs/src/data/articles.ts +++ b/packages/docs/src/data/articles.ts @@ -1,73 +1,80 @@ export const articles = [ { - id: "2-0-migration", - title: "v2.0 Migration", - relativePath: "docs/2-0-migration.mdx", - compId: "articles-docs-2-0-migration", - crumb: "Version Upgrade", + id: "figma", + title: "Import from Figma", + relativePath: "docs/figma.mdx", + compId: "articles-docs-figma", + crumb: "The best of both", }, { - id: "3-0-migration", - title: "v3.0 Migration", - relativePath: "docs/3-0-migration.mdx", - compId: "articles-docs-3-0-migration", - crumb: "Version Upgrade", + id: "env-variables", + title: "Environment variables", + relativePath: "docs/env-variables.mdx", + compId: "articles-docs-env-variables", + crumb: "How To", }, { - id: "4-0-alpha", - title: "v4.0 Alpha", - relativePath: "docs/4-0-alpha.mdx", - compId: "articles-docs-4-0-alpha", - crumb: "Version Upgrade", + id: "skia", + title: "@remotion/skia", + relativePath: "docs/skia/skia.mdx", + compId: "articles-docs-skia-skia", + crumb: null, }, { - id: "4-0-migration", - title: "v4.0 Migration", - relativePath: "docs/4-0-migration.mdx", - compId: "articles-docs-4-0-migration", - crumb: "Version Upgrade", + id: "skia/enable-skia", + title: "enableSkia()", + relativePath: "docs/skia/enable-skia.mdx", + compId: "articles-docs-skia-enable-skia", + crumb: "@remotion/skia", }, { - id: "5-0-migration", - title: "v5.0 Migration", - relativePath: "docs/5-0-migration.mdx", - compId: "articles-docs-5-0-migration", - crumb: "Version Upgrade", + id: "skia/skia-canvas", + title: "<SkiaCanvas />", + relativePath: "docs/skia/skia-canvas.mdx", + compId: "articles-docs-skia-skia-canvas", + crumb: "@remotion/skia", }, { - id: "absolute-fill", - title: "<AbsoluteFill>", - relativePath: "docs/absolute-fill.mdx", - compId: "articles-docs-absolute-fill", + id: "spring", + title: "spring()", + relativePath: "docs/spring.mdx", + compId: "articles-docs-spring", crumb: "API", }, { - id: "acknowledgements", - title: "Acknowledgements", - relativePath: "docs/acknowledgements.mdx", - compId: "articles-docs-acknowledgements", - crumb: "Credits", + id: "audio-visualization", + title: "Audio visualization", + relativePath: "docs/audio-visualization.mdx", + compId: "articles-docs-audio-visualization", + crumb: "Techniques", }, { - id: "after-effects", - title: "Import from After Effects", - relativePath: "docs/after-effects.mdx", - compId: "articles-docs-after-effects", - crumb: "Integrations", + id: "react-native", + title: "React Native", + relativePath: "docs/react-native.mdx", + compId: "articles-docs-react-native", + crumb: null, }, { - id: "animating-properties", - title: "Animating properties", - relativePath: "docs/animating-properties.mdx", - compId: "articles-docs-animating-properties", - crumb: "The basics", + id: "use-current-scale", + title: "useCurrentScale()", + relativePath: "docs/use-current-scale.mdx", + compId: "articles-docs-use-current-scale", + crumb: "API", }, { - id: "animation-math", - title: "Animation math", - relativePath: "docs/animation-math.mdx", - compId: "articles-docs-animation-math", - crumb: "Techniques", + id: "config", + title: "Configuration file", + relativePath: "docs/config.mdx", + compId: "articles-docs-config", + crumb: "remotion.config.ts", + }, + { + id: "make-transform", + title: "makeTransform()", + relativePath: "docs/animation-utils/make-transform.mdx", + compId: "articles-docs-animation-utils-make-transform", + crumb: "@remotion/animation-utils", }, { id: "animation-utils/index", @@ -84,46 +91,25 @@ export const articles = [ crumb: "@remotion/animation-utils", }, { - id: "make-transform", - title: "makeTransform()", - relativePath: "docs/animation-utils/make-transform.mdx", - compId: "articles-docs-animation-utils-make-transform", - crumb: "@remotion/animation-utils", - }, - { - id: "api", - title: "API overview", - relativePath: "docs/api.mdx", - compId: "articles-docs-api", - crumb: null, - }, - { - id: "get-help", - title: "Get help", - relativePath: "docs/ask-for-help.mdx", - compId: "articles-docs-ask-for-help", - crumb: null, - }, - { - id: "audio-visualization", - title: "Audio visualization", - relativePath: "docs/audio-visualization.mdx", - compId: "articles-docs-audio-visualization", - crumb: "Techniques", + id: "4-0-alpha", + title: "v4.0 Alpha", + relativePath: "docs/4-0-alpha.mdx", + compId: "articles-docs-4-0-alpha", + crumb: "Version Upgrade", }, { - id: "audio", - title: "<Audio>", - relativePath: "docs/audio.mdx", - compId: "articles-docs-audio", - crumb: "How to", + id: "use-video-config", + title: "useVideoConfig()", + relativePath: "docs/use-video-config.mdx", + compId: "articles-docs-use-video-config", + crumb: "API", }, { - id: "audio-buffer-to-data-url", - title: "audioBufferToDataUrl()", - relativePath: "docs/audiobuffertodataurl.mdx", - compId: "articles-docs-audiobuffertodataurl", - crumb: "@remotion/media-utils", + id: "legacy-babel", + title: "Using legacy Babel transpilation", + relativePath: "docs/legacy-babel-loader.mdx", + compId: "articles-docs-legacy-babel-loader", + crumb: "How To", }, { id: "brownfield", @@ -133,508 +119,487 @@ export const articles = [ crumb: "Brownfield integration", }, { - id: "buffer-state", - title: "Display a buffer state", - relativePath: "docs/buffer-state.mdx", - compId: "articles-docs-buffer-state", - crumb: "Building video apps", + id: "studio/studio", + title: "Starting the Studio", + relativePath: "docs/studio/studio.mdx", + compId: "articles-docs-studio-studio", + crumb: "Timeline basics", }, { - id: "bun", - title: "Bun support", - relativePath: "docs/bun.mdx", - compId: "articles-docs-bun", - crumb: "bun bun bun bun bun", + id: "studio/quick-switcher", + title: "Quick Switcher", + relativePath: "docs/studio/quick-switcher.mdx", + compId: "articles-docs-studio-quick-switcher", + crumb: "Remotion Studio", }, { - id: "bundle", - title: "bundle()", - relativePath: "docs/bundle.mdx", - compId: "articles-docs-bundle", - crumb: "@remotion/bundler", + id: "studio/restart-studio", + title: "restartStudio()", + relativePath: "docs/studio/restart-studio.mdx", + compId: "articles-docs-studio-restart-studio", + crumb: "@remotion/studio", }, { - id: "bundler", - title: "@remotion/bundler", - relativePath: "docs/bundler.mdx", - compId: "articles-docs-bundler", - crumb: null, + id: "studio/focus-default-props-path", + title: "focusDefaultPropsPath()", + relativePath: "docs/studio/focus-default-props-path.mdx", + compId: "articles-docs-studio-focus-default-props-path", + crumb: "@remotion/studio", }, { - id: "calculate-metadata", - title: "calculateMetadata()", - relativePath: "docs/calculate-metadata.mdx", - compId: "articles-docs-calculate-metadata", - crumb: "API", + id: "studio/save-default-props", + title: "saveDefaultProps()", + relativePath: "docs/studio/save-default-props.mdx", + compId: "articles-docs-studio-save-default-props", + crumb: "@remotion/studio", }, { - id: "cancel-render", - title: "cancelRender()", - relativePath: "docs/cancel-render.mdx", - compId: "articles-docs-cancel-render", - crumb: "How to", + id: "studio/deploy-static", + title: "Deploy Remotion Studio as static site", + relativePath: "docs/studio/deploy-static.mdx", + compId: "articles-docs-studio-deploy-static", + crumb: "Remotion Studio", }, { - id: "chromium-flags", - title: "Chromium flags", - relativePath: "docs/chromium-flags.mdx", - compId: "articles-docs-chromium-flags", - crumb: "Tweaks", + id: "studio/watch-public-folder", + title: "watchPublicFolder()", + relativePath: "docs/studio/watch-public-folder.mdx", + compId: "articles-docs-studio-watch-public-folder", + crumb: "@remotion/studio", }, { - id: "cli/benchmark", - title: "npx remotion benchmark", - relativePath: "docs/cli/benchmark.mdx", - compId: "articles-docs-cli-benchmark", - crumb: "CLI Reference", + id: "studio/deploy-server", + title: "Deploy the Remotion Studio on a VPS", + relativePath: "docs/studio/deploy-server.mdx", + compId: "articles-docs-studio-deploy-server", + crumb: "Remotion Studio", }, { - id: "cli/browser/ensure", - title: "npx remotion browser ensure", - relativePath: "docs/cli/browser/ensure.mdx", - compId: "articles-docs-cli-browser-ensure", - crumb: "@remotion/cli", + id: "studio/get-static-files", + title: "getStaticFiles()", + relativePath: "docs/studio/get-static-files.mdx", + compId: "articles-docs-studio-get-static-files", + crumb: "@remotion/studio", }, { - id: "cli/browser/index", - title: "npx remotion browser", - relativePath: "docs/cli/browser/index.mdx", - compId: "articles-docs-cli-browser-index", - crumb: "@remotion/cli", + id: "studio/watch-static-file", + title: "watchStaticFile()", + relativePath: "docs/studio/watch-static-file.mdx", + compId: "articles-docs-studio-watch-static-file", + crumb: "@remotion/studio", }, { - id: "cli/bundle", - title: "npx remotion bundle", - relativePath: "docs/cli/bundle.mdx", - compId: "articles-docs-cli-bundle", - crumb: "CLI Reference", + id: "studio/shortcuts", + title: "Keyboard navigation", + relativePath: "docs/studio/shortcuts.mdx", + compId: "articles-docs-studio-shortcuts", + crumb: "Remotion Studio", }, { - id: "cli", - title: "Command line reference", - relativePath: "docs/cli/cli.mdx", - compId: "articles-docs-cli-cli", + id: "studio/api", + title: "@remotion/studio", + relativePath: "docs/studio/api.mdx", + compId: "articles-docs-studio-api", crumb: null, }, { - id: "cli/compositions", - title: "npx remotion compositions", - relativePath: "docs/cli/compositions.mdx", - compId: "articles-docs-cli-compositions", - crumb: "CLI Reference", - }, - { - id: "ffmpeg", - title: "npx remotion ffmpeg", - relativePath: "docs/cli/ffmpeg.mdx", - compId: "articles-docs-cli-ffmpeg", - crumb: "@remotion/cli", + id: "studio/reevaluate-composition", + title: "reevaluateComposition()", + relativePath: "docs/studio/reevaluate-composition.mdx", + compId: "articles-docs-studio-reevaluate-composition", + crumb: "@remotion/studio", }, { - id: "ffprobe", - title: "npx remotion ffprobe", - relativePath: "docs/cli/ffprobe.mdx", - compId: "articles-docs-cli-ffprobe", - crumb: "@remotion/cli", + id: "studio/write-static-file", + title: "writeStaticFile()", + relativePath: "docs/studio/write-static-file.mdx", + compId: "articles-docs-studio-write-static-file", + crumb: "@remotion/studio", }, { - id: "cli/gpu", - title: "npx remotion gpu", - relativePath: "docs/cli/gpu.mdx", - compId: "articles-docs-cli-gpu", - crumb: "CLI Reference", + id: "studio/update-default-props", + title: "updateDefaultProps()", + relativePath: "docs/studio/update-default-props.mdx", + compId: "articles-docs-studio-update-default-props", + crumb: "@remotion/studio", }, { - id: "cli/help", - title: "npx remotion help", - relativePath: "docs/cli/help.mdx", - compId: "articles-docs-cli-help", - crumb: "CLI Reference", + id: "studio/delete-static-file", + title: "deleteStaticFile()", + relativePath: "docs/studio/delete-static-file.mdx", + compId: "articles-docs-studio-delete-static-file", + crumb: "@remotion/studio", }, { - id: "cli/install", - title: "npx remotion install", - relativePath: "docs/cli/install.mdx", - compId: "articles-docs-cli-install", - crumb: "CLI Reference", + id: "video-uploads", + title: "Handling user video uploads", + relativePath: "docs/video-uploads.mdx", + compId: "articles-docs-video-uploads", + crumb: "Building video apps", }, { - id: "cli/render", - title: "npx remotion render", - relativePath: "docs/cli/render.mdx", - compId: "articles-docs-cli-render", - crumb: "CLI Reference", + id: "bundler", + title: "@remotion/bundler", + relativePath: "docs/bundler.mdx", + compId: "articles-docs-bundler", + crumb: null, }, { - id: "cli/still", - title: "npx remotion still", - relativePath: "docs/cli/still.mdx", - compId: "articles-docs-cli-still", - crumb: "CLI Reference", + id: "using-audio", + title: "Using audio", + relativePath: "docs/using-audio.mdx", + compId: "articles-docs-using-audio", + crumb: "Techniques", }, { - id: "cli/studio", - title: "npx remotion studio", - relativePath: "docs/cli/studio.mdx", - compId: "articles-docs-cli-studio", - crumb: "CLI Reference", + id: "get-compositions", + title: "getCompositions()", + relativePath: "docs/renderer/get-compositions.mdx", + compId: "articles-docs-renderer-get-compositions", + crumb: "@remotion/renderer", }, { - id: "cli/upgrade", - title: "npx remotion upgrade", - relativePath: "docs/cli/upgrade.mdx", - compId: "articles-docs-cli-upgrade", - crumb: "CLI Reference", + id: "make-cancel-signal", + title: "makeCancelSignal()", + relativePath: "docs/renderer/make-cancel-signal.mdx", + compId: "articles-docs-renderer-make-cancel-signal", + crumb: "@remotion/renderer", }, { - id: "cli/versions", - title: "npx remotion versions", - relativePath: "docs/cli/versions.mdx", - compId: "articles-docs-cli-versions", - crumb: "CLI Reference", + id: "extract-audio", + title: "extractAudio()", + relativePath: "docs/renderer/extract-audio.mdx", + compId: "articles-docs-renderer-extract-audio", + crumb: "@remotion/renderer", }, { - id: "clipper", - title: "<Experimental.Clipper>", - relativePath: "docs/clipper.mdx", - compId: "articles-docs-clipper", - crumb: "Experimental API", + id: "get-video-metadata", + title: "getVideoMetadata()", + relativePath: "docs/renderer/get-video-metadata.mdx", + compId: "articles-docs-renderer-get-video-metadata", + crumb: "@remotion/renderer", }, { - id: "cloudrun/api", - title: "@remotion/cloudrun", - relativePath: "docs/cloudrun/api.mdx", - compId: "articles-docs-cloudrun-api", - crumb: "Render videos without servers on GCP", + id: "select-composition", + title: "selectComposition()", + relativePath: "docs/renderer/select-composition.mdx", + compId: "articles-docs-renderer-select-composition", + crumb: "@remotion/renderer", }, { - id: "checklist", - title: "Production Checklist", - relativePath: "docs/cloudrun/checklist.mdx", - compId: "articles-docs-cloudrun-checklist", - crumb: "Cloud Run", + id: "ensure-ffmpeg", + title: "ensureFfmpeg()", + relativePath: "docs/renderer/ensure-ffmpeg.mdx", + compId: "articles-docs-renderer-ensure-ffmpeg", + crumb: "@remotion/renderer", }, { - id: "permissions", - title: "npx remotion cloudrun permissions", - relativePath: "docs/cloudrun/cli/permissions.mdx", - compId: "articles-docs-cloudrun-cli-permissions", - crumb: "Cloud Run CLI Reference", + id: "get-can-extract-frames-fast", + title: "getCanExtractFramesFast()", + relativePath: "docs/renderer/get-can-extract-frames-fast.mdx", + compId: "articles-docs-renderer-get-can-extract-frames-fast", + crumb: "@remotion/renderer", }, { - id: "regions", - title: "npx remotion cloudrun regions", - relativePath: "docs/cloudrun/cli/regions.mdx", - compId: "articles-docs-cloudrun-cli-regions", - crumb: "Cloud Run CLI Reference", + id: "get-silent-parts", + title: "getSilentParts()", + relativePath: "docs/renderer/get-silent-parts.mdx", + compId: "articles-docs-renderer-get-silent-parts", + crumb: "@remotion/renderer", }, { - id: "render", - title: "npx remotion cloudrun render", - relativePath: "docs/cloudrun/cli/render.mdx", - compId: "articles-docs-cloudrun-cli-render", - crumb: "Cloud Run CLI Reference", + id: "open-browser", + title: "openBrowser()", + relativePath: "docs/renderer/open-browser.mdx", + compId: "articles-docs-renderer-open-browser", + crumb: "@remotion/renderer", }, { - id: "services", - title: "npx remotion cloudrun services", - relativePath: "docs/cloudrun/cli/services.mdx", - compId: "articles-docs-cloudrun-cli-services", - crumb: "Cloud Run CLI Reference", + id: "stitch-frames-to-video", + title: "stitchFramesToVideo()", + relativePath: "docs/renderer/stitch-frames-to-video.mdx", + compId: "articles-docs-renderer-stitch-frames-to-video", + crumb: "@remotion/renderer", }, { - id: "sites", - title: "npx remotion cloudrun sites", - relativePath: "docs/cloudrun/cli/sites.mdx", - compId: "articles-docs-cloudrun-cli-sites", - crumb: "Cloud Run CLI Reference", + id: "ensure-ffprobe", + title: "ensureFfprobe()", + relativePath: "docs/renderer/ensure-ffprobe.mdx", + compId: "articles-docs-renderer-ensure-ffprobe", + crumb: "@remotion/renderer", }, { - id: "still", - title: "npx remotion cloudrun still", - relativePath: "docs/cloudrun/cli/still.mdx", - compId: "articles-docs-cloudrun-cli-still", - crumb: "Cloud Run CLI Reference", + id: "render-media", + title: "renderMedia()", + relativePath: "docs/renderer/render-media.mdx", + compId: "articles-docs-renderer-render-media", + crumb: "@remotion/renderer", }, { - id: "cli", - title: "@remotion/cloudrun - CLI", - relativePath: "docs/cloudrun/cli.mdx", - compId: "articles-docs-cloudrun-cli", - crumb: null, + id: "ensure-browser", + title: "ensureBrowser()", + relativePath: "docs/renderer/ensure-browser.mdx", + compId: "articles-docs-renderer-ensure-browser", + crumb: "@remotion/renderer", }, { - id: "deleteservice", - title: "deleteService()", - relativePath: "docs/cloudrun/deleteservice.mdx", - compId: "articles-docs-cloudrun-deleteservice", - crumb: "Cloud Run API", + id: "render-still", + title: "renderStill()", + relativePath: "docs/renderer/render-still.mdx", + compId: "articles-docs-renderer-render-still", + crumb: "@remotion/renderer", }, { - id: "deletesite", - title: "deleteSite()", - relativePath: "docs/cloudrun/deletesite.mdx", - compId: "articles-docs-cloudrun-deletesite", - crumb: "Cloud Run API", + id: "render-frames", + title: "renderFrames()", + relativePath: "docs/renderer/render-frames.mdx", + compId: "articles-docs-renderer-render-frames", + crumb: "@remotion/renderer", }, { - id: "deployservice", - title: "deployService()", - relativePath: "docs/cloudrun/deployservice.mdx", - compId: "articles-docs-cloudrun-deployservice", - crumb: "Cloud Run API", + id: "bundle", + title: "bundle()", + relativePath: "docs/bundle.mdx", + compId: "articles-docs-bundle", + crumb: "@remotion/bundler", }, { - id: "deploysite", - title: "deploySite()", - relativePath: "docs/cloudrun/deploysite.mdx", - compId: "articles-docs-cloudrun-deploysite", - crumb: "Cloud Run API", - }, - { - id: "generate-env", - title: "Generate .env File", - relativePath: "docs/cloudrun/generate-env.mdx", - compId: "articles-docs-cloudrun-generate-env", - crumb: "Cloud Run", - }, - { - id: "getserviceinfo", - title: "getServiceInfo()", - relativePath: "docs/cloudrun/getServiceinfo.mdx", - compId: "articles-docs-cloudrun-getServiceinfo", - crumb: "Cloud Run API", + id: "img", + title: "<Img>", + relativePath: "docs/img.mdx", + compId: "articles-docs-img", + crumb: "API", }, { - id: "getorcreatebucket", - title: "getOrCreateBucket()", - relativePath: "docs/cloudrun/getorcreatebucket.mdx", - compId: "articles-docs-cloudrun-getorcreatebucket", - crumb: "Cloud Run API", + id: "encoding", + title: "Encoding Guide", + relativePath: "docs/encoding.mdx", + compId: "articles-docs-encoding", + crumb: "Codecs and more", }, { - id: "getregions", - title: "getRegions()", - relativePath: "docs/cloudrun/getregions.mdx", - compId: "articles-docs-cloudrun-getregions", - crumb: "Cloud Run API", + id: "get-remotion-environment", + title: "getRemotionEnvironment()", + relativePath: "docs/get-remotion-environment.mdx", + compId: "articles-docs-get-remotion-environment", + crumb: "API", }, { - id: "getservices", - title: "getServices()", - relativePath: "docs/cloudrun/getservices.mdx", - compId: "articles-docs-cloudrun-getservices", - crumb: "Cloud Run API", + id: "sequence", + title: "<Sequence>", + relativePath: "docs/sequence.mdx", + compId: "articles-docs-sequence", + crumb: "API", }, { - id: "getsites", - title: "getSites()", - relativePath: "docs/cloudrun/getsites.mdx", - compId: "articles-docs-cloudrun-getsites", - crumb: "Cloud Run API", + id: "the-fundamentals", + title: "The fundamentals", + relativePath: "docs/the-fundamentals.mdx", + compId: "articles-docs-the-fundamentals", + crumb: "Getting started", }, { - id: "instancecount", - title: "Instance Count", - relativePath: "docs/cloudrun/instancecount.mdx", - compId: "articles-docs-cloudrun-instancecount", - crumb: "Cloud Run", + id: "ssr", + title: "Server-Side Rendering", + relativePath: "docs/ssr.mdx", + compId: "articles-docs-ssr", + crumb: "The power of", }, { - id: "cloudrun/light-client", - title: "Light client", - relativePath: "docs/cloudrun/light-client.mdx", - compId: "articles-docs-cloudrun-light-client", - crumb: "Cloud Run", + id: "after-effects", + title: "Import from After Effects", + relativePath: "docs/after-effects.mdx", + compId: "articles-docs-after-effects", + crumb: "Integrations", }, { - id: "cloudrun/limits", - title: "Cloud Run Limits", - relativePath: "docs/cloudrun/limits.mdx", - compId: "articles-docs-cloudrun-limits", - crumb: "Cloud Run", + id: "assets", + title: "Importing assets", + relativePath: "docs/importing-assets.mdx", + compId: "articles-docs-importing-assets", + crumb: "How To", }, { - id: "cloudrun/multiple-buckets", - title: "Multiple buckets in Remotion Cloud Run", - relativePath: "docs/cloudrun/multiple-buckets.mdx", - compId: "articles-docs-cloudrun-multiple-buckets", - crumb: "@remotion/cloudrun", + id: "get-video-metadata", + title: "getVideoMetadata()", + relativePath: "docs/get-video-metadata.mdx", + compId: "articles-docs-get-video-metadata", + crumb: "@remotion/media-utils", }, { - id: "cloudrun/permissions", - title: "Permissions", - relativePath: "docs/cloudrun/permissions.mdx", - compId: "articles-docs-cloudrun-permissions", - crumb: "Cloud Run", + id: "media-playback-error", + title: "Could not play video/audio with src", + relativePath: "docs/media-playback-error.mdx", + compId: "articles-docs-media-playback-error", + crumb: "Troubleshooting", }, { - id: "region-selection", - title: "Region selection", - relativePath: "docs/cloudrun/region-selection.mdx", - compId: "articles-docs-cloudrun-region-selection", - crumb: "Cloud Run", + id: "staticfile-relative-paths", + title: "staticFile() does not support relative paths", + relativePath: "docs/static-file-relative-paths.mdx", + compId: "articles-docs-static-file-relative-paths", + crumb: "Troubleshooting", }, { - id: "rendermediaoncloudrun", - title: "renderMediaOnCloudrun()", - relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx", - compId: "articles-docs-cloudrun-rendermediaoncloudrun", - crumb: "Cloud Run API", + id: "interpolate", + title: "interpolate()", + relativePath: "docs/interpolate.mdx", + compId: "articles-docs-interpolate", + crumb: "API", }, { - id: "renderstilloncloudrun", - title: "renderStillOnCloudrun()", - relativePath: "docs/cloudrun/renderstilloncloudrun.mdx", - compId: "articles-docs-cloudrun-renderstilloncloudrun", - crumb: "Cloud Run API", + id: "render-as-gif", + title: "Rendering GIFs", + relativePath: "docs/render-as-gif.mdx", + compId: "articles-docs-render-as-gif", + crumb: "Techniques", }, { - id: "setup", - title: "Setup", - relativePath: "docs/cloudrun/setup.mdx", - compId: "articles-docs-cloudrun-setup", - crumb: "Cloud Run", + id: "webpack-dynamic-imports", + title: "Webpack dynamic imports", + relativePath: "docs/dynamic-import.mdx", + compId: "articles-docs-dynamic-import", + crumb: "Knowledge Base", }, { - id: "speculateservicename", - title: "speculateServiceName()", - relativePath: "docs/cloudrun/speculateservicename.mdx", - compId: "articles-docs-cloudrun-speculateservicename", - crumb: "Cloud Run API", + id: "terminology/studio", + title: "Remotion Studio", + relativePath: "docs/terminology/studio.mdx", + compId: "articles-docs-terminology-studio", + crumb: "Terminology", }, { - id: "testpermissions", - title: "testPermissions()", - relativePath: "docs/cloudrun/testpermissions.mdx", - compId: "articles-docs-cloudrun-testpermissions", - crumb: "Cloud Run API", + id: "terminology/bundle", + title: "Remotion Bundle", + relativePath: "docs/terminology/bundle.mdx", + compId: "articles-docs-terminology-bundle", + crumb: "Terminology", }, { - id: "uninstall", - title: "Uninstall Cloud Run", - relativePath: "docs/cloudrun/uninstall.mdx", - compId: "articles-docs-cloudrun-uninstall", - crumb: null, + id: "terminology/sequence", + title: "Sequence", + relativePath: "docs/terminology/sequence.mdx", + compId: "articles-docs-terminology-sequence", + crumb: "Terminology", }, { - id: "upgrading", - title: "Upgrading Cloud Run", - relativePath: "docs/cloudrun/upgrading.mdx", - compId: "articles-docs-cloudrun-upgrading", - crumb: null, + id: "terminology/concurrency", + title: "Concurrency", + relativePath: "docs/terminology/concurrency.mdx", + compId: "articles-docs-terminology-concurrency", + crumb: "Terminology", }, { - id: "cloudrun-alpha", - title: "Cloud Run Alpha", - relativePath: "docs/cloudrun-alpha.mdx", - compId: "articles-docs-cloudrun-alpha", - crumb: "Version Upgrade", + id: "terminology/entry-point", + title: "Entry point", + relativePath: "docs/terminology/entry-point.mdx", + compId: "articles-docs-terminology-entry-point", + crumb: "Terminology", }, { - id: "cloudrun", - title: "@remotion/cloudrun", - relativePath: "docs/cloudrun.mdx", - compId: "articles-docs-cloudrun", - crumb: null, + id: "terminology/player", + title: "Remotion Player", + relativePath: "docs/terminology/player.mdx", + compId: "articles-docs-terminology-player", + crumb: "Terminology", }, { - id: "compare/motion-canvas", - title: "How does Remotion compare to Motion Canvas?", - relativePath: "docs/compare/motion-canvas.mdx", - compId: "articles-docs-compare-motion-canvas", - crumb: "FAQ", + id: "terminology/cloud-run-url", + title: "Cloud Run URL", + relativePath: "docs/terminology/cloud-run-url.mdx", + compId: "articles-docs-terminology-cloud-run-url", + crumb: "Terminology", }, { - id: "composition", - title: "<Composition>", - relativePath: "docs/composition.mdx", - compId: "articles-docs-composition", - crumb: "API", + id: "terminology/input-props", + title: "Input Props", + relativePath: "docs/terminology/input-props.mdx", + compId: "articles-docs-terminology-input-props", + crumb: "Terminology", }, { - id: "config", - title: "Configuration file", - relativePath: "docs/config.mdx", - compId: "articles-docs-config", - crumb: "remotion.config.ts", + id: "terminology/public-dir", + title: "Public directory", + relativePath: "docs/terminology/public-dir.mdx", + compId: "articles-docs-terminology-public-dir", + crumb: "Terminology", }, { - id: "continue-render", - title: "continueRender()", - relativePath: "docs/continue-render.mdx", - compId: "articles-docs-continue-render", - crumb: "API", + id: "terminology/root-file", + title: "Root file", + relativePath: "docs/terminology/root-file.mdx", + compId: "articles-docs-terminology-root-file", + crumb: "Terminology", }, { - id: "contributing/bounty", - title: "Take and solve bounty issues", - relativePath: "docs/contributing/bounty.mdx", - compId: "articles-docs-contributing-bounty", - crumb: "Contributing", + id: "terminology/service-name", + title: "Service Name", + relativePath: "docs/terminology/service-name.mdx", + compId: "articles-docs-terminology-service-name", + crumb: "Terminology", }, { - id: "contributing/docs", - title: "Contributing to the documentation", - relativePath: "docs/contributing/docs.mdx", - compId: "articles-docs-contributing-docs", - crumb: "Contributing", + id: "terminology/serve-url", + title: "Serve URL", + relativePath: "docs/terminology/serve-url.mdx", + compId: "articles-docs-terminology-serve-url", + crumb: "Terminology", }, { - id: "contributing/feature", - title: "Implementing a new feature", - relativePath: "docs/contributing/feature.mdx", - compId: "articles-docs-contributing-feature", - crumb: "Contributing", + id: "terminology/composition", + title: "Composition", + relativePath: "docs/terminology/composition.mdx", + compId: "articles-docs-terminology-composition", + crumb: "Terminology", }, { - id: "contributing/formatting", - title: "Formatting in the Remotion repo", - relativePath: "docs/contributing/formatting.mdx", - compId: "articles-docs-contributing-formatting", - crumb: "Contributing", + id: "terminology/remotion-root", + title: "Remotion Root", + relativePath: "docs/terminology/remotion-root.mdx", + compId: "articles-docs-terminology-remotion-root", + crumb: "Terminology", }, { - id: "contributing/index", - title: "Contributing to Remotion", - relativePath: "docs/contributing/index.mdx", - compId: "articles-docs-contributing-index", - crumb: "How to be awesome", + id: "motion-blur/camera-motion-blur", + title: "<CameraMotionBlur>", + relativePath: "docs/motion-blur/camera-motion-blur.mdx", + compId: "articles-docs-motion-blur-camera-motion-blur", + crumb: "Realistic camera effect", }, { - id: "contributing/ineligible", - title: "Ineligible for bounties", - relativePath: "docs/contributing/ineligible.mdx", - compId: "articles-docs-contributing-ineligible", - crumb: "Contributing", + id: "motion-blur/index", + title: "@remotion/motion-blur", + relativePath: "docs/motion-blur/index.mdx", + compId: "articles-docs-motion-blur-index", + crumb: null, }, { - id: "contributing/option", - title: "Implementing a new option", - relativePath: "docs/contributing/option.mdx", - compId: "articles-docs-contributing-option", - crumb: "Contributing", + id: "motion-blur/motion-blur", + title: "<MotionBlur>", + relativePath: "docs/motion-blur/motion-blur.mdx", + compId: "articles-docs-motion-blur-motion-blur", + crumb: null, }, { - id: "contributing/presentation", - title: "Contribute your own presentation", - relativePath: "docs/contributing/presentation.mdx", - compId: "articles-docs-contributing-presentation", - crumb: "Contributing", + id: "motion-blur/trail", + title: "<Trail>", + relativePath: "docs/motion-blur/trail.mdx", + compId: "articles-docs-motion-blur-trail", + crumb: "Motion blur", }, { - id: "contributing/rust", - title: "Contributing Rust code", - relativePath: "docs/contributing/rust.mdx", - compId: "articles-docs-contributing-rust", - crumb: "Contributing", + id: "freeze", + title: "<Freeze>", + relativePath: "docs/freeze.mdx", + compId: "articles-docs-freeze", + crumb: "API", }, { - id: "authoring-packages", - title: "Authoring a Remotion library", - relativePath: "docs/creating-a-library.mdx", - compId: "articles-docs-creating-a-library", - crumb: "How to", + id: "terminology", + title: "Terminology", + relativePath: "docs/terminology.mdx", + compId: "articles-docs-terminology", + crumb: "The Remotion dictionary", }, { id: "data-fetching", @@ -644,95 +609,18 @@ export const articles = [ crumb: "How to", }, { - id: "dataset-render", - title: "Render videos programmatically from a dataset", - relativePath: "docs/dataset-render.mdx", - compId: "articles-docs-dataset-render", - crumb: "Tutorials", - }, - { - id: "delay-render", - title: "delayRender() and continueRender()", - relativePath: "docs/delay-render.mdx", - compId: "articles-docs-delay-render", - crumb: "How to", - }, - { - id: "docker", - title: "Dockerizing a Remotion app", - relativePath: "docs/docker.mdx", - compId: "articles-docs-docker", - crumb: "Building video apps", - }, - { - id: "webpack-dynamic-imports", - title: "Webpack dynamic imports", - relativePath: "docs/dynamic-import.mdx", - compId: "articles-docs-dynamic-import", - crumb: "Knowledge Base", - }, - { - id: "dynamic-metadata", - title: "Variable duration and dimensions", - relativePath: "docs/dynamic-metadata.mdx", - compId: "articles-docs-dynamic-metadata", - crumb: "How To", - }, - { - id: "easing", - title: "Easing", - relativePath: "docs/easing.mdx", - compId: "articles-docs-easing", - crumb: "API", - }, - { - id: "enable-scss/enable-scss", - title: "enableScss()", - relativePath: "docs/enable-scss/enable-scss.mdx", - compId: "articles-docs-enable-scss-enable-scss", - crumb: "@remotion/enable-scss", - }, - { - id: "enable-scss/overview", - title: "@remotion/enable-scss", - relativePath: "docs/enable-scss/overview.mdx", - compId: "articles-docs-enable-scss-overview", - crumb: null, - }, - { - id: "enametoolong", - title: "ENAMETOOLONG", - relativePath: "docs/enametoolong.mdx", - compId: "articles-docs-enametoolong", - crumb: "Troubleshooting", - }, - { - id: "encoding", - title: "Encoding Guide", - relativePath: "docs/encoding.mdx", - compId: "articles-docs-encoding", - crumb: "Codecs and more", - }, - { - id: "env-variables", - title: "Environment variables", - relativePath: "docs/env-variables.mdx", - compId: "articles-docs-env-variables", - crumb: "How To", - }, - { - id: "ffmpeg", - title: "Installing FFmpeg", - relativePath: "docs/ffmpeg.mdx", - compId: "articles-docs-ffmpeg", - crumb: "(you don't have to)", + id: "target-closed", + title: "'Target closed' error message", + relativePath: "docs/target-closed.mdx", + compId: "articles-docs-target-closed", + crumb: "Troubleshooting", }, { - id: "figma", - title: "Import from Figma", - relativePath: "docs/figma.mdx", - compId: "articles-docs-figma", - crumb: "The best of both", + id: "wrong-composition-mount", + title: "Wrongly mounted <Composition>", + relativePath: "docs/wrong-composition-mount.mdx", + compId: "articles-docs-wrong-composition-mount", + crumb: "Troubleshooting", }, { id: "flickering", @@ -742,213 +630,199 @@ export const articles = [ crumb: "How to avoid", }, { - id: "folder", - title: "<Folder>", - relativePath: "docs/folder.mdx", - compId: "articles-docs-folder", - crumb: "API", - }, - { - id: "font-picker", - title: "Build a Google Font picker", - relativePath: "docs/font-picker.mdx", - compId: "articles-docs-font-picker", - crumb: "Building video apps", - }, - { - id: "fonts-api", - title: "@remotion/fonts", - relativePath: "docs/fonts-api/index.mdx", - compId: "articles-docs-fonts-api-index", - crumb: null, + id: "contributing/ineligible", + title: "Ineligible for bounties", + relativePath: "docs/contributing/ineligible.mdx", + compId: "articles-docs-contributing-ineligible", + crumb: "Contributing", }, { - id: "fonts-api/load-font", - title: "loadFont()", - relativePath: "docs/fonts-api/load-font.mdx", - compId: "articles-docs-fonts-api-load-font", - crumb: "@remotion/fonts", + id: "contributing/bounty", + title: "Take and solve bounty issues", + relativePath: "docs/contributing/bounty.mdx", + compId: "articles-docs-contributing-bounty", + crumb: "Contributing", }, { - id: "fonts", - title: "Using fonts", - relativePath: "docs/fonts.mdx", - compId: "articles-docs-fonts", - crumb: "Techniques", + id: "contributing/rust", + title: "Contributing Rust code", + relativePath: "docs/contributing/rust.mdx", + compId: "articles-docs-contributing-rust", + crumb: "Contributing", }, { - id: "freeze", - title: "<Freeze>", - relativePath: "docs/freeze.mdx", - compId: "articles-docs-freeze", - crumb: "API", + id: "contributing/option", + title: "Implementing a new option", + relativePath: "docs/contributing/option.mdx", + compId: "articles-docs-contributing-option", + crumb: "Contributing", }, { - id: "get-audio-data", - title: "getAudioData()", - relativePath: "docs/get-audio-data.mdx", - compId: "articles-docs-get-audio-data", - crumb: "@remotion/media-utils", + id: "contributing/presentation", + title: "Contribute your own presentation", + relativePath: "docs/contributing/presentation.mdx", + compId: "articles-docs-contributing-presentation", + crumb: "Contributing", }, { - id: "get-audio-duration-in-seconds", - title: "getAudioDurationInSeconds()", - relativePath: "docs/get-audio-duration-in-seconds.mdx", - compId: "articles-docs-get-audio-duration-in-seconds", - crumb: "@remotion/media-utils", + id: "contributing/feature", + title: "Implementing a new feature", + relativePath: "docs/contributing/feature.mdx", + compId: "articles-docs-contributing-feature", + crumb: "Contributing", }, { - id: "get-image-dimensions", - title: "getImageDimensions()", - relativePath: "docs/get-image-dimensions.mdx", - compId: "articles-docs-get-image-dimensions", - crumb: "@remotion/media-utils", + id: "contributing/index", + title: "Contributing to Remotion", + relativePath: "docs/contributing/index.mdx", + compId: "articles-docs-contributing-index", + crumb: "How to be awesome", }, { - id: "get-input-props", - title: "getInputProps()", - relativePath: "docs/get-input-props.mdx", - compId: "articles-docs-get-input-props", - crumb: "API", + id: "contributing/formatting", + title: "Formatting in the Remotion repo", + relativePath: "docs/contributing/formatting.mdx", + compId: "articles-docs-contributing-formatting", + crumb: "Contributing", }, { - id: "get-remotion-environment", - title: "getRemotionEnvironment()", - relativePath: "docs/get-remotion-environment.mdx", - compId: "articles-docs-get-remotion-environment", - crumb: "API", + id: "contributing/docs", + title: "Contributing to the documentation", + relativePath: "docs/contributing/docs.mdx", + compId: "articles-docs-contributing-docs", + crumb: "Contributing", }, { - id: "getstaticfiles", - title: "getStaticFiles()", - relativePath: "docs/get-static-files.mdx", - compId: "articles-docs-get-static-files", - crumb: "API", + id: "third-party", + title: "Third party integrations", + relativePath: "docs/third-party.mdx", + compId: "articles-docs-third-party", + crumb: "Integrations", }, { - id: "get-video-metadata", - title: "getVideoMetadata()", - relativePath: "docs/get-video-metadata.mdx", - compId: "articles-docs-get-video-metadata", - crumb: "@remotion/media-utils", + id: "tailwind", + title: "TailwindCSS", + relativePath: "docs/tailwind.mdx", + compId: "articles-docs-tailwind", + crumb: "text-lg font-bold", }, { - id: "get-waveform-portion", - title: "getWaveformPortion()", - relativePath: "docs/get-waveform-portion.mdx", - compId: "articles-docs-get-waveform-portion", + id: "get-image-dimensions", + title: "getImageDimensions()", + relativePath: "docs/get-image-dimensions.mdx", + compId: "articles-docs-get-image-dimensions", crumb: "@remotion/media-utils", }, { - id: "getting-started", - title: "Creating a new project", - relativePath: "docs/getting-started.mdx", - compId: "articles-docs-getting-started", - crumb: "Let's begin!", - }, - { - id: "get-gif-duration-in-seconds", - title: "getGifDurationInSeconds()", - relativePath: "docs/gif/get-gif-duration-in-seconds.mdx", - compId: "articles-docs-gif-get-gif-duration-in-seconds", - crumb: "@remotion/gif", + id: "cancel-render", + title: "cancelRender()", + relativePath: "docs/cancel-render.mdx", + compId: "articles-docs-cancel-render", + crumb: "How to", }, { - id: "gif/gif", - title: "<Gif>", - relativePath: "docs/gif/gif.mdx", - compId: "articles-docs-gif-gif", - crumb: "@remotion/gif", + id: "spline", + title: "Import from Spline", + relativePath: "docs/spline.mdx", + compId: "articles-docs-spline", + crumb: "Integrations", }, { - id: "gif/index", - title: "@remotion/gif", - relativePath: "docs/gif/index.mdx", - compId: "articles-docs-gif-index", - crumb: null, + id: "use-video-texture", + title: "useVideoTexture()", + relativePath: "docs/use-video-texture.mdx", + compId: "articles-docs-use-video-texture", + crumb: "@remotion/three", }, { - id: "gif/preload-gif", - title: "preloadGif()", - relativePath: "docs/gif/preload-gif.mdx", - compId: "articles-docs-gif-preload-gif", - crumb: "@remotion/gif", + id: "gpu", + title: "Using the GPU", + relativePath: "docs/gpu.mdx", + compId: "articles-docs-gpu", + crumb: "Need for Speed", }, { - id: "google-fonts/get-available-fonts", - title: "getAvailableFonts()", - relativePath: "docs/google-fonts/get-available-fonts.mdx", - compId: "articles-docs-google-fonts-get-available-fonts", - crumb: "@remotion/google-fonts", + id: "timeout", + title: "Debugging timeouts", + relativePath: "docs/timeout.mdx", + compId: "articles-docs-timeout", + crumb: "Troubleshooting", }, { - id: "google-fonts/get-info", - title: "getInfo()", - relativePath: "docs/google-fonts/get-info.mdx", - compId: "articles-docs-google-fonts-get-info", - crumb: "@remotion/google-fonts", + id: "renderer", + title: "@remotion/renderer", + relativePath: "docs/renderer.mdx", + compId: "articles-docs-renderer", + crumb: null, }, { - id: "google-fonts", - title: "@remotion/google-fonts", - relativePath: "docs/google-fonts/index.mdx", - compId: "articles-docs-google-fonts-index", + id: "three", + title: "@remotion/three", + relativePath: "docs/three.mdx", + compId: "articles-docs-three", crumb: null, }, { - id: "google-fonts/load-font", - title: "loadFont()", - relativePath: "docs/google-fonts/load-font.mdx", - compId: "articles-docs-google-fonts-load-font", - crumb: "@remotion/google-fonts", + id: "absolute-fill", + title: "<AbsoluteFill>", + relativePath: "docs/absolute-fill.mdx", + compId: "articles-docs-absolute-fill", + crumb: "API", }, { - id: "gpu", - title: "Using the GPU", - relativePath: "docs/gpu.mdx", - compId: "articles-docs-gpu", - crumb: "Need for Speed", + id: "offthreadvideo", + title: "<OffthreadVideo>", + relativePath: "docs/offthreadvideo.mdx", + compId: "articles-docs-offthreadvideo", + crumb: "API", }, { - id: "props-resolution", - title: "How props get resolved", - relativePath: "docs/how-props-flow.mdx", - compId: "articles-docs-how-props-flow", - crumb: "Parameterized videos", + id: "dynamic-metadata", + title: "Variable duration and dimensions", + relativePath: "docs/dynamic-metadata.mdx", + compId: "articles-docs-dynamic-metadata", + crumb: "How To", }, { - id: "iframe", - title: "<IFrame>", - relativePath: "docs/iframe.mdx", - compId: "articles-docs-iframe", - crumb: "API", + id: "zod-types/z-textarea", + title: "zTextarea()", + relativePath: "docs/zod-types/z-textarea.mdx", + compId: "articles-docs-zod-types-z-textarea", + crumb: null, }, { - id: "img", - title: "<Img>", - relativePath: "docs/img.mdx", - compId: "articles-docs-img", - crumb: "API", + id: "zod-types/z-color", + title: "zColor()", + relativePath: "docs/zod-types/z-color.mdx", + compId: "articles-docs-zod-types-z-color", + crumb: null, }, { - id: "assets", - title: "Importing assets", - relativePath: "docs/importing-assets.mdx", - compId: "articles-docs-importing-assets", - crumb: "How To", + id: "zod-types/index", + title: "@remotion/zod-types", + relativePath: "docs/zod-types/index.mdx", + compId: "articles-docs-zod-types-index", + crumb: "Schema", }, { - id: "install-whisper-cpp/convert-to-captions", - title: "convertToCaptions()", - relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx", - compId: "articles-docs-install-whisper-cpp-convert-to-captions", - crumb: "@remotion/install-whisper-cpp", + id: "non-seekable-media", + title: "Non-seekable media", + relativePath: "docs/non-seekable-media.mdx", + compId: "articles-docs-non-seekable-media", + crumb: "Troubleshooting", }, { - id: "install-whisper-cpp/download-whisper-model", - title: "downloadWhisperModel()", - relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx", - compId: "articles-docs-install-whisper-cpp-download-whisper-model", + id: "docker", + title: "Dockerizing a Remotion app", + relativePath: "docs/docker.mdx", + compId: "articles-docs-docker", + crumb: "Building video apps", + }, + { + id: "install-whisper-cpp/install-whisper-cpp", + title: "installWhisperCpp()", + relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx", + compId: "articles-docs-install-whisper-cpp-install-whisper-cpp", crumb: "@remotion/install-whisper-cpp", }, { @@ -958,13 +832,6 @@ export const articles = [ compId: "articles-docs-install-whisper-cpp-index", crumb: "Transcribe audio locally", }, - { - id: "install-whisper-cpp/install-whisper-cpp", - title: "installWhisperCpp()", - relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx", - compId: "articles-docs-install-whisper-cpp-install-whisper-cpp", - crumb: "@remotion/install-whisper-cpp", - }, { id: "install-whisper-cpp/transcribe", title: "transcribe()", @@ -973,710 +840,706 @@ export const articles = [ crumb: "@remotion/install-whisper-cpp", }, { - id: "interpolate-colors", - title: "interpolateColors()", - relativePath: "docs/interpolate-colors.mdx", - compId: "articles-docs-interpolate-colors", - crumb: "API", - }, - { - id: "interpolate", - title: "interpolate()", - relativePath: "docs/interpolate.mdx", - compId: "articles-docs-interpolate", - crumb: "API", + id: "install-whisper-cpp/convert-to-captions", + title: "convertToCaptions()", + relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx", + compId: "articles-docs-install-whisper-cpp-convert-to-captions", + crumb: "@remotion/install-whisper-cpp", }, { - id: "javascript", - title: "Plain JavaScript", - relativePath: "docs/jsx-support.mdx", - compId: "articles-docs-jsx-support", - crumb: "How To", + id: "install-whisper-cpp/download-whisper-model", + title: "downloadWhisperModel()", + relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx", + compId: "articles-docs-install-whisper-cpp-download-whisper-model", + crumb: "@remotion/install-whisper-cpp", }, { - id: "lambda/api", - title: "@remotion/lambda", - relativePath: "docs/lambda/api.mdx", - compId: "articles-docs-lambda-api", - crumb: "Render videos without servers on AWS", + id: "get-audio-duration-in-seconds", + title: "getAudioDurationInSeconds()", + relativePath: "docs/get-audio-duration-in-seconds.mdx", + compId: "articles-docs-get-audio-duration-in-seconds", + crumb: "@remotion/media-utils", }, { - id: "authentication", - title: "Authentication", - relativePath: "docs/lambda/authentication.mdx", - compId: "articles-docs-lambda-authentication", - crumb: "Lambda", + id: "dataset-render", + title: "Render videos programmatically from a dataset", + relativePath: "docs/dataset-render.mdx", + compId: "articles-docs-dataset-render", + crumb: "Tutorials", }, { - id: "autodelete", - title: "Auto-delete renders", - relativePath: "docs/lambda/autodelete.mdx", - compId: "articles-docs-lambda-autodelete", - crumb: "Lambda API", + id: "prefetch", + title: "prefetch()", + relativePath: "docs/prefetch.mdx", + compId: "articles-docs-prefetch", + crumb: "API", }, { - id: "lambda/bucket-naming", - title: "Bucket names in Remotion Lambda", - relativePath: "docs/lambda/bucket-naming.mdx", - compId: "articles-docs-lambda-bucket-naming", - crumb: "@remotion/lambda", + id: "audio-buffer-to-data-url", + title: "audioBufferToDataUrl()", + relativePath: "docs/audiobuffertodataurl.mdx", + compId: "articles-docs-audiobuffertodataurl", + crumb: "@remotion/media-utils", }, { - id: "changelog", - title: "Prerelease Changelog", - relativePath: "docs/lambda/changelog.mdx", - compId: "articles-docs-lambda-changelog", - crumb: null, + id: "animating-properties", + title: "Animating properties", + relativePath: "docs/animating-properties.mdx", + compId: "articles-docs-animating-properties", + crumb: "The basics", }, { - id: "checklist", - title: "Production Checklist", - relativePath: "docs/lambda/checklist.mdx", - compId: "articles-docs-lambda-checklist", - crumb: "Lambda", + id: "getstaticfiles", + title: "getStaticFiles()", + relativePath: "docs/get-static-files.mdx", + compId: "articles-docs-get-static-files", + crumb: "API", }, { - id: "lambda/cli/compositions", - title: "npx remotion lambda compositions", - relativePath: "docs/lambda/cli/compositions.mdx", - compId: "articles-docs-lambda-cli-compositions", - crumb: "Lambda CLI Reference", + id: "paths/reverse-path", + title: "reversePath()", + relativePath: "docs/paths/reverse-path.mdx", + compId: "articles-docs-paths-reverse-path", + crumb: "@remotion/paths", }, { - id: "functions", - title: "npx remotion lambda functions", - relativePath: "docs/lambda/cli/functions.mdx", - compId: "articles-docs-lambda-cli-functions", - crumb: "Lambda CLI Reference", + id: "paths/evolve-path", + title: "evolvePath()", + relativePath: "docs/paths/evolve-path.mdx", + compId: "articles-docs-paths-evolve-path", + crumb: "@remotion/paths", }, { - id: "policies", - title: "npx remotion lambda policies", - relativePath: "docs/lambda/cli/policies.mdx", - compId: "articles-docs-lambda-cli-policies", - crumb: "Lambda CLI Reference", + id: "paths/get-subpaths", + title: "getSubpaths()", + relativePath: "docs/paths/get-subpaths.mdx", + compId: "articles-docs-paths-get-subpaths", + crumb: "@remotion/paths", }, { - id: "quotas", - title: "npx remotion lambda quotas", - relativePath: "docs/lambda/cli/quotas.mdx", - compId: "articles-docs-lambda-cli-quotas", - crumb: "Lambda CLI Reference", + id: "paths/get-point-at-length", + title: "getPointAtLength()", + relativePath: "docs/paths/get-point-at-length.mdx", + compId: "articles-docs-paths-get-point-at-length", + crumb: "@remotion/paths", }, { - id: "regions", - title: "npx remotion lambda regions", - relativePath: "docs/lambda/cli/regions.mdx", - compId: "articles-docs-lambda-cli-regions", - crumb: "Lambda CLI Reference", + id: "paths/get-length", + title: "getLength()", + relativePath: "docs/paths/get-length.mdx", + compId: "articles-docs-paths-get-length", + crumb: "@remotion/paths", }, { - id: "render", - title: "npx remotion lambda render", - relativePath: "docs/lambda/cli/render.mdx", - compId: "articles-docs-lambda-cli-render", - crumb: "Lambda CLI Reference", + id: "paths/scale-path", + title: "scalePath()", + relativePath: "docs/paths/scale-path.mdx", + compId: "articles-docs-paths-scale-path", + crumb: "@remotion/paths", }, { - id: "sites", - title: "npx remotion lambda sites", - relativePath: "docs/lambda/cli/sites.mdx", - compId: "articles-docs-lambda-cli-sites", - crumb: "Lambda CLI Reference", + id: "paths/get-parts", + title: "getParts()", + relativePath: "docs/paths/get-parts.mdx", + compId: "articles-docs-paths-get-parts", + crumb: "@remotion/paths", }, { - id: "still", - title: "npx remotion lambda still", - relativePath: "docs/lambda/cli/still.mdx", - compId: "articles-docs-lambda-cli-still", - crumb: "Lambda CLI Reference", + id: "paths/get-tangent-at-length", + title: "getTangentAtLength()", + relativePath: "docs/paths/get-tangent-at-length.mdx", + compId: "articles-docs-paths-get-tangent-at-length", + crumb: "@remotion/paths", }, { - id: "cli", - title: "@remotion/lambda - CLI", - relativePath: "docs/lambda/cli.mdx", - compId: "articles-docs-lambda-cli", - crumb: null, + id: "paths/get-instruction-index-at-length", + title: "getInstructionIndexAtLength()", + relativePath: "docs/paths/get-instruction-index-at-length.mdx", + compId: "articles-docs-paths-get-instruction-index-at-length", + crumb: "@remotion/paths", }, { - id: "concurrency", - title: "Concurrency", - relativePath: "docs/lambda/concurrency.mdx", - compId: "articles-docs-lambda-concurrency", - crumb: "Lambda", + id: "paths/extend-viewbox", + title: "extendViewBox()", + relativePath: "docs/paths/extend-viewbox.mdx", + compId: "articles-docs-paths-extend-viewbox", + crumb: "@remotion/paths", }, - { - id: "optimizing-cost", - title: "Optimizing for cost", - relativePath: "docs/lambda/cost.mdx", - compId: "articles-docs-lambda-cost", - crumb: "Lambda", + { + id: "paths/parse-path", + title: "parsePath()", + relativePath: "docs/paths/parse-path.mdx", + compId: "articles-docs-paths-parse-path", + crumb: "@remotion/paths", }, { - id: "custom-destination", - title: "Customizing Lambda output destination", - relativePath: "docs/lambda/custom-destination.mdx", - compId: "articles-docs-lambda-custom-destination", - crumb: "Lambda", + id: "paths/translate-path", + title: "translatePath()", + relativePath: "docs/paths/translate-path.mdx", + compId: "articles-docs-paths-translate-path", + crumb: "@remotion/paths", }, { - id: "lambda/custom-layers", - title: "Custom Layers", - relativePath: "docs/lambda/custom-layers.mdx", - compId: "articles-docs-lambda-custom-layers", - crumb: "Lambda", + id: "paths/normalize-path", + title: "normalizePath()", + relativePath: "docs/paths/normalize-path.mdx", + compId: "articles-docs-paths-normalize-path", + crumb: "@remotion/paths", }, { - id: "deletefunction", - title: "deleteFunction()", - relativePath: "docs/lambda/deletefunction.mdx", - compId: "articles-docs-lambda-deletefunction", - crumb: "Lambda API", + id: "paths/index", + title: "@remotion/paths", + relativePath: "docs/paths/index.mdx", + compId: "articles-docs-paths-index", + crumb: "SVG", }, { - id: "deleterender", - title: "deleteRender()", - relativePath: "docs/lambda/deleterender.mdx", - compId: "articles-docs-lambda-deleterender", - crumb: "Lambda API", + id: "paths/get-bounding-box", + title: "getBoundingBox()", + relativePath: "docs/paths/get-bounding-box.mdx", + compId: "articles-docs-paths-get-bounding-box", + crumb: "@remotion/paths", }, { - id: "deletesite", - title: "deleteSite()", - relativePath: "docs/lambda/deletesite.mdx", - compId: "articles-docs-lambda-deletesite", - crumb: "Lambda API", + id: "paths/warp-path", + title: "warpPath()", + relativePath: "docs/paths/warp-path.mdx", + compId: "articles-docs-paths-warp-path", + crumb: "@remotion/paths", }, { - id: "deployfunction", - title: "deployFunction()", - relativePath: "docs/lambda/deployfunction.mdx", - compId: "articles-docs-lambda-deployfunction", - crumb: "Lambda API", + id: "paths/interpolate-path", + title: "interpolatePath()", + relativePath: "docs/paths/interpolate-path.mdx", + compId: "articles-docs-paths-interpolate-path", + crumb: "@remotion/paths", }, { - id: "deploysite", - title: "deploySite()", - relativePath: "docs/lambda/deploysite.mdx", - compId: "articles-docs-lambda-deploysite", - crumb: "Lambda API", + id: "paths/serialize-instructions", + title: "serializeInstructions()", + relativePath: "docs/paths/serialize-instructions.mdx", + compId: "articles-docs-paths-serialize-instructions", + crumb: "@remotion/paths", }, { - id: "disk-size", - title: "Disk size", - relativePath: "docs/lambda/disk-size.mdx", - compId: "articles-docs-lambda-disk-size", - crumb: "Lambda", + id: "paths/reset-path", + title: "resetPath()", + relativePath: "docs/paths/reset-path.mdx", + compId: "articles-docs-paths-reset-path", + crumb: "@remotion/paths", }, { - id: "downloadmedia", - title: "downloadMedia()", - relativePath: "docs/lambda/downloadmedia.mdx", - compId: "articles-docs-lambda-downloadmedia", - crumb: "Lambda API", + id: "paths/reduce-instructions", + title: "reduceInstructions()", + relativePath: "docs/paths/reduce-instructions.mdx", + compId: "articles-docs-paths-reduce-instructions", + crumb: "@remotion/paths", }, { - id: "downloadvideo", - title: "downloadVideo()", - relativePath: "docs/lambda/downloadvideo.mdx", - compId: "articles-docs-lambda-downloadvideo", - crumb: "Lambda API", + id: "rive/index", + title: "@remotion/rive", + relativePath: "docs/rive/index.mdx", + compId: "articles-docs-rive-index", + crumb: "Integrations", }, { - id: "estimateprice", - title: "estimatePrice()", - relativePath: "docs/lambda/estimateprice.mdx", - compId: "articles-docs-lambda-estimateprice", - crumb: "Lambda API", + id: "rive/remotionrivecanvas", + title: "<RemotionRiveCanvas>", + relativePath: "docs/rive/remotionrivecanvas.mdx", + compId: "articles-docs-rive-remotionrivecanvas", + crumb: "@remotion/rive", }, { - id: "lambda/faq", - title: "FAQ", - relativePath: "docs/lambda/faq.mdx", - compId: "articles-docs-lambda-faq", - crumb: "Lambda", + id: "layers", + title: "Layers", + relativePath: "docs/layers.mdx", + compId: "articles-docs-layers", + crumb: "Designing videos", }, { - id: "feb-2022-outage", - title: "February 2022 Outage", - relativePath: "docs/lambda/feb-2022-outage.mdx", - compId: "articles-docs-lambda-feb-2022-outage", - crumb: null, + id: "use-img-and-iframe", + title: "<Img>, <Video> and <Audio>", + relativePath: "docs/use-img-and-iframe.mdx", + compId: "articles-docs-use-img-and-iframe", + crumb: "Best practices", }, { - id: "lambda/feb-2023-incident", - title: "Upgrade your Lambda functions to prevent breakage", - relativePath: "docs/lambda/feb-2023-incident.mdx", - compId: "articles-docs-lambda-feb-2023-incident", - crumb: "DevOps advisory", + id: "font-picker", + title: "Build a Google Font picker", + relativePath: "docs/font-picker.mdx", + compId: "articles-docs-font-picker", + crumb: "Building video apps", }, { - id: "getawsclient", - title: "getAwsClient()", - relativePath: "docs/lambda/getawsclient.mdx", - compId: "articles-docs-lambda-getawsclient", - crumb: "Lambda API", + id: "support", + title: "Support Policy", + relativePath: "docs/support.mdx", + compId: "articles-docs-support", + crumb: "Let us help you", }, { - id: "getcompositionsonlambda", - title: "getCompositionsOnLambda()", - relativePath: "docs/lambda/getcompositionsonlambda.mdx", - compId: "articles-docs-lambda-getcompositionsonlambda", - crumb: "Lambda API", + id: "register-root", + title: "registerRoot()", + relativePath: "docs/register-root.mdx", + compId: "articles-docs-register-root", + crumb: "API", }, { - id: "getfunctioninfo", - title: "getFunctionInfo()", - relativePath: "docs/lambda/getfunctioninfo.mdx", - compId: "articles-docs-lambda-getfunctioninfo", - crumb: "Lambda API", + id: "props-resolution", + title: "How props get resolved", + relativePath: "docs/how-props-flow.mdx", + compId: "articles-docs-how-props-flow", + crumb: "Parameterized videos", }, { - id: "getfunctions", - title: "getFunctions()", - relativePath: "docs/lambda/getfunctions.mdx", - compId: "articles-docs-lambda-getfunctions", - crumb: "Lambda API", + id: "preview", + title: "Preview your video", + relativePath: "docs/preview.mdx", + compId: "articles-docs-preview", + crumb: "Timeline basics", }, { - id: "getorcreatebucket", - title: "getOrCreateBucket()", - relativePath: "docs/lambda/getorcreatebucket.mdx", - compId: "articles-docs-lambda-getorcreatebucket", - crumb: "Lambda API", + id: "shapes/triangle", + title: "<Triangle />", + relativePath: "docs/shapes/triangle.mdx", + compId: "articles-docs-shapes-triangle", + crumb: "@remotion/shapes", }, { - id: "getregions", - title: "getRegions()", - relativePath: "docs/lambda/getregions.mdx", - compId: "articles-docs-lambda-getregions", - crumb: "Lambda API", + id: "shapes/rect", + title: "<Rect />", + relativePath: "docs/shapes/rect.mdx", + compId: "articles-docs-shapes-rect", + crumb: "@remotion/shapes", }, { - id: "getrenderprogress", - title: "getRenderProgress()", - relativePath: "docs/lambda/getrenderprogress.mdx", - compId: "articles-docs-lambda-getrenderprogress", - crumb: "Lambda API", + id: "shapes/polygon", + title: "<Polygon />", + relativePath: "docs/shapes/polygon.mdx", + compId: "articles-docs-shapes-polygon", + crumb: "@remotion/shapes", }, { - id: "getrolepolicy", - title: "getRolePolicy()", - relativePath: "docs/lambda/getrolepolicy.mdx", - compId: "articles-docs-lambda-getrolepolicy", - crumb: "Lambda API", + id: "shapes/circle", + title: "<Circle />", + relativePath: "docs/shapes/circle.mdx", + compId: "articles-docs-shapes-circle", + crumb: "@remotion/shapes", }, { - id: "getsites", - title: "getSites()", - relativePath: "docs/lambda/getsites.mdx", - compId: "articles-docs-lambda-getsites", - crumb: "Lambda API", + id: "shapes/make-rect", + title: "makeRect()", + relativePath: "docs/shapes/make-rect.mdx", + compId: "articles-docs-shapes-make-rect", + crumb: "@remotion/shapes", }, { - id: "getuserpolicy", - title: "getUserPolicy()", - relativePath: "docs/lambda/getuserpolicy.mdx", - compId: "articles-docs-lambda-getuserpolicy", - crumb: "Lambda API", + id: "shapes/index", + title: "@remotion/shapes", + relativePath: "docs/shapes/index.mdx", + compId: "articles-docs-shapes-index", + crumb: "SVG component library", }, { - id: "lambda/go", - title: "Triggering renders from Go", - relativePath: "docs/lambda/go.mdx", - compId: "articles-docs-lambda-go", - crumb: "@remotion/lambda", + id: "shapes/ellipse", + title: "<Ellipse />", + relativePath: "docs/shapes/ellipse.mdx", + compId: "articles-docs-shapes-ellipse", + crumb: "@remotion/shapes", }, { - id: "how-lambda-works", - title: "How Remotion Lambda works", - relativePath: "docs/lambda/how-lambda-works.mdx", - compId: "articles-docs-lambda-how-lambda-works", - crumb: "Lambda", + id: "shapes/make-triangle", + title: "makeTriangle()", + relativePath: "docs/shapes/make-triangle.mdx", + compId: "articles-docs-shapes-make-triangle", + crumb: "@remotion/shapes", }, { - id: "lambda/insights", - title: "Enable Lambda Insights", - relativePath: "docs/lambda/insights.mdx", - compId: "articles-docs-lambda-insights", - crumb: "Lambda", + id: "shapes/pie", + title: "<Pie />", + relativePath: "docs/shapes/pie.mdx", + compId: "articles-docs-shapes-pie", + crumb: "@remotion/shapes", }, { - id: "lambda/light-client", - title: "Light client", - relativePath: "docs/lambda/light-client.mdx", - compId: "articles-docs-lambda-light-client", - crumb: "Lambda", + id: "shapes/make-star", + title: "makeStar()", + relativePath: "docs/shapes/make-star.mdx", + compId: "articles-docs-shapes-make-star", + crumb: "@remotion/shapes", }, { - id: "lambda/limits", - title: "Lambda Limits", - relativePath: "docs/lambda/limits.mdx", - compId: "articles-docs-lambda-limits", - crumb: "Lambda", + id: "shapes/make-pie", + title: "makePie()", + relativePath: "docs/shapes/make-pie.mdx", + compId: "articles-docs-shapes-make-pie", + crumb: "@remotion/shapes", }, { - id: "lambda/multiple-buckets", - title: "Multiple buckets in Remotion Lambda", - relativePath: "docs/lambda/multiple-buckets.mdx", - compId: "articles-docs-lambda-multiple-buckets", - crumb: "@remotion/lambda", + id: "shapes/make-ellipse", + title: "makeEllipse()", + relativePath: "docs/shapes/make-ellipse.mdx", + compId: "articles-docs-shapes-make-ellipse", + crumb: "@remotion/shapes", }, { - id: "naming-convention", - title: "Function naming convention", - relativePath: "docs/lambda/naming-convention.mdx", - compId: "articles-docs-lambda-naming-convention", - crumb: "Lambda", + id: "shapes/star", + title: "<Star />", + relativePath: "docs/shapes/star.mdx", + compId: "articles-docs-shapes-star", + crumb: "@remotion/shapes", }, { - id: "lambda/permissions", - title: "Permissions", - relativePath: "docs/lambda/permissions.mdx", - compId: "articles-docs-lambda-permissions", - crumb: "Lambda", + id: "shapes/make-polygon", + title: "makePolygon()", + relativePath: "docs/shapes/make-polygon.mdx", + compId: "articles-docs-shapes-make-polygon", + crumb: "@remotion/shapes", }, { - id: "lambda/php", - title: "Triggering renders from PHP", - relativePath: "docs/lambda/php.mdx", - compId: "articles-docs-lambda-php", - crumb: "@remotion/lambda", + id: "shapes/make-circle", + title: "makeCircle()", + relativePath: "docs/shapes/make-circle.mdx", + compId: "articles-docs-shapes-make-circle", + crumb: "@remotion/shapes", }, { - id: "presignurl", - title: "presignUrl()", - relativePath: "docs/lambda/presignurl.mdx", - compId: "articles-docs-lambda-presignurl", - crumb: "Lambda API", + id: "series", + title: "<Series>", + relativePath: "docs/series.mdx", + compId: "articles-docs-series", + crumb: "API", }, { - id: "lambda/python", - title: "Triggering renders from Python", - relativePath: "docs/lambda/python.mdx", - compId: "articles-docs-lambda-python", - crumb: "@remotion/lambda", + id: "bun", + title: "Bun support", + relativePath: "docs/bun.mdx", + compId: "articles-docs-bun", + crumb: "bun bun bun bun bun", }, { - id: "region-selection", - title: "Region selection", - relativePath: "docs/lambda/region-selection.mdx", - compId: "articles-docs-lambda-region-selection", - crumb: "Lambda", + id: "loading-root-component", + title: "Root component Timeout", + relativePath: "docs/troubleshooting/loading-root-component.mdx", + compId: "articles-docs-troubleshooting-loading-root-component", + crumb: "Troubleshooting", }, { - id: "rendermediaonlambda", - title: "renderMediaOnLambda()", - relativePath: "docs/lambda/rendermediaonlambda.mdx", - compId: "articles-docs-lambda-rendermediaonlambda", - crumb: "Lambda API", + id: "troubleshooting/background-image", + title: "Flickering when using background-image or mask-image", + relativePath: "docs/troubleshooting/background-image.mdx", + compId: "articles-docs-troubleshooting-background-image", + crumb: "Common mistakes", }, { - id: "renderstillonlambda", - title: "renderStillOnLambda()", - relativePath: "docs/lambda/renderstillonlambda.mdx", - compId: "articles-docs-lambda-renderstillonlambda", - crumb: "Lambda API", + id: "rosetta", + title: "Apple Silicon under Rosetta", + relativePath: "docs/troubleshooting/rosetta.mdx", + compId: "articles-docs-troubleshooting-rosetta", + crumb: "Troubleshooting", }, { - id: "rendervideoonlambda", - title: "renderVideoOnLambda()", - relativePath: "docs/lambda/rendervideoonlambda.mdx", - compId: "articles-docs-lambda-rendervideoonlambda", - crumb: "Lambda API", + id: "troubleshooting/delay-render-proxy", + title: "Loading <Img> with src http://localhost:3000/proxy", + relativePath: "docs/troubleshooting/delay-render-proxy.mdx", + compId: "articles-docs-troubleshooting-delay-render-proxy", + crumb: "Troubleshooting", }, { - id: "runtime", - title: "Runtime", - relativePath: "docs/lambda/runtime.mdx", - compId: "articles-docs-lambda-runtime", - crumb: "Lambda", + id: "broken-fast-refresh", + title: "Fast Refresh not working", + relativePath: "docs/troubleshooting/broken-fast-refresh.mdx", + compId: "articles-docs-troubleshooting-broken-fast-refresh", + crumb: "Troubleshooting", }, { - id: "lambda/s3-public-access", - title: "Cannot create a S3 bucket using Remotion", - relativePath: "docs/lambda/s3-public-access.mdx", - compId: "articles-docs-lambda-s3-public-access", - crumb: "DevOps advisory", + id: "troubleshooting/timed-out-page-function", + title: "Timed out evaluating page function", + relativePath: "docs/troubleshooting/timed-out-page-function.mdx", + compId: "articles-docs-troubleshooting-timed-out-page-function", + crumb: "Troubleshooting", }, { - id: "lambda/serverless-framework-integration", - title: "Using the Serverless Framework with Remotion Lambda", - relativePath: "docs/lambda/serverless-framework-integration.mdx", - compId: "articles-docs-lambda-serverless-framework-integration", - crumb: "@remotion/lambda", + id: "troubleshooting/browser-launch", + title: "Failed to launch the browser process", + relativePath: "docs/troubleshooting/browser-launch.mdx", + compId: "articles-docs-troubleshooting-browser-launch", + crumb: "Troubleshooting", }, { - id: "setup", - title: "Setup", - relativePath: "docs/lambda/setup.mdx", - compId: "articles-docs-lambda-setup", - crumb: "Lambda", + id: "troubleshooting/sigkill", + title: "Process quit with signal SIGKILL", + relativePath: "docs/troubleshooting/sigkill.mdx", + compId: "articles-docs-troubleshooting-sigkill", + crumb: "Troubleshooting", }, { - id: "simulatepermissions", - title: "simulatePermissions()", - relativePath: "docs/lambda/simulatepermissions.mdx", - compId: "articles-docs-lambda-simulatepermissions", - crumb: "Lambda API", + id: "troubleshooting/nextjs-image", + title: "Flickering when using Next.js <Image> tag", + relativePath: "docs/troubleshooting/nextjs-image.mdx", + compId: "articles-docs-troubleshooting-nextjs-image", + crumb: "Common mistakes", }, { - id: "speculatefunctionname", - title: "speculateFunctionName()", - relativePath: "docs/lambda/speculateFunctionName.mdx", - compId: "articles-docs-lambda-speculateFunctionName", - crumb: "Lambda API", + id: "player-flicker", + title: "Avoiding flickering in <Player>", + relativePath: "docs/troubleshooting/video-flicker.mdx", + compId: "articles-docs-troubleshooting-video-flicker", + crumb: "Frame-perfection", }, { - id: "optimizing-speed", - title: "Optimizing for speed", - relativePath: "docs/lambda/speed.mdx", - compId: "articles-docs-lambda-speed", - crumb: "Lambda", + id: "troubleshooting/debug-failed-render", + title: "Debugging render failures", + relativePath: "docs/troubleshooting/debug-failed-render.mdx", + compId: "articles-docs-troubleshooting-debug-failed-render", + crumb: "Troubleshooting", }, { - id: "lambda/sqs", - title: "Using Lambda with SQS", - relativePath: "docs/lambda/sqs.mdx", - compId: "articles-docs-lambda-sqs", - crumb: "@remotion/lambda", + id: "defaultprops-too-big", + title: "defaultProps too big - could not serialize", + relativePath: "docs/troubleshooting/defaultprops-too-big.mdx", + compId: "articles-docs-troubleshooting-defaultprops-too-big", + crumb: "Troubleshooting", }, { - id: "bucket-disallows-acl", - title: "The bucket does not allow ACLs", - relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx", - compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl", - crumb: "Lambda Troubleshooting", + id: "troubleshooting/bundling-bundle", + title: "Calling bundle() in bundled code", + relativePath: "docs/troubleshooting/bundling-bundle.mdx", + compId: "articles-docs-troubleshooting-bundling-bundle", + crumb: "Troubleshooting", }, { - id: "debug", - title: "Debugging failed Lambda renders", - relativePath: "docs/lambda/troubleshooting/debug.mdx", - compId: "articles-docs-lambda-troubleshooting-debug", - crumb: "Lambda", + id: "troubleshooting/could-not-be-parsed-as-a-value-list", + title: "The source provided could not be parsed as a value list", + relativePath: + "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx", + compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list", + crumb: "Browser quirks", }, { - id: "permissions", - title: "AWS Permissions Troubleshooting", - relativePath: "docs/lambda/troubleshooting/permissions.mdx", - compId: "articles-docs-lambda-troubleshooting-permissions", - crumb: "Lambda Troubleshooting", + id: "cli/studio", + title: "npx remotion studio", + relativePath: "docs/cli/studio.mdx", + compId: "articles-docs-cli-studio", + crumb: "CLI Reference", }, { - id: "rate-limit", - title: "AWS Rate Limit Troubleshooting", - relativePath: "docs/lambda/troubleshooting/rate-limit.mdx", - compId: "articles-docs-lambda-troubleshooting-rate-limit", - crumb: "Lambda Troubleshooting", + id: "cli", + title: "Command line reference", + relativePath: "docs/cli/cli.mdx", + compId: "articles-docs-cli-cli", + crumb: null, }, { - id: "lambda/troubleshooting/security-token", - title: "'\"The security token included in the request is invalid\"'", - relativePath: "docs/lambda/troubleshooting/security-token.mdx", - compId: "articles-docs-lambda-troubleshooting-security-token", - crumb: "Lambda", + id: "cli/bundle", + title: "npx remotion bundle", + relativePath: "docs/cli/bundle.mdx", + compId: "articles-docs-cli-bundle", + crumb: "CLI Reference", }, { - id: "unrecognizedclientexception", - title: "UnrecognizedClientException", - relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx", - compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception", - crumb: "Lambda Troubleshooting", + id: "cli/gpu", + title: "npx remotion gpu", + relativePath: "docs/cli/gpu.mdx", + compId: "articles-docs-cli-gpu", + crumb: "CLI Reference", }, { - id: "uninstall", - title: "Uninstall Lambda", - relativePath: "docs/lambda/uninstall.mdx", - compId: "articles-docs-lambda-uninstall", - crumb: null, + id: "cli/install", + title: "npx remotion install", + relativePath: "docs/cli/install.mdx", + compId: "articles-docs-cli-install", + crumb: "CLI Reference", }, { - id: "upgrading", - title: "Upgrading Lambda", - relativePath: "docs/lambda/upgrading.mdx", - compId: "articles-docs-lambda-upgrading", - crumb: null, + id: "cli/versions", + title: "npx remotion versions", + relativePath: "docs/cli/versions.mdx", + compId: "articles-docs-cli-versions", + crumb: "CLI Reference", }, { - id: "validatewebhooksignature", - title: "validateWebhookSignature()", - relativePath: "docs/lambda/validatewebhooksignature.mdx", - compId: "articles-docs-lambda-validatewebhooksignature", - crumb: "Lambda API", + id: "cli/browser/index", + title: "npx remotion browser", + relativePath: "docs/cli/browser/index.mdx", + compId: "articles-docs-cli-browser-index", + crumb: "@remotion/cli", }, { - id: "lambda/webhooks", - title: "Webhooks", - relativePath: "docs/lambda/webhooks.mdx", - compId: "articles-docs-lambda-webhooks", - crumb: "Lambda", + id: "cli/browser/ensure", + title: "npx remotion browser ensure", + relativePath: "docs/cli/browser/ensure.mdx", + compId: "articles-docs-cli-browser-ensure", + crumb: "@remotion/cli", }, { - id: "lambda/without-iam/ec2", - title: "Authenticating Lambda with EC2", - relativePath: "docs/lambda/without-iam/ec2.mdx", - compId: "articles-docs-lambda-without-iam-ec2", - crumb: "@remotion/lambda", + id: "ffprobe", + title: "npx remotion ffprobe", + relativePath: "docs/cli/ffprobe.mdx", + compId: "articles-docs-cli-ffprobe", + crumb: "@remotion/cli", }, { - id: "lambda/without-iam/example", - title: "Example setup without IAM user", - relativePath: "docs/lambda/without-iam/example.mdx", - compId: "articles-docs-lambda-without-iam-example", - crumb: "IAM Roles Example", + id: "cli/render", + title: "npx remotion render", + relativePath: "docs/cli/render.mdx", + compId: "articles-docs-cli-render", + crumb: "CLI Reference", }, { - id: "lambda/without-iam/index", - title: "Using Lambda with IAM roles", - relativePath: "docs/lambda/without-iam/index.mdx", - compId: "articles-docs-lambda-without-iam-index", - crumb: "Lambda without IAM", + id: "cli/compositions", + title: "npx remotion compositions", + relativePath: "docs/cli/compositions.mdx", + compId: "articles-docs-cli-compositions", + crumb: "CLI Reference", }, { - id: "lambda", - title: "@remotion/lambda", - relativePath: "docs/lambda.mdx", - compId: "articles-docs-lambda", - crumb: null, + id: "cli/benchmark", + title: "npx remotion benchmark", + relativePath: "docs/cli/benchmark.mdx", + compId: "articles-docs-cli-benchmark", + crumb: "CLI Reference", }, { - id: "layers", - title: "Layers", - relativePath: "docs/layers.mdx", - compId: "articles-docs-layers", - crumb: "Designing videos", + id: "ffmpeg", + title: "npx remotion ffmpeg", + relativePath: "docs/cli/ffmpeg.mdx", + compId: "articles-docs-cli-ffmpeg", + crumb: "@remotion/cli", }, { - id: "layout-utils/best-practices", - title: "Best practices for @remotion/layout-utils", - relativePath: "docs/layout-utils/best-practices.mdx", - compId: "articles-docs-layout-utils-best-practices", - crumb: "@remotion/layout-utils", + id: "cli/still", + title: "npx remotion still", + relativePath: "docs/cli/still.mdx", + compId: "articles-docs-cli-still", + crumb: "CLI Reference", }, { - id: "layout-utils/fill-text-box", - title: "fillTextBox()", - relativePath: "docs/layout-utils/fill-text-box.mdx", - compId: "articles-docs-layout-utils-fill-text-box", - crumb: "@remotion/layout-utils", + id: "cli/help", + title: "npx remotion help", + relativePath: "docs/cli/help.mdx", + compId: "articles-docs-cli-help", + crumb: "CLI Reference", }, { - id: "layout-utils/fit-text", - title: "fitText()", - relativePath: "docs/layout-utils/fit-text.mdx", - compId: "articles-docs-layout-utils-fit-text", - crumb: "@remotion/layout-utils", + id: "cli/upgrade", + title: "npx remotion upgrade", + relativePath: "docs/cli/upgrade.mdx", + compId: "articles-docs-cli-upgrade", + crumb: "CLI Reference", }, { - id: "layout-utils/index", - title: "@remotion/layout-utils", - relativePath: "docs/layout-utils/index.mdx", - compId: "articles-docs-layout-utils-index", - crumb: null, + id: "watchstaticfile", + title: "watchStaticFile()", + relativePath: "docs/watch-static-file.mdx", + compId: "articles-docs-watch-static-file", + crumb: "API", }, { - id: "layout-utils/measure-text", - title: "measureText()", - relativePath: "docs/layout-utils/measure-text.mdx", - compId: "articles-docs-layout-utils-measure-text", - crumb: "@remotion/layout-utils", + id: "chromium-flags", + title: "Chromium flags", + relativePath: "docs/chromium-flags.mdx", + compId: "articles-docs-chromium-flags", + crumb: "Tweaks", }, { - id: "legacy-babel", - title: "Using legacy Babel transpilation", - relativePath: "docs/legacy-babel-loader.mdx", - compId: "articles-docs-legacy-babel-loader", - crumb: "How To", + id: "noise-visualization", + title: "Noise visualization", + relativePath: "docs/noise-visualization.mdx", + compId: "articles-docs-noise-visualization", + crumb: "Techniques", }, { - id: "license", - title: "License & Pricing", - relativePath: "docs/license.mdx", - compId: "articles-docs-license", - crumb: null, + id: "acknowledgements", + title: "Acknowledgements", + relativePath: "docs/acknowledgements.mdx", + compId: "articles-docs-acknowledgements", + crumb: "Credits", }, { - id: "loop", - title: "<Loop>", - relativePath: "docs/loop.mdx", - compId: "articles-docs-loop", - crumb: "API", + id: "miscellaneous/snippets/accelerated-video", + title: "Change the speed of a video over time", + relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx", + compId: "articles-docs-miscellaneous-snippets-accelerated-video", + crumb: "Snippets", }, { - id: "getlottiemetadata", - title: "getLottieMetadata()", - relativePath: "docs/lottie/getlottiemetadata.mdx", - compId: "articles-docs-lottie-getlottiemetadata", - crumb: "@remotion/lottie", + id: "miscellaneous/snippets/fps-converter", + title: "FPS converter", + relativePath: "docs/miscellaneous/snippets/fps-converter.mdx", + compId: "articles-docs-miscellaneous-snippets-fps-converter", + crumb: "Snippets", }, { - id: "lottie-index", - title: "@remotion/lottie", - relativePath: "docs/lottie/index.mdx", - compId: "articles-docs-lottie-index", - crumb: null, + id: "hls", + title: "HLS support (HTTP Live Streaming)", + relativePath: "docs/miscellaneous/snippets/hls.mdx", + compId: "articles-docs-miscellaneous-snippets-hls", + crumb: "Video", }, { - id: "lottie-comp", - title: "<Lottie>", - relativePath: "docs/lottie/lottie-comp.mdx", - compId: "articles-docs-lottie-lottie-comp", - crumb: "@remotion/lottie", + id: "miscellaneous/snippets/offthread-video-while-rendering", + title: "<OffthreadVideo /> while rendering", + relativePath: + "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx", + compId: + "articles-docs-miscellaneous-snippets-offthread-video-while-rendering", + crumb: "Snippets", }, { - id: "lottie-lottiefiles", - title: "Finding Lottie files to use", - relativePath: "docs/lottie/lottie-lottiefiles.mdx", - compId: "articles-docs-lottie-lottie-lottiefiles", - crumb: "Resources", + id: "miscellaneous/snippets/player-in-iframe", + title: "Embedding a <Player> into an <iframe>", + relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx", + compId: "articles-docs-miscellaneous-snippets-player-in-iframe", + crumb: "Snippets", }, { - id: "lottie-remote", - title: "Loading Lottie animations from a URL", - relativePath: "docs/lottie/lottie-remote.mdx", - compId: "articles-docs-lottie-lottie-remote", - crumb: "@remotion/lottie", + id: "miscellaneous/snippets/combine-compositions", + title: "How do I combine compositions?", + relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx", + compId: "articles-docs-miscellaneous-snippets-combine-compositions", + crumb: "FAQ", }, { - id: "lottie-staticfile", - title: "Loading Lottie animations from staticFile()", - relativePath: "docs/lottie/lottie-staticfile.mdx", - compId: "articles-docs-lottie-lottie-staticfile", - crumb: "@remotion/lottie", + id: "miscellaneous/snippets/align-duration", + title: "How do I make the composition the same duration as my video?", + relativePath: "docs/miscellaneous/snippets/align-duration.mdx", + compId: "articles-docs-miscellaneous-snippets-align-duration", + crumb: "FAQ", }, { - id: "measure-spring", - title: "measureSpring()", - relativePath: "docs/measure-spring.mdx", - compId: "articles-docs-measure-spring", - crumb: "API", + id: "miscellaneous/snippets/use-delay-render", + title: "useDelayRender()", + relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx", + compId: "articles-docs-miscellaneous-snippets-use-delay-render", + crumb: "Snippets", }, { - id: "measuring", - title: "Measuring DOM nodes", - relativePath: "docs/measuring.mdx", - compId: "articles-docs-measuring", - crumb: "How To", + id: "miscellaneous/linux-single-process", + title: "Multiple cores on Linux", + relativePath: "docs/miscellaneous/linux-single-process.mdx", + compId: "articles-docs-miscellaneous-linux-single-process", + crumb: "Server-side rendering", }, { - id: "media-playback-error", - title: "Could not play video/audio with src", - relativePath: "docs/media-playback-error.mdx", - compId: "articles-docs-media-playback-error", - crumb: "Troubleshooting", + id: "miscellaneous/vercel-functions", + title: "Can I render videos using Vercel Serverless functions?", + relativePath: "docs/miscellaneous/vercel-functions.mdx", + compId: "articles-docs-miscellaneous-vercel-functions", + crumb: "FAQ", }, { - id: "media-utils/index", - title: "@remotion/media-utils", - relativePath: "docs/media-utils/index.mdx", - compId: "articles-docs-media-utils-index", - crumb: null, + id: "miscellaneous/chrome-headless-shell", + title: "Chrome Headless Shell", + relativePath: "docs/miscellaneous/chrome-headless-shell.mdx", + compId: "articles-docs-miscellaneous-chrome-headless-shell", + crumb: "FAQ", }, { - id: "miscellaneous/absolute-paths", - title: "Files with absolute paths", - relativePath: "docs/miscellaneous/absolute-paths.mdx", - compId: "articles-docs-miscellaneous-absolute-paths", + id: "miscellaneous/render-in-browser", + title: "Can I render videos in the browser?", + relativePath: "docs/miscellaneous/render-in-browser.mdx", + compId: "articles-docs-miscellaneous-render-in-browser", crumb: "FAQ", }, { @@ -1686,6 +1549,13 @@ export const articles = [ compId: "articles-docs-miscellaneous-automatic-duration", crumb: "FAQ", }, + { + id: "typescript-aliases", + title: "TypeScript aliases", + relativePath: "docs/miscellaneous/ts-aliases.mdx", + compId: "articles-docs-miscellaneous-ts-aliases", + crumb: "How to", + }, { id: "miscellaneous/changing-temp-dir", title: "Changing the temporary directory", @@ -1694,10 +1564,17 @@ export const articles = [ crumb: "Advanced configuration", }, { - id: "miscellaneous/chrome-headless-shell", - title: "Chrome Headless Shell", - relativePath: "docs/miscellaneous/chrome-headless-shell.mdx", - compId: "articles-docs-miscellaneous-chrome-headless-shell", + id: "embed-studio", + title: "Can I embed the Remotion Studio?", + relativePath: "docs/miscellaneous/embed-remotion-studio.mdx", + compId: "articles-docs-miscellaneous-embed-remotion-studio", + crumb: "FAQ", + }, + { + id: "miscellaneous/render-on-edge", + title: "Can I render videos on the edge?", + relativePath: "docs/miscellaneous/render-on-edge.mdx", + compId: "articles-docs-miscellaneous-render-on-edge", crumb: "FAQ", }, { @@ -1715,18 +1592,18 @@ export const articles = [ crumb: "FAQ", }, { - id: "embed-studio", - title: "Can I embed the Remotion Studio?", - relativePath: "docs/miscellaneous/embed-remotion-studio.mdx", - compId: "articles-docs-miscellaneous-embed-remotion-studio", + id: "miscellaneous/linux-dependencies", + title: "Linux Dependencies", + relativePath: "docs/miscellaneous/linux-dependencies.mdx", + compId: "articles-docs-miscellaneous-linux-dependencies", crumb: "FAQ", }, { - id: "miscellaneous/linux-single-process", - title: "Multiple cores on Linux", - relativePath: "docs/miscellaneous/linux-single-process.mdx", - compId: "articles-docs-miscellaneous-linux-single-process", - crumb: "Server-side rendering", + id: "miscellaneous/video-formats", + title: "Which video formats does Remotion support?", + relativePath: "docs/miscellaneous/video-formats.mdx", + compId: "articles-docs-miscellaneous-video-formats", + crumb: "FAQ", }, { id: "miscellaneous/live-streaming", @@ -1736,503 +1613,494 @@ export const articles = [ crumb: "FAQ", }, { - id: "miscellaneous/render-in-browser", - title: "Can I render videos in the browser?", - relativePath: "docs/miscellaneous/render-in-browser.mdx", - compId: "articles-docs-miscellaneous-render-in-browser", - crumb: "FAQ", - }, - { - id: "miscellaneous/render-on-edge", - title: "Can I render videos on the edge?", - relativePath: "docs/miscellaneous/render-on-edge.mdx", - compId: "articles-docs-miscellaneous-render-on-edge", + id: "miscellaneous/absolute-paths", + title: "Files with absolute paths", + relativePath: "docs/miscellaneous/absolute-paths.mdx", + compId: "articles-docs-miscellaneous-absolute-paths", crumb: "FAQ", }, { - id: "miscellaneous/snippets/accelerated-video", - title: "Change the speed of a video over time", - relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx", - compId: "articles-docs-miscellaneous-snippets-accelerated-video", - crumb: "Snippets", - }, - { - id: "miscellaneous/snippets/align-duration", - title: "How do I make the composition the same duration as my video?", - relativePath: "docs/miscellaneous/snippets/align-duration.mdx", - compId: "articles-docs-miscellaneous-snippets-align-duration", - crumb: "FAQ", + id: "use-audio-data", + title: "useAudioData()", + relativePath: "docs/use-audio-data.mdx", + compId: "articles-docs-use-audio-data", + crumb: "@remotion/media-utils", }, { - id: "miscellaneous/snippets/combine-compositions", - title: "How do I combine compositions?", - relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx", - compId: "articles-docs-miscellaneous-snippets-combine-compositions", - crumb: "FAQ", + id: "calculate-metadata", + title: "calculateMetadata()", + relativePath: "docs/calculate-metadata.mdx", + compId: "articles-docs-calculate-metadata", + crumb: "API", }, { - id: "miscellaneous/snippets/fps-converter", - title: "FPS converter", - relativePath: "docs/miscellaneous/snippets/fps-converter.mdx", - compId: "articles-docs-miscellaneous-snippets-fps-converter", - crumb: "Snippets", + id: "passing-props", + title: "Passing props to a composition", + relativePath: "docs/passing-props.mdx", + compId: "articles-docs-passing-props", + crumb: "How To", }, { - id: "hls", - title: "HLS support (HTTP Live Streaming)", - relativePath: "docs/miscellaneous/snippets/hls.mdx", - compId: "articles-docs-miscellaneous-snippets-hls", - crumb: "Video", + id: "standalone", + title: "Useful functions outside of Remotion", + relativePath: "docs/standalone.mdx", + compId: "articles-docs-standalone", + crumb: null, }, { - id: "miscellaneous/snippets/offthread-video-while-rendering", - title: "<OffthreadVideo /> while rendering", - relativePath: - "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx", - compId: - "articles-docs-miscellaneous-snippets-offthread-video-while-rendering", - crumb: "Snippets", + id: "ssr-node", + title: "Rendering using SSR APIs", + relativePath: "docs/ssr-node.mdx", + compId: "articles-docs-ssr-node", + crumb: "Server-side rendering", }, { - id: "miscellaneous/snippets/player-in-iframe", - title: "Embedding a <Player> into an <iframe>", - relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx", - compId: "articles-docs-miscellaneous-snippets-player-in-iframe", - crumb: "Snippets", + id: "tailwind", + title: "@remotion/tailwind", + relativePath: "docs/tailwind/overview.mdx", + compId: "articles-docs-tailwind-overview", + crumb: null, }, { - id: "miscellaneous/snippets/use-delay-render", - title: "useDelayRender()", - relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx", - compId: "articles-docs-miscellaneous-snippets-use-delay-render", - crumb: "Snippets", + id: "tailwind/enable-tailwind", + title: "enableTailwind()", + relativePath: "docs/tailwind/enable-tailwind.mdx", + compId: "articles-docs-tailwind-enable-tailwind", + crumb: "@remotion/tailwind", }, { - id: "typescript-aliases", - title: "TypeScript aliases", - relativePath: "docs/miscellaneous/ts-aliases.mdx", - compId: "articles-docs-miscellaneous-ts-aliases", - crumb: "How to", + id: "upgrading", + title: "Upgrading Remotion", + relativePath: "docs/upgrading.mdx", + compId: "articles-docs-upgrading", + crumb: "upgrade remotion", }, { - id: "miscellaneous/vercel-functions", - title: "Can I render videos using Vercel Serverless functions?", - relativePath: "docs/miscellaneous/vercel-functions.mdx", - compId: "articles-docs-miscellaneous-vercel-functions", - crumb: "FAQ", + id: "getregions", + title: "getRegions()", + relativePath: "docs/cloudrun/getregions.mdx", + compId: "articles-docs-cloudrun-getregions", + crumb: "Cloud Run API", }, { - id: "miscellaneous/video-formats", - title: "Which video formats does Remotion support?", - relativePath: "docs/miscellaneous/video-formats.mdx", - compId: "articles-docs-miscellaneous-video-formats", - crumb: "FAQ", + id: "cloudrun/multiple-buckets", + title: "Multiple buckets in Remotion Cloud Run", + relativePath: "docs/cloudrun/multiple-buckets.mdx", + compId: "articles-docs-cloudrun-multiple-buckets", + crumb: "@remotion/cloudrun", }, { - id: "motion-blur/camera-motion-blur", - title: "<CameraMotionBlur>", - relativePath: "docs/motion-blur/camera-motion-blur.mdx", - compId: "articles-docs-motion-blur-camera-motion-blur", - crumb: "Realistic camera effect", + id: "instancecount", + title: "Instance Count", + relativePath: "docs/cloudrun/instancecount.mdx", + compId: "articles-docs-cloudrun-instancecount", + crumb: "Cloud Run", }, { - id: "motion-blur/index", - title: "@remotion/motion-blur", - relativePath: "docs/motion-blur/index.mdx", - compId: "articles-docs-motion-blur-index", + id: "cli", + title: "@remotion/cloudrun - CLI", + relativePath: "docs/cloudrun/cli.mdx", + compId: "articles-docs-cloudrun-cli", crumb: null, }, { - id: "motion-blur/motion-blur", - title: "<MotionBlur>", - relativePath: "docs/motion-blur/motion-blur.mdx", - compId: "articles-docs-motion-blur-motion-blur", - crumb: null, + id: "deployservice", + title: "deployService()", + relativePath: "docs/cloudrun/deployservice.mdx", + compId: "articles-docs-cloudrun-deployservice", + crumb: "Cloud Run API", }, { - id: "motion-blur/trail", - title: "<Trail>", - relativePath: "docs/motion-blur/trail.mdx", - compId: "articles-docs-motion-blur-trail", - crumb: "Motion blur", + id: "getsites", + title: "getSites()", + relativePath: "docs/cloudrun/getsites.mdx", + compId: "articles-docs-cloudrun-getsites", + crumb: "Cloud Run API", }, { - id: "noise/index", - title: "@remotion/noise", - relativePath: "docs/noise/index.mdx", - compId: "articles-docs-noise-index", - crumb: null, + id: "deleteservice", + title: "deleteService()", + relativePath: "docs/cloudrun/deleteservice.mdx", + compId: "articles-docs-cloudrun-deleteservice", + crumb: "Cloud Run API", }, { - id: "noise/noise-2d", - title: "noise2D()", - relativePath: "docs/noise/noise-2d.mdx", - compId: "articles-docs-noise-noise-2d", - crumb: "Make some", + id: "generate-env", + title: "Generate .env File", + relativePath: "docs/cloudrun/generate-env.mdx", + compId: "articles-docs-cloudrun-generate-env", + crumb: "Cloud Run", }, { - id: "noise/noise-3d", - title: "noise3D()", - relativePath: "docs/noise/noise-3d.mdx", - compId: "articles-docs-noise-noise-3d", - crumb: "Make some", + id: "cloudrun/permissions", + title: "Permissions", + relativePath: "docs/cloudrun/permissions.mdx", + compId: "articles-docs-cloudrun-permissions", + crumb: "Cloud Run", }, { - id: "noise/noise-4d", - title: "noise4D()", - relativePath: "docs/noise/noise-4d.mdx", - compId: "articles-docs-noise-noise-4d", - crumb: "Make some", + id: "renderstilloncloudrun", + title: "renderStillOnCloudrun()", + relativePath: "docs/cloudrun/renderstilloncloudrun.mdx", + compId: "articles-docs-cloudrun-renderstilloncloudrun", + crumb: "Cloud Run API", }, { - id: "noise-visualization", - title: "Noise visualization", - relativePath: "docs/noise-visualization.mdx", - compId: "articles-docs-noise-visualization", - crumb: "Techniques", + id: "getorcreatebucket", + title: "getOrCreateBucket()", + relativePath: "docs/cloudrun/getorcreatebucket.mdx", + compId: "articles-docs-cloudrun-getorcreatebucket", + crumb: "Cloud Run API", }, { - id: "non-seekable-media", - title: "Non-seekable media", - relativePath: "docs/non-seekable-media.mdx", - compId: "articles-docs-non-seekable-media", - crumb: "Troubleshooting", + id: "testpermissions", + title: "testPermissions()", + relativePath: "docs/cloudrun/testpermissions.mdx", + compId: "articles-docs-cloudrun-testpermissions", + crumb: "Cloud Run API", }, { - id: '"null"', - title: "<Experimental.Null>", - relativePath: "docs/null.mdx", - compId: "articles-docs-null", - crumb: "Experimental API", + id: "cloudrun/light-client", + title: "Light client", + relativePath: "docs/cloudrun/light-client.mdx", + compId: "articles-docs-cloudrun-light-client", + crumb: "Cloud Run", }, { - id: "offthreadvideo", - title: "<OffthreadVideo>", - relativePath: "docs/offthreadvideo.mdx", - compId: "articles-docs-offthreadvideo", - crumb: "API", + id: "services", + title: "npx remotion cloudrun services", + relativePath: "docs/cloudrun/cli/services.mdx", + compId: "articles-docs-cloudrun-cli-services", + crumb: "Cloud Run CLI Reference", }, { - id: "gl-options", - title: "--gl flag", - relativePath: "docs/open-gl.mdx", - compId: "articles-docs-open-gl", - crumb: "Flags", + id: "permissions", + title: "npx remotion cloudrun permissions", + relativePath: "docs/cloudrun/cli/permissions.mdx", + compId: "articles-docs-cloudrun-cli-permissions", + crumb: "Cloud Run CLI Reference", }, { - id: "overlay", - title: "Creating overlays", - relativePath: "docs/overlay.mdx", - compId: "articles-docs-overlay", - crumb: null, + id: "sites", + title: "npx remotion cloudrun sites", + relativePath: "docs/cloudrun/cli/sites.mdx", + compId: "articles-docs-cloudrun-cli-sites", + crumb: "Cloud Run CLI Reference", }, { - id: "webpack", - title: "Custom Webpack config", - relativePath: "docs/overwriting-webpack-config.mdx", - compId: "articles-docs-overwriting-webpack-config", - crumb: "How To", + id: "render", + title: "npx remotion cloudrun render", + relativePath: "docs/cloudrun/cli/render.mdx", + compId: "articles-docs-cloudrun-cli-render", + crumb: "Cloud Run CLI Reference", }, { - id: "parameterized-rendering", - title: "Parameterized videos", - relativePath: "docs/parameterized-rendering.mdx", - compId: "articles-docs-parameterized-rendering", - crumb: "How To", + id: "regions", + title: "npx remotion cloudrun regions", + relativePath: "docs/cloudrun/cli/regions.mdx", + compId: "articles-docs-cloudrun-cli-regions", + crumb: "Cloud Run CLI Reference", }, { - id: "passing-props", - title: "Passing props to a composition", - relativePath: "docs/passing-props.mdx", - compId: "articles-docs-passing-props", - crumb: "How To", + id: "still", + title: "npx remotion cloudrun still", + relativePath: "docs/cloudrun/cli/still.mdx", + compId: "articles-docs-cloudrun-cli-still", + crumb: "Cloud Run CLI Reference", }, { - id: "paths/evolve-path", - title: "evolvePath()", - relativePath: "docs/paths/evolve-path.mdx", - compId: "articles-docs-paths-evolve-path", - crumb: "@remotion/paths", + id: "upgrading", + title: "Upgrading Cloud Run", + relativePath: "docs/cloudrun/upgrading.mdx", + compId: "articles-docs-cloudrun-upgrading", + crumb: null, }, { - id: "paths/extend-viewbox", - title: "extendViewBox()", - relativePath: "docs/paths/extend-viewbox.mdx", - compId: "articles-docs-paths-extend-viewbox", - crumb: "@remotion/paths", + id: "region-selection", + title: "Region selection", + relativePath: "docs/cloudrun/region-selection.mdx", + compId: "articles-docs-cloudrun-region-selection", + crumb: "Cloud Run", }, { - id: "paths/get-bounding-box", - title: "getBoundingBox()", - relativePath: "docs/paths/get-bounding-box.mdx", - compId: "articles-docs-paths-get-bounding-box", - crumb: "@remotion/paths", + id: "getservices", + title: "getServices()", + relativePath: "docs/cloudrun/getservices.mdx", + compId: "articles-docs-cloudrun-getservices", + crumb: "Cloud Run API", }, { - id: "paths/get-instruction-index-at-length", - title: "getInstructionIndexAtLength()", - relativePath: "docs/paths/get-instruction-index-at-length.mdx", - compId: "articles-docs-paths-get-instruction-index-at-length", - crumb: "@remotion/paths", + id: "getserviceinfo", + title: "getServiceInfo()", + relativePath: "docs/cloudrun/getServiceinfo.mdx", + compId: "articles-docs-cloudrun-getServiceinfo", + crumb: "Cloud Run API", }, { - id: "paths/get-length", - title: "getLength()", - relativePath: "docs/paths/get-length.mdx", - compId: "articles-docs-paths-get-length", - crumb: "@remotion/paths", + id: "checklist", + title: "Production Checklist", + relativePath: "docs/cloudrun/checklist.mdx", + compId: "articles-docs-cloudrun-checklist", + crumb: "Cloud Run", }, { - id: "paths/get-parts", - title: "getParts()", - relativePath: "docs/paths/get-parts.mdx", - compId: "articles-docs-paths-get-parts", - crumb: "@remotion/paths", + id: "cloudrun/api", + title: "@remotion/cloudrun", + relativePath: "docs/cloudrun/api.mdx", + compId: "articles-docs-cloudrun-api", + crumb: "Render videos without servers on GCP", }, { - id: "paths/get-point-at-length", - title: "getPointAtLength()", - relativePath: "docs/paths/get-point-at-length.mdx", - compId: "articles-docs-paths-get-point-at-length", - crumb: "@remotion/paths", + id: "setup", + title: "Setup", + relativePath: "docs/cloudrun/setup.mdx", + compId: "articles-docs-cloudrun-setup", + crumb: "Cloud Run", }, { - id: "paths/get-subpaths", - title: "getSubpaths()", - relativePath: "docs/paths/get-subpaths.mdx", - compId: "articles-docs-paths-get-subpaths", - crumb: "@remotion/paths", + id: "rendermediaoncloudrun", + title: "renderMediaOnCloudrun()", + relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx", + compId: "articles-docs-cloudrun-rendermediaoncloudrun", + crumb: "Cloud Run API", }, { - id: "paths/get-tangent-at-length", - title: "getTangentAtLength()", - relativePath: "docs/paths/get-tangent-at-length.mdx", - compId: "articles-docs-paths-get-tangent-at-length", - crumb: "@remotion/paths", + id: "speculateservicename", + title: "speculateServiceName()", + relativePath: "docs/cloudrun/speculateservicename.mdx", + compId: "articles-docs-cloudrun-speculateservicename", + crumb: "Cloud Run API", }, { - id: "paths/index", - title: "@remotion/paths", - relativePath: "docs/paths/index.mdx", - compId: "articles-docs-paths-index", - crumb: "SVG", + id: "cloudrun/limits", + title: "Cloud Run Limits", + relativePath: "docs/cloudrun/limits.mdx", + compId: "articles-docs-cloudrun-limits", + crumb: "Cloud Run", }, { - id: "paths/interpolate-path", - title: "interpolatePath()", - relativePath: "docs/paths/interpolate-path.mdx", - compId: "articles-docs-paths-interpolate-path", - crumb: "@remotion/paths", + id: "deploysite", + title: "deploySite()", + relativePath: "docs/cloudrun/deploysite.mdx", + compId: "articles-docs-cloudrun-deploysite", + crumb: "Cloud Run API", }, { - id: "paths/normalize-path", - title: "normalizePath()", - relativePath: "docs/paths/normalize-path.mdx", - compId: "articles-docs-paths-normalize-path", - crumb: "@remotion/paths", + id: "uninstall", + title: "Uninstall Cloud Run", + relativePath: "docs/cloudrun/uninstall.mdx", + compId: "articles-docs-cloudrun-uninstall", + crumb: null, }, { - id: "paths/parse-path", - title: "parsePath()", - relativePath: "docs/paths/parse-path.mdx", - compId: "articles-docs-paths-parse-path", - crumb: "@remotion/paths", + id: "deletesite", + title: "deleteSite()", + relativePath: "docs/cloudrun/deletesite.mdx", + compId: "articles-docs-cloudrun-deletesite", + crumb: "Cloud Run API", + }, + { + id: "reusability", + title: "Making components reusable", + relativePath: "docs/sequences.mdx", + compId: "articles-docs-sequences", + crumb: "The power of React", }, { - id: "paths/reduce-instructions", - title: "reduceInstructions()", - relativePath: "docs/paths/reduce-instructions.mdx", - compId: "articles-docs-paths-reduce-instructions", - crumb: "@remotion/paths", + id: "three-canvas", + title: "<ThreeCanvas>", + relativePath: "docs/three-canvas.mdx", + compId: "articles-docs-three-canvas", + crumb: "@remotion/three", }, { - id: "paths/reset-path", - title: "resetPath()", - relativePath: "docs/paths/reset-path.mdx", - compId: "articles-docs-paths-reset-path", - crumb: "@remotion/paths", + id: "version-mismatch", + title: "Version mismatch", + relativePath: "docs/version-mismatch.mdx", + compId: "articles-docs-version-mismatch", + crumb: "How to fix a", }, { - id: "paths/reverse-path", - title: "reversePath()", - relativePath: "docs/paths/reverse-path.mdx", - compId: "articles-docs-paths-reverse-path", - crumb: "@remotion/paths", + id: "iframe", + title: "<IFrame>", + relativePath: "docs/iframe.mdx", + compId: "articles-docs-iframe", + crumb: "API", }, { - id: "paths/scale-path", - title: "scalePath()", - relativePath: "docs/paths/scale-path.mdx", - compId: "articles-docs-paths-scale-path", - crumb: "@remotion/paths", + id: "cloudrun", + title: "@remotion/cloudrun", + relativePath: "docs/cloudrun.mdx", + compId: "articles-docs-cloudrun", + crumb: null, }, { - id: "paths/serialize-instructions", - title: "serializeInstructions()", - relativePath: "docs/paths/serialize-instructions.mdx", - compId: "articles-docs-paths-serialize-instructions", - crumb: "@remotion/paths", + id: "transitioning", + title: "Transitions", + relativePath: "docs/transitions.mdx", + compId: "articles-docs-transitions", + crumb: "Techniques", }, { - id: "paths/translate-path", - title: "translatePath()", - relativePath: "docs/paths/translate-path.mdx", - compId: "articles-docs-paths-translate-path", - crumb: "@remotion/paths", + id: "gl-options", + title: "--gl flag", + relativePath: "docs/open-gl.mdx", + compId: "articles-docs-open-gl", + crumb: "Flags", }, { - id: "paths/warp-path", - title: "warpPath()", - relativePath: "docs/paths/warp-path.mdx", - compId: "articles-docs-paths-warp-path", - crumb: "@remotion/paths", + id: "interpolate-colors", + title: "interpolateColors()", + relativePath: "docs/interpolate-colors.mdx", + compId: "articles-docs-interpolate-colors", + crumb: "API", }, { - id: "performance", - title: "Performance Tips", - relativePath: "docs/performance.mdx", - compId: "articles-docs-performance", - crumb: "Need for Speed", + id: "using-randomness", + title: "Using randomness", + relativePath: "docs/using-randomness.mdx", + compId: "articles-docs-using-randomness", + crumb: "Roll the dice", }, { - id: "player/api", - title: "<Player>", - relativePath: "docs/player/api.mdx", - compId: "articles-docs-player-api", - crumb: "@remotion/player", + id: "5-0-migration", + title: "v5.0 Migration", + relativePath: "docs/5-0-migration.mdx", + compId: "articles-docs-5-0-migration", + crumb: "Version Upgrade", }, { - id: "autoplay", - title: "Combatting autoplay issues", - relativePath: "docs/player/autoplay.mdx", - compId: "articles-docs-player-autoplay", - crumb: "@remotion/player", + id: "get-input-props", + title: "getInputProps()", + relativePath: "docs/get-input-props.mdx", + compId: "articles-docs-get-input-props", + crumb: "API", }, { - id: "best-practices", - title: "Player - Best practices", - relativePath: "docs/player/best-practices.mdx", - compId: "articles-docs-player-best-practices", - crumb: "@remotion/player", + id: "measuring", + title: "Measuring DOM nodes", + relativePath: "docs/measuring.mdx", + compId: "articles-docs-measuring", + crumb: "How To", }, { - id: "buffer-state", - title: "The Player buffer state", - relativePath: "docs/player/buffer-state.mdx", - compId: "articles-docs-player-buffer-state", - crumb: "Best practices", + id: "2-0-migration", + title: "v2.0 Migration", + relativePath: "docs/2-0-migration.mdx", + compId: "articles-docs-2-0-migration", + crumb: "Version Upgrade", }, { - id: "current-time", - title: "Displaying the current time", - relativePath: "docs/player/current-time.mdx", - compId: "articles-docs-player-current-time", - crumb: "@remotion/player", + id: "getting-started", + title: "Creating a new project", + relativePath: "docs/getting-started.mdx", + compId: "articles-docs-getting-started", + crumb: "Let's begin!", }, { - id: "player/index", - title: "@remotion/player", - relativePath: "docs/player/index.mdx", - compId: "articles-docs-player-index", + id: "google-fonts", + title: "@remotion/google-fonts", + relativePath: "docs/google-fonts/index.mdx", + compId: "articles-docs-google-fonts-index", crumb: null, }, { - id: "installation", - title: "Installation", - relativePath: "docs/player/installation.mdx", - compId: "articles-docs-player-installation", - crumb: "@remotion/player", + id: "google-fonts/get-available-fonts", + title: "getAvailableFonts()", + relativePath: "docs/google-fonts/get-available-fonts.mdx", + compId: "articles-docs-google-fonts-get-available-fonts", + crumb: "@remotion/google-fonts", }, { - id: "examples", - title: "Examples for @remotion/player", - relativePath: "docs/player/player-examples.mdx", - compId: "articles-docs-player-player-examples", - crumb: "@remotion/player", + id: "google-fonts/get-info", + title: "getInfo()", + relativePath: "docs/google-fonts/get-info.mdx", + compId: "articles-docs-google-fonts-get-info", + crumb: "@remotion/google-fonts", }, { - id: "integration", - title: "Code sharing", - relativePath: "docs/player/player-integration.mdx", - compId: "articles-docs-player-player-integration", - crumb: "@remotion/player", + id: "google-fonts/load-font", + title: "loadFont()", + relativePath: "docs/google-fonts/load-font.mdx", + compId: "articles-docs-google-fonts-load-font", + crumb: "@remotion/google-fonts", }, { - id: "player/preloading", - title: "Preloading assets", - relativePath: "docs/player/preloading.mdx", - compId: "articles-docs-player-preloading", - crumb: "@remotion/player", + id: "media-utils/index", + title: "@remotion/media-utils", + relativePath: "docs/media-utils/index.mdx", + compId: "articles-docs-media-utils-index", + crumb: null, }, { - id: "player/premounting", - title: "Premounting", - relativePath: "docs/player/premounting.mdx", - compId: "articles-docs-player-premounting", - crumb: "@remotion/player", + id: "noise/noise-4d", + title: "noise4D()", + relativePath: "docs/noise/noise-4d.mdx", + compId: "articles-docs-noise-noise-4d", + crumb: "Make some", }, { - id: "scaling", - title: "Sizing", - relativePath: "docs/player/scaling.mdx", - compId: "articles-docs-player-scaling", - crumb: "@remotion/player", + id: "noise/noise-3d", + title: "noise3D()", + relativePath: "docs/noise/noise-3d.mdx", + compId: "articles-docs-noise-noise-3d", + crumb: "Make some", }, { - id: "thumbnail", - title: "<Thumbnail>", - relativePath: "docs/player/thumbnail.mdx", - compId: "articles-docs-player-thumbnail", - crumb: "@remotion/player", + id: "noise/index", + title: "@remotion/noise", + relativePath: "docs/noise/index.mdx", + compId: "articles-docs-noise-index", + crumb: null, }, { - id: "prefetch", - title: "prefetch()", - relativePath: "docs/prefetch.mdx", - compId: "articles-docs-prefetch", - crumb: "API", + id: "noise/noise-2d", + title: "noise2D()", + relativePath: "docs/noise/noise-2d.mdx", + compId: "articles-docs-noise-noise-2d", + crumb: "Make some", }, { - id: "preload-audio", - title: "preloadAudio()", - relativePath: "docs/preload/preload-audio.mdx", - compId: "articles-docs-preload-preload-audio", - crumb: "@remotion/preload", + id: "security", + title: "Security Best Practices", + relativePath: "docs/security.mdx", + compId: "articles-docs-security", + crumb: "FAQ", }, { - id: "preload-font", - title: "preloadFont()", - relativePath: "docs/preload/preload-font.mdx", - compId: "articles-docs-preload-preload-font", - crumb: "@remotion/preload", + id: "delay-render", + title: "delayRender() and continueRender()", + relativePath: "docs/delay-render.mdx", + compId: "articles-docs-delay-render", + crumb: "How to", }, { - id: "preload-image", - title: "preloadImage()", - relativePath: "docs/preload/preload-image.mdx", - compId: "articles-docs-preload-preload-image", - crumb: "@remotion/preload", + id: "loop", + title: "<Loop>", + relativePath: "docs/loop.mdx", + compId: "articles-docs-loop", + crumb: "API", }, - { - id: "preload-video", - title: "preloadVideo()", - relativePath: "docs/preload/preload-video.mdx", - compId: "articles-docs-preload-preload-video", - crumb: "@remotion/player", + { + id: "render", + title: "Render your video", + relativePath: "docs/render.mdx", + compId: "articles-docs-render", + crumb: "How To", }, { - id: "preload", - title: "@remotion/preload", - relativePath: "docs/preload/preload.mdx", - compId: "articles-docs-preload-preload", - crumb: null, + id: "ssr-legacy", + title: "Server-Side Rendering (v1 and v2)", + relativePath: "docs/ssr-legacy.mdx", + compId: "articles-docs-ssr-legacy", + crumb: "Legacy docs", }, { - id: "prereleases", - title: "Testing prereleases", - relativePath: "docs/prereleases.mdx", - compId: "articles-docs-prereleases", - crumb: "Only the brave", + id: "continue-render", + title: "continueRender()", + relativePath: "docs/continue-render.mdx", + compId: "articles-docs-continue-render", + crumb: "API", }, { id: "presigned-urls", @@ -2242,975 +2110,1010 @@ export const articles = [ crumb: "Building video apps", }, { - id: "preview", - title: "Preview your video", - relativePath: "docs/preview.mdx", - compId: "articles-docs-preview", - crumb: "Timeline basics", + id: "tailwind-legacy", + title: "TailwindCSS v2 (Legacy)", + relativePath: "docs/tailwind-2.mdx", + compId: "articles-docs-tailwind-2", + crumb: "Legacy docs", }, { - id: "quality", - title: "Quality Guide", - relativePath: "docs/quality.mdx", - compId: "articles-docs-quality", - crumb: "Make it look good", + id: "performance", + title: "Performance Tips", + relativePath: "docs/performance.mdx", + compId: "articles-docs-performance", + crumb: "Need for Speed", }, { - id: "random", - title: "random()", - relativePath: "docs/random.mdx", - compId: "articles-docs-random", - crumb: "API", + id: "getregions", + title: "getRegions()", + relativePath: "docs/lambda/getregions.mdx", + compId: "articles-docs-lambda-getregions", + crumb: "Lambda API", }, { - id: "react-18", - title: "Upgrade to React 18", - relativePath: "docs/react-18.mdx", - compId: "articles-docs-react-18", - crumb: "The new and shiny", + id: "lambda/feb-2023-incident", + title: "Upgrade your Lambda functions to prevent breakage", + relativePath: "docs/lambda/feb-2023-incident.mdx", + compId: "articles-docs-lambda-feb-2023-incident", + crumb: "DevOps advisory", }, { - id: "react-native", - title: "React Native", - relativePath: "docs/react-native.mdx", - compId: "articles-docs-react-native", + id: "lambda/multiple-buckets", + title: "Multiple buckets in Remotion Lambda", + relativePath: "docs/lambda/multiple-buckets.mdx", + compId: "articles-docs-lambda-multiple-buckets", + crumb: "@remotion/lambda", + }, + { + id: "cli", + title: "@remotion/lambda - CLI", + relativePath: "docs/lambda/cli.mdx", + compId: "articles-docs-lambda-cli", crumb: null, }, { - id: "recorder/before-you-buy", - title: "Before you buy", - relativePath: "docs/recorder/before-you-buy.mdx", - compId: "articles-docs-recorder-before-you-buy", - crumb: "Recorder", + id: "getuserpolicy", + title: "getUserPolicy()", + relativePath: "docs/lambda/getuserpolicy.mdx", + compId: "articles-docs-lambda-getuserpolicy", + crumb: "Lambda API", }, { - id: "recorder/buy", - title: "Buy", - relativePath: "docs/recorder/buy.mdx", - compId: "articles-docs-recorder-buy", - crumb: "Recorder", + id: "concurrency", + title: "Concurrency", + relativePath: "docs/lambda/concurrency.mdx", + compId: "articles-docs-lambda-concurrency", + crumb: "Lambda", }, { - id: "recorder/captions", - title: "Generate captions", - relativePath: "docs/recorder/captions.mdx", - compId: "articles-docs-recorder-captions", - crumb: "Recorder", + id: "autodelete", + title: "Auto-delete renders", + relativePath: "docs/lambda/autodelete.mdx", + compId: "articles-docs-lambda-autodelete", + crumb: "Lambda API", }, { - id: "recorder/create", - title: "Create a new video", - relativePath: "docs/recorder/create.mdx", - compId: "articles-docs-recorder-create", - crumb: "Recorder", + id: "optimizing-speed", + title: "Optimizing for speed", + relativePath: "docs/lambda/speed.mdx", + compId: "articles-docs-lambda-speed", + crumb: "Lambda", }, { - id: "recorder/editing/b-roll", - title: "B-Roll", - relativePath: "docs/recorder/editing/b-roll.mdx", - compId: "articles-docs-recorder-editing-b-roll", - crumb: "Recorder", + id: "rendervideoonlambda", + title: "renderVideoOnLambda()", + relativePath: "docs/lambda/rendervideoonlambda.mdx", + compId: "articles-docs-lambda-rendervideoonlambda", + crumb: "Lambda API", }, { - id: "recorder/editing/captions", - title: "Edit captions", - relativePath: "docs/recorder/editing/captions.mdx", - compId: "articles-docs-recorder-editing-captions", - crumb: "Recorder", + id: "getsites", + title: "getSites()", + relativePath: "docs/lambda/getsites.mdx", + compId: "articles-docs-lambda-getsites", + crumb: "Lambda API", }, { - id: "recorder/editing/chapters", - title: "Chapters", - relativePath: "docs/recorder/editing/chapters.mdx", - compId: "articles-docs-recorder-editing-chapters", - crumb: "Recorder", + id: "speculatefunctionname", + title: "speculateFunctionName()", + relativePath: "docs/lambda/speculateFunctionName.mdx", + compId: "articles-docs-lambda-speculateFunctionName", + crumb: "Lambda API", }, { - id: "recorder/editing/cutting-clips", - title: "Cutting clips", - relativePath: "docs/recorder/editing/cutting-clips.mdx", - compId: "articles-docs-recorder-editing-cutting-clips", - crumb: "Recorder", + id: "lambda/permissions", + title: "Permissions", + relativePath: "docs/lambda/permissions.mdx", + compId: "articles-docs-lambda-permissions", + crumb: "Lambda", }, { - id: "recorder/editing/editing", - title: "Start editing", - relativePath: "docs/recorder/editing/editing.mdx", - compId: "articles-docs-recorder-editing-editing", - crumb: "Recorder", + id: "deployfunction", + title: "deployFunction()", + relativePath: "docs/lambda/deployfunction.mdx", + compId: "articles-docs-lambda-deployfunction", + crumb: "Lambda API", }, { - id: "recorder/editing/endcard", - title: "Endcard", - relativePath: "docs/recorder/editing/endcard.mdx", - compId: "articles-docs-recorder-editing-endcard", - crumb: "Recorder", + id: "feb-2022-outage", + title: "February 2022 Outage", + relativePath: "docs/lambda/feb-2022-outage.mdx", + compId: "articles-docs-lambda-feb-2022-outage", + crumb: null, }, { - id: "recorder/editing/layout", - title: "Layout", - relativePath: "docs/recorder/editing/layout.mdx", - compId: "articles-docs-recorder-editing-layout", - crumb: "Recorder", + id: "getrenderprogress", + title: "getRenderProgress()", + relativePath: "docs/lambda/getrenderprogress.mdx", + compId: "articles-docs-lambda-getrenderprogress", + crumb: "Lambda API", }, { - id: "recorder/editing/music", - title: "Adding music", - relativePath: "docs/recorder/editing/music.mdx", - compId: "articles-docs-recorder-editing-music", - crumb: "Recorder", + id: "authentication", + title: "Authentication", + relativePath: "docs/lambda/authentication.mdx", + compId: "articles-docs-lambda-authentication", + crumb: "Lambda", }, { - id: "recorder/editing/scenes", - title: "Scenes", - relativePath: "docs/recorder/editing/scenes.mdx", - compId: "articles-docs-recorder-editing-scenes", - crumb: "Recorder", + id: "lambda/python", + title: "Triggering renders from Python", + relativePath: "docs/lambda/python.mdx", + compId: "articles-docs-lambda-python", + crumb: "@remotion/lambda", }, { - id: "recorder/editing/silence-removal", - title: "Silence removal", - relativePath: "docs/recorder/editing/silence-removal.mdx", - compId: "articles-docs-recorder-editing-silence-removal", - crumb: "Recorder", + id: "lambda/webhooks", + title: "Webhooks", + relativePath: "docs/lambda/webhooks.mdx", + compId: "articles-docs-lambda-webhooks", + crumb: "Lambda", }, { - id: "recorder/editing/transitions", - title: "Transitions", - relativePath: "docs/recorder/editing/transitions.mdx", - compId: "articles-docs-recorder-editing-transitions", - crumb: "Recorder", + id: "getorcreatebucket", + title: "getOrCreateBucket()", + relativePath: "docs/lambda/getorcreatebucket.mdx", + compId: "articles-docs-lambda-getorcreatebucket", + crumb: "Lambda API", }, { - id: "recorder/exporting", - title: "Exporting a video", - relativePath: "docs/recorder/exporting.mdx", - compId: "articles-docs-recorder-exporting", - crumb: "Recorder", + id: "custom-destination", + title: "Customizing Lambda output destination", + relativePath: "docs/lambda/custom-destination.mdx", + compId: "articles-docs-lambda-custom-destination", + crumb: "Lambda", }, { - id: "recorder/external-recordings", - title: "External recordings", - relativePath: "docs/recorder/external-recordings.mdx", - compId: "articles-docs-recorder-external-recordings", - crumb: "Recorder", + id: "deletefunction", + title: "deleteFunction()", + relativePath: "docs/lambda/deletefunction.mdx", + compId: "articles-docs-lambda-deletefunction", + crumb: "Lambda API", + }, + { + id: "estimateprice", + title: "estimatePrice()", + relativePath: "docs/lambda/estimateprice.mdx", + compId: "articles-docs-lambda-estimateprice", + crumb: "Lambda API", }, { - id: "recorder/gear", - title: "Our Gear", - relativePath: "docs/recorder/gear.mdx", - compId: "articles-docs-recorder-gear", - crumb: "Recorder", + id: "getrolepolicy", + title: "getRolePolicy()", + relativePath: "docs/lambda/getrolepolicy.mdx", + compId: "articles-docs-lambda-getrolepolicy", + crumb: "Lambda API", }, { - id: "recorder/index", - title: "Remotion Recorder", - relativePath: "docs/recorder/index.mdx", - compId: "articles-docs-recorder-index", - crumb: "Templates", + id: "lambda/light-client", + title: "Light client", + relativePath: "docs/lambda/light-client.mdx", + compId: "articles-docs-lambda-light-client", + crumb: "Lambda", }, { - id: "recorder/lambda-rendering", - title: "Render on Lambda", - relativePath: "docs/recorder/lambda-rendering.mdx", - compId: "articles-docs-recorder-lambda-rendering", - crumb: "Recorder", + id: "debug", + title: "Debugging failed Lambda renders", + relativePath: "docs/lambda/troubleshooting/debug.mdx", + compId: "articles-docs-lambda-troubleshooting-debug", + crumb: "Lambda", }, { - id: "recorder/our-recorder", - title: "Our Recorder", - relativePath: "docs/recorder/our-recorder.mdx", - compId: "articles-docs-recorder-our-recorder", - crumb: "Recorder", + id: "unrecognizedclientexception", + title: "UnrecognizedClientException", + relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx", + compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception", + crumb: "Lambda Troubleshooting", }, { - id: "recorder/record", - title: "Record a scene", - relativePath: "docs/recorder/record.mdx", - compId: "articles-docs-recorder-record", - crumb: "Recorder", + id: "bucket-disallows-acl", + title: "The bucket does not allow ACLs", + relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx", + compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl", + crumb: "Lambda Troubleshooting", }, { - id: "recorder/roadmap", - title: "Roadmap", - relativePath: "docs/recorder/roadmap.mdx", - compId: "articles-docs-recorder-roadmap", - crumb: "Recorder", + id: "permissions", + title: "AWS Permissions Troubleshooting", + relativePath: "docs/lambda/troubleshooting/permissions.mdx", + compId: "articles-docs-lambda-troubleshooting-permissions", + crumb: "Lambda Troubleshooting", }, { - id: "recorder/setup", - title: "Setup", - relativePath: "docs/recorder/setup.mdx", - compId: "articles-docs-recorder-setup", - crumb: "Recorder", + id: "lambda/troubleshooting/security-token", + title: "'\"The security token included in the request is invalid\"'", + relativePath: "docs/lambda/troubleshooting/security-token.mdx", + compId: "articles-docs-lambda-troubleshooting-security-token", + crumb: "Lambda", }, { - id: "recorder/source-control", - title: "Source Control", - relativePath: "docs/recorder/source-control.mdx", - compId: "articles-docs-recorder-source-control", - crumb: "Recorder", + id: "rate-limit", + title: "AWS Rate Limit Troubleshooting", + relativePath: "docs/lambda/troubleshooting/rate-limit.mdx", + compId: "articles-docs-lambda-troubleshooting-rate-limit", + crumb: "Lambda Troubleshooting", }, { - id: "recorder/support", - title: "Get Help", - relativePath: "docs/recorder/support.mdx", - compId: "articles-docs-recorder-support", - crumb: "Recorder", + id: "policies", + title: "npx remotion lambda policies", + relativePath: "docs/lambda/cli/policies.mdx", + compId: "articles-docs-lambda-cli-policies", + crumb: "Lambda CLI Reference", }, { - id: "recorder/troubleshooting/cannot-read-properties-of-undefined", - title: "Cannot read properties of undefined (reading 'decode')", - relativePath: - "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx", - compId: - "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined", - crumb: "Recorder", + id: "quotas", + title: "npx remotion lambda quotas", + relativePath: "docs/lambda/cli/quotas.mdx", + compId: "articles-docs-lambda-cli-quotas", + crumb: "Lambda CLI Reference", }, { - id: "recorder/troubleshooting/failed-to-execute-get-video-metadata", - title: "Failed to execute getVideoMetadata()", - relativePath: - "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx", - compId: - "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata", - crumb: "Recorder", + id: "functions", + title: "npx remotion lambda functions", + relativePath: "docs/lambda/cli/functions.mdx", + compId: "articles-docs-lambda-cli-functions", + crumb: "Lambda CLI Reference", }, { - id: "recorder/upgrading", - title: "Upgrading", - relativePath: "docs/recorder/upgrading.mdx", - compId: "articles-docs-recorder-upgrading", - crumb: "Recorder", + id: "sites", + title: "npx remotion lambda sites", + relativePath: "docs/lambda/cli/sites.mdx", + compId: "articles-docs-lambda-cli-sites", + crumb: "Lambda CLI Reference", }, { - id: "register-root", - title: "registerRoot()", - relativePath: "docs/register-root.mdx", - compId: "articles-docs-register-root", - crumb: "API", + id: "render", + title: "npx remotion lambda render", + relativePath: "docs/lambda/cli/render.mdx", + compId: "articles-docs-lambda-cli-render", + crumb: "Lambda CLI Reference", }, { - id: "remotion", - title: "remotion", - relativePath: "docs/remotion.mdx", - compId: "articles-docs-remotion", - crumb: "Core API Reference", + id: "regions", + title: "npx remotion lambda regions", + relativePath: "docs/lambda/cli/regions.mdx", + compId: "articles-docs-lambda-cli-regions", + crumb: "Lambda CLI Reference", }, { - id: "render-all", - title: "Render all compositions", - relativePath: "docs/render-all.mdx", - compId: "articles-docs-render-all", - crumb: "Techniques", + id: "lambda/cli/compositions", + title: "npx remotion lambda compositions", + relativePath: "docs/lambda/cli/compositions.mdx", + compId: "articles-docs-lambda-cli-compositions", + crumb: "Lambda CLI Reference", }, { - id: "render-as-gif", - title: "Rendering GIFs", - relativePath: "docs/render-as-gif.mdx", - compId: "articles-docs-render-as-gif", - crumb: "Techniques", + id: "still", + title: "npx remotion lambda still", + relativePath: "docs/lambda/cli/still.mdx", + compId: "articles-docs-lambda-cli-still", + crumb: "Lambda CLI Reference", }, { - id: "render", - title: "Render your video", - relativePath: "docs/render.mdx", - compId: "articles-docs-render", - crumb: "How To", + id: "deleterender", + title: "deleteRender()", + relativePath: "docs/lambda/deleterender.mdx", + compId: "articles-docs-lambda-deleterender", + crumb: "Lambda API", }, { - id: "ensure-browser", - title: "ensureBrowser()", - relativePath: "docs/renderer/ensure-browser.mdx", - compId: "articles-docs-renderer-ensure-browser", - crumb: "@remotion/renderer", + id: "runtime", + title: "Runtime", + relativePath: "docs/lambda/runtime.mdx", + compId: "articles-docs-lambda-runtime", + crumb: "Lambda", }, { - id: "ensure-ffmpeg", - title: "ensureFfmpeg()", - relativePath: "docs/renderer/ensure-ffmpeg.mdx", - compId: "articles-docs-renderer-ensure-ffmpeg", - crumb: "@remotion/renderer", + id: "upgrading", + title: "Upgrading Lambda", + relativePath: "docs/lambda/upgrading.mdx", + compId: "articles-docs-lambda-upgrading", + crumb: null, }, { - id: "ensure-ffprobe", - title: "ensureFfprobe()", - relativePath: "docs/renderer/ensure-ffprobe.mdx", - compId: "articles-docs-renderer-ensure-ffprobe", - crumb: "@remotion/renderer", + id: "region-selection", + title: "Region selection", + relativePath: "docs/lambda/region-selection.mdx", + compId: "articles-docs-lambda-region-selection", + crumb: "Lambda", }, { - id: "extract-audio", - title: "extractAudio()", - relativePath: "docs/renderer/extract-audio.mdx", - compId: "articles-docs-renderer-extract-audio", - crumb: "@remotion/renderer", + id: "renderstillonlambda", + title: "renderStillOnLambda()", + relativePath: "docs/lambda/renderstillonlambda.mdx", + compId: "articles-docs-lambda-renderstillonlambda", + crumb: "Lambda API", }, { - id: "get-can-extract-frames-fast", - title: "getCanExtractFramesFast()", - relativePath: "docs/renderer/get-can-extract-frames-fast.mdx", - compId: "articles-docs-renderer-get-can-extract-frames-fast", - crumb: "@remotion/renderer", + id: "lambda/custom-layers", + title: "Custom Layers", + relativePath: "docs/lambda/custom-layers.mdx", + compId: "articles-docs-lambda-custom-layers", + crumb: "Lambda", }, { - id: "get-compositions", - title: "getCompositions()", - relativePath: "docs/renderer/get-compositions.mdx", - compId: "articles-docs-renderer-get-compositions", - crumb: "@remotion/renderer", + id: "changelog", + title: "Prerelease Changelog", + relativePath: "docs/lambda/changelog.mdx", + compId: "articles-docs-lambda-changelog", + crumb: null, }, { - id: "get-silent-parts", - title: "getSilentParts()", - relativePath: "docs/renderer/get-silent-parts.mdx", - compId: "articles-docs-renderer-get-silent-parts", - crumb: "@remotion/renderer", + id: "optimizing-cost", + title: "Optimizing for cost", + relativePath: "docs/lambda/cost.mdx", + compId: "articles-docs-lambda-cost", + crumb: "Lambda", }, { - id: "get-video-metadata", - title: "getVideoMetadata()", - relativePath: "docs/renderer/get-video-metadata.mdx", - compId: "articles-docs-renderer-get-video-metadata", - crumb: "@remotion/renderer", + id: "lambda/serverless-framework-integration", + title: "Using the Serverless Framework with Remotion Lambda", + relativePath: "docs/lambda/serverless-framework-integration.mdx", + compId: "articles-docs-lambda-serverless-framework-integration", + crumb: "@remotion/lambda", }, { - id: "make-cancel-signal", - title: "makeCancelSignal()", - relativePath: "docs/renderer/make-cancel-signal.mdx", - compId: "articles-docs-renderer-make-cancel-signal", - crumb: "@remotion/renderer", + id: "disk-size", + title: "Disk size", + relativePath: "docs/lambda/disk-size.mdx", + compId: "articles-docs-lambda-disk-size", + crumb: "Lambda", }, { - id: "open-browser", - title: "openBrowser()", - relativePath: "docs/renderer/open-browser.mdx", - compId: "articles-docs-renderer-open-browser", - crumb: "@remotion/renderer", + id: "lambda/without-iam/ec2", + title: "Authenticating Lambda with EC2", + relativePath: "docs/lambda/without-iam/ec2.mdx", + compId: "articles-docs-lambda-without-iam-ec2", + crumb: "@remotion/lambda", }, { - id: "render-frames", - title: "renderFrames()", - relativePath: "docs/renderer/render-frames.mdx", - compId: "articles-docs-renderer-render-frames", - crumb: "@remotion/renderer", + id: "lambda/without-iam/index", + title: "Using Lambda with IAM roles", + relativePath: "docs/lambda/without-iam/index.mdx", + compId: "articles-docs-lambda-without-iam-index", + crumb: "Lambda without IAM", }, { - id: "render-media", - title: "renderMedia()", - relativePath: "docs/renderer/render-media.mdx", - compId: "articles-docs-renderer-render-media", - crumb: "@remotion/renderer", + id: "lambda/without-iam/example", + title: "Example setup without IAM user", + relativePath: "docs/lambda/without-iam/example.mdx", + compId: "articles-docs-lambda-without-iam-example", + crumb: "IAM Roles Example", }, { - id: "render-still", - title: "renderStill()", - relativePath: "docs/renderer/render-still.mdx", - compId: "articles-docs-renderer-render-still", - crumb: "@remotion/renderer", + id: "downloadvideo", + title: "downloadVideo()", + relativePath: "docs/lambda/downloadvideo.mdx", + compId: "articles-docs-lambda-downloadvideo", + crumb: "Lambda API", }, { - id: "select-composition", - title: "selectComposition()", - relativePath: "docs/renderer/select-composition.mdx", - compId: "articles-docs-renderer-select-composition", - crumb: "@remotion/renderer", + id: "getawsclient", + title: "getAwsClient()", + relativePath: "docs/lambda/getawsclient.mdx", + compId: "articles-docs-lambda-getawsclient", + crumb: "Lambda API", }, { - id: "stitch-frames-to-video", - title: "stitchFramesToVideo()", - relativePath: "docs/renderer/stitch-frames-to-video.mdx", - compId: "articles-docs-renderer-stitch-frames-to-video", - crumb: "@remotion/renderer", + id: "lambda/php", + title: "Triggering renders from PHP", + relativePath: "docs/lambda/php.mdx", + compId: "articles-docs-lambda-php", + crumb: "@remotion/lambda", }, { - id: "renderer", - title: "@remotion/renderer", - relativePath: "docs/renderer.mdx", - compId: "articles-docs-renderer", - crumb: null, + id: "rendermediaonlambda", + title: "renderMediaOnLambda()", + relativePath: "docs/lambda/rendermediaonlambda.mdx", + compId: "articles-docs-lambda-rendermediaonlambda", + crumb: "Lambda API", }, { - id: "resources", - title: "List of resources", - relativePath: "docs/resources.mdx", - compId: "articles-docs-resources", - crumb: "Ecosystem", + id: "simulatepermissions", + title: "simulatePermissions()", + relativePath: "docs/lambda/simulatepermissions.mdx", + compId: "articles-docs-lambda-simulatepermissions", + crumb: "Lambda API", }, { - id: "rive/index", - title: "@remotion/rive", - relativePath: "docs/rive/index.mdx", - compId: "articles-docs-rive-index", - crumb: "Integrations", + id: "checklist", + title: "Production Checklist", + relativePath: "docs/lambda/checklist.mdx", + compId: "articles-docs-lambda-checklist", + crumb: "Lambda", }, { - id: "rive/remotionrivecanvas", - title: "<RemotionRiveCanvas>", - relativePath: "docs/rive/remotionrivecanvas.mdx", - compId: "articles-docs-rive-remotionrivecanvas", - crumb: "@remotion/rive", + id: "how-lambda-works", + title: "How Remotion Lambda works", + relativePath: "docs/lambda/how-lambda-works.mdx", + compId: "articles-docs-lambda-how-lambda-works", + crumb: "Lambda", }, { - id: "scaling", - title: "Output scaling", - relativePath: "docs/scaling.mdx", - compId: "articles-docs-scaling", - crumb: "How To", + id: "presignurl", + title: "presignUrl()", + relativePath: "docs/lambda/presignurl.mdx", + compId: "articles-docs-lambda-presignurl", + crumb: "Lambda API", }, { - id: "schemas", - title: "Defining a schema for your props", - relativePath: "docs/schemas.mdx", - compId: "articles-docs-schemas", - crumb: "How To", + id: "lambda/faq", + title: "FAQ", + relativePath: "docs/lambda/faq.mdx", + compId: "articles-docs-lambda-faq", + crumb: "Lambda", }, { - id: "security", - title: "Security Best Practices", - relativePath: "docs/security.mdx", - compId: "articles-docs-security", - crumb: "FAQ", + id: "lambda/api", + title: "@remotion/lambda", + relativePath: "docs/lambda/api.mdx", + compId: "articles-docs-lambda-api", + crumb: "Render videos without servers on AWS", }, { - id: "sequence", - title: "<Sequence>", - relativePath: "docs/sequence.mdx", - compId: "articles-docs-sequence", - crumb: "API", + id: "validatewebhooksignature", + title: "validateWebhookSignature()", + relativePath: "docs/lambda/validatewebhooksignature.mdx", + compId: "articles-docs-lambda-validatewebhooksignature", + crumb: "Lambda API", }, { - id: "reusability", - title: "Making components reusable", - relativePath: "docs/sequences.mdx", - compId: "articles-docs-sequences", - crumb: "The power of React", + id: "getfunctioninfo", + title: "getFunctionInfo()", + relativePath: "docs/lambda/getfunctioninfo.mdx", + compId: "articles-docs-lambda-getfunctioninfo", + crumb: "Lambda API", }, { - id: "series", - title: "<Series>", - relativePath: "docs/series.mdx", - compId: "articles-docs-series", - crumb: "API", + id: "setup", + title: "Setup", + relativePath: "docs/lambda/setup.mdx", + compId: "articles-docs-lambda-setup", + crumb: "Lambda", }, { - id: "shapes/circle", - title: "<Circle />", - relativePath: "docs/shapes/circle.mdx", - compId: "articles-docs-shapes-circle", - crumb: "@remotion/shapes", + id: "getfunctions", + title: "getFunctions()", + relativePath: "docs/lambda/getfunctions.mdx", + compId: "articles-docs-lambda-getfunctions", + crumb: "Lambda API", }, { - id: "shapes/ellipse", - title: "<Ellipse />", - relativePath: "docs/shapes/ellipse.mdx", - compId: "articles-docs-shapes-ellipse", - crumb: "@remotion/shapes", + id: "getcompositionsonlambda", + title: "getCompositionsOnLambda()", + relativePath: "docs/lambda/getcompositionsonlambda.mdx", + compId: "articles-docs-lambda-getcompositionsonlambda", + crumb: "Lambda API", }, { - id: "shapes/index", - title: "@remotion/shapes", - relativePath: "docs/shapes/index.mdx", - compId: "articles-docs-shapes-index", - crumb: "SVG component library", + id: "downloadmedia", + title: "downloadMedia()", + relativePath: "docs/lambda/downloadmedia.mdx", + compId: "articles-docs-lambda-downloadmedia", + crumb: "Lambda API", }, { - id: "shapes/make-circle", - title: "makeCircle()", - relativePath: "docs/shapes/make-circle.mdx", - compId: "articles-docs-shapes-make-circle", - crumb: "@remotion/shapes", + id: "lambda/s3-public-access", + title: "Cannot create a S3 bucket using Remotion", + relativePath: "docs/lambda/s3-public-access.mdx", + compId: "articles-docs-lambda-s3-public-access", + crumb: "DevOps advisory", }, { - id: "shapes/make-ellipse", - title: "makeEllipse()", - relativePath: "docs/shapes/make-ellipse.mdx", - compId: "articles-docs-shapes-make-ellipse", - crumb: "@remotion/shapes", + id: "lambda/limits", + title: "Lambda Limits", + relativePath: "docs/lambda/limits.mdx", + compId: "articles-docs-lambda-limits", + crumb: "Lambda", }, { - id: "shapes/make-pie", - title: "makePie()", - relativePath: "docs/shapes/make-pie.mdx", - compId: "articles-docs-shapes-make-pie", - crumb: "@remotion/shapes", + id: "lambda/sqs", + title: "Using Lambda with SQS", + relativePath: "docs/lambda/sqs.mdx", + compId: "articles-docs-lambda-sqs", + crumb: "@remotion/lambda", }, { - id: "shapes/make-polygon", - title: "makePolygon()", - relativePath: "docs/shapes/make-polygon.mdx", - compId: "articles-docs-shapes-make-polygon", - crumb: "@remotion/shapes", + id: "lambda/go", + title: "Triggering renders from Go", + relativePath: "docs/lambda/go.mdx", + compId: "articles-docs-lambda-go", + crumb: "@remotion/lambda", }, { - id: "shapes/make-rect", - title: "makeRect()", - relativePath: "docs/shapes/make-rect.mdx", - compId: "articles-docs-shapes-make-rect", - crumb: "@remotion/shapes", + id: "deploysite", + title: "deploySite()", + relativePath: "docs/lambda/deploysite.mdx", + compId: "articles-docs-lambda-deploysite", + crumb: "Lambda API", }, { - id: "shapes/make-star", - title: "makeStar()", - relativePath: "docs/shapes/make-star.mdx", - compId: "articles-docs-shapes-make-star", - crumb: "@remotion/shapes", + id: "uninstall", + title: "Uninstall Lambda", + relativePath: "docs/lambda/uninstall.mdx", + compId: "articles-docs-lambda-uninstall", + crumb: null, }, { - id: "shapes/make-triangle", - title: "makeTriangle()", - relativePath: "docs/shapes/make-triangle.mdx", - compId: "articles-docs-shapes-make-triangle", - crumb: "@remotion/shapes", + id: "lambda/bucket-naming", + title: "Bucket names in Remotion Lambda", + relativePath: "docs/lambda/bucket-naming.mdx", + compId: "articles-docs-lambda-bucket-naming", + crumb: "@remotion/lambda", }, { - id: "shapes/pie", - title: "<Pie />", - relativePath: "docs/shapes/pie.mdx", - compId: "articles-docs-shapes-pie", - crumb: "@remotion/shapes", + id: "naming-convention", + title: "Function naming convention", + relativePath: "docs/lambda/naming-convention.mdx", + compId: "articles-docs-lambda-naming-convention", + crumb: "Lambda", }, { - id: "shapes/polygon", - title: "<Polygon />", - relativePath: "docs/shapes/polygon.mdx", - compId: "articles-docs-shapes-polygon", - crumb: "@remotion/shapes", + id: "lambda/insights", + title: "Enable Lambda Insights", + relativePath: "docs/lambda/insights.mdx", + compId: "articles-docs-lambda-insights", + crumb: "Lambda", }, { - id: "shapes/rect", - title: "<Rect />", - relativePath: "docs/shapes/rect.mdx", - compId: "articles-docs-shapes-rect", - crumb: "@remotion/shapes", + id: "deletesite", + title: "deleteSite()", + relativePath: "docs/lambda/deletesite.mdx", + compId: "articles-docs-lambda-deletesite", + crumb: "Lambda API", }, { - id: "shapes/star", - title: "<Star />", - relativePath: "docs/shapes/star.mdx", - compId: "articles-docs-shapes-star", - crumb: "@remotion/shapes", + id: "compare/motion-canvas", + title: "How does Remotion compare to Motion Canvas?", + relativePath: "docs/compare/motion-canvas.mdx", + compId: "articles-docs-compare-motion-canvas", + crumb: "FAQ", }, { - id: "shapes/triangle", - title: "<Triangle />", - relativePath: "docs/shapes/triangle.mdx", - compId: "articles-docs-shapes-triangle", - crumb: "@remotion/shapes", + id: "api", + title: "API overview", + relativePath: "docs/api.mdx", + compId: "articles-docs-api", + crumb: null, }, { - id: "skia/enable-skia", - title: "enableSkia()", - relativePath: "docs/skia/enable-skia.mdx", - compId: "articles-docs-skia-enable-skia", - crumb: "@remotion/skia", + id: "staticfile", + title: "staticFile()", + relativePath: "docs/staticfile.mdx", + compId: "articles-docs-staticfile", + crumb: "API", }, { - id: "skia/skia-canvas", - title: "<SkiaCanvas />", - relativePath: "docs/skia/skia-canvas.mdx", - compId: "articles-docs-skia-skia-canvas", - crumb: "@remotion/skia", + id: "lottie-staticfile", + title: "Loading Lottie animations from staticFile()", + relativePath: "docs/lottie/lottie-staticfile.mdx", + compId: "articles-docs-lottie-lottie-staticfile", + crumb: "@remotion/lottie", }, { - id: "skia", - title: "@remotion/skia", - relativePath: "docs/skia/skia.mdx", - compId: "articles-docs-skia-skia", + id: "lottie-index", + title: "@remotion/lottie", + relativePath: "docs/lottie/index.mdx", + compId: "articles-docs-lottie-index", crumb: null, }, { - id: "slow-method-to-extract-frame", - title: "Slow method to extract frame", - relativePath: "docs/slow-method-to-extract-frame.mdx", - compId: "articles-docs-slow-method-to-extract-frame", - crumb: "Troubleshooting", + id: "getlottiemetadata", + title: "getLottieMetadata()", + relativePath: "docs/lottie/getlottiemetadata.mdx", + compId: "articles-docs-lottie-getlottiemetadata", + crumb: "@remotion/lottie", }, { - id: "spline", - title: "Import from Spline", - relativePath: "docs/spline.mdx", - compId: "articles-docs-spline", - crumb: "Integrations", + id: "lottie-remote", + title: "Loading Lottie animations from a URL", + relativePath: "docs/lottie/lottie-remote.mdx", + compId: "articles-docs-lottie-lottie-remote", + crumb: "@remotion/lottie", }, { - id: "spring", - title: "spring()", - relativePath: "docs/spring.mdx", - compId: "articles-docs-spring", - crumb: "API", + id: "lottie-comp", + title: "<Lottie>", + relativePath: "docs/lottie/lottie-comp.mdx", + compId: "articles-docs-lottie-lottie-comp", + crumb: "@remotion/lottie", }, { - id: "ssr-legacy", - title: "Server-Side Rendering (v1 and v2)", - relativePath: "docs/ssr-legacy.mdx", - compId: "articles-docs-ssr-legacy", - crumb: "Legacy docs", + id: "lottie-lottiefiles", + title: "Finding Lottie files to use", + relativePath: "docs/lottie/lottie-lottiefiles.mdx", + compId: "articles-docs-lottie-lottie-lottiefiles", + crumb: "Resources", }, { - id: "ssr-node", - title: "Rendering using SSR APIs", - relativePath: "docs/ssr-node.mdx", - compId: "articles-docs-ssr-node", - crumb: "Server-side rendering", + id: "overlay", + title: "Creating overlays", + relativePath: "docs/overlay.mdx", + compId: "articles-docs-overlay", + crumb: null, }, { - id: "ssr", - title: "Server-Side Rendering", - relativePath: "docs/ssr.mdx", - compId: "articles-docs-ssr", - crumb: "The power of", + id: "transparent-videos", + title: "Transparent videos", + relativePath: "docs/transparent-videos.mdx", + compId: "articles-docs-transparent-videos", + crumb: "Techniques", }, { - id: "standalone", - title: "Useful functions outside of Remotion", - relativePath: "docs/standalone.mdx", - compId: "articles-docs-standalone", - crumb: null, + id: "recorder/create", + title: "Create a new video", + relativePath: "docs/recorder/create.mdx", + compId: "articles-docs-recorder-create", + crumb: "Recorder", }, { - id: "staticfile-relative-paths", - title: "staticFile() does not support relative paths", - relativePath: "docs/static-file-relative-paths.mdx", - compId: "articles-docs-static-file-relative-paths", - crumb: "Troubleshooting", + id: "recorder/lambda-rendering", + title: "Render on Lambda", + relativePath: "docs/recorder/lambda-rendering.mdx", + compId: "articles-docs-recorder-lambda-rendering", + crumb: "Recorder", }, { - id: "staticfile-remote-urls", - title: "staticFile() does not support remote URLs", - relativePath: "docs/static-file-remote-urls.mdx", - compId: "articles-docs-static-file-remote-urls", - crumb: "Troubleshooting", + id: "recorder/exporting", + title: "Exporting a video", + relativePath: "docs/recorder/exporting.mdx", + compId: "articles-docs-recorder-exporting", + crumb: "Recorder", }, { - id: "staticfile", - title: "staticFile()", - relativePath: "docs/staticfile.mdx", - compId: "articles-docs-staticfile", - crumb: "API", + id: "recorder/external-recordings", + title: "External recordings", + relativePath: "docs/recorder/external-recordings.mdx", + compId: "articles-docs-recorder-external-recordings", + crumb: "Recorder", }, { - id: "still", - title: "<Still>", - relativePath: "docs/still.mdx", - compId: "articles-docs-still", - crumb: "API", + id: "recorder/captions", + title: "Generate captions", + relativePath: "docs/recorder/captions.mdx", + compId: "articles-docs-recorder-captions", + crumb: "Recorder", }, { - id: "stills", - title: "Still images", - relativePath: "docs/stills.mdx", - compId: "articles-docs-stills", - crumb: "How to generate", + id: "recorder/index", + title: "Remotion Recorder", + relativePath: "docs/recorder/index.mdx", + compId: "articles-docs-recorder-index", + crumb: "Templates", }, { - id: "studio/api", - title: "@remotion/studio", - relativePath: "docs/studio/api.mdx", - compId: "articles-docs-studio-api", - crumb: null, + id: "recorder/support", + title: "Get Help", + relativePath: "docs/recorder/support.mdx", + compId: "articles-docs-recorder-support", + crumb: "Recorder", }, { - id: "studio/delete-static-file", - title: "deleteStaticFile()", - relativePath: "docs/studio/delete-static-file.mdx", - compId: "articles-docs-studio-delete-static-file", - crumb: "@remotion/studio", + id: "recorder/source-control", + title: "Source Control", + relativePath: "docs/recorder/source-control.mdx", + compId: "articles-docs-recorder-source-control", + crumb: "Recorder", }, { - id: "studio/deploy-server", - title: "Deploy the Remotion Studio on a VPS", - relativePath: "docs/studio/deploy-server.mdx", - compId: "articles-docs-studio-deploy-server", - crumb: "Remotion Studio", + id: "recorder/buy", + title: "Buy", + relativePath: "docs/recorder/buy.mdx", + compId: "articles-docs-recorder-buy", + crumb: "Recorder", }, { - id: "studio/deploy-static", - title: "Deploy Remotion Studio as static site", - relativePath: "docs/studio/deploy-static.mdx", - compId: "articles-docs-studio-deploy-static", - crumb: "Remotion Studio", + id: "recorder/troubleshooting/failed-to-execute-get-video-metadata", + title: "Failed to execute getVideoMetadata()", + relativePath: + "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx", + compId: + "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata", + crumb: "Recorder", + }, + { + id: "recorder/troubleshooting/cannot-read-properties-of-undefined", + title: "Cannot read properties of undefined (reading 'decode')", + relativePath: + "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx", + compId: + "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined", + crumb: "Recorder", }, { - id: "studio/focus-default-props-path", - title: "focusDefaultPropsPath()", - relativePath: "docs/studio/focus-default-props-path.mdx", - compId: "articles-docs-studio-focus-default-props-path", - crumb: "@remotion/studio", + id: "recorder/record", + title: "Record a scene", + relativePath: "docs/recorder/record.mdx", + compId: "articles-docs-recorder-record", + crumb: "Recorder", }, { - id: "studio/get-static-files", - title: "getStaticFiles()", - relativePath: "docs/studio/get-static-files.mdx", - compId: "articles-docs-studio-get-static-files", - crumb: "@remotion/studio", + id: "recorder/gear", + title: "Our Gear", + relativePath: "docs/recorder/gear.mdx", + compId: "articles-docs-recorder-gear", + crumb: "Recorder", }, { - id: "studio/quick-switcher", - title: "Quick Switcher", - relativePath: "docs/studio/quick-switcher.mdx", - compId: "articles-docs-studio-quick-switcher", - crumb: "Remotion Studio", + id: "recorder/roadmap", + title: "Roadmap", + relativePath: "docs/recorder/roadmap.mdx", + compId: "articles-docs-recorder-roadmap", + crumb: "Recorder", }, { - id: "studio/reevaluate-composition", - title: "reevaluateComposition()", - relativePath: "docs/studio/reevaluate-composition.mdx", - compId: "articles-docs-studio-reevaluate-composition", - crumb: "@remotion/studio", + id: "recorder/upgrading", + title: "Upgrading", + relativePath: "docs/recorder/upgrading.mdx", + compId: "articles-docs-recorder-upgrading", + crumb: "Recorder", }, { - id: "studio/restart-studio", - title: "restartStudio()", - relativePath: "docs/studio/restart-studio.mdx", - compId: "articles-docs-studio-restart-studio", - crumb: "@remotion/studio", + id: "recorder/editing/scenes", + title: "Scenes", + relativePath: "docs/recorder/editing/scenes.mdx", + compId: "articles-docs-recorder-editing-scenes", + crumb: "Recorder", }, { - id: "studio/save-default-props", - title: "saveDefaultProps()", - relativePath: "docs/studio/save-default-props.mdx", - compId: "articles-docs-studio-save-default-props", - crumb: "@remotion/studio", + id: "recorder/editing/b-roll", + title: "B-Roll", + relativePath: "docs/recorder/editing/b-roll.mdx", + compId: "articles-docs-recorder-editing-b-roll", + crumb: "Recorder", }, { - id: "studio/shortcuts", - title: "Keyboard navigation", - relativePath: "docs/studio/shortcuts.mdx", - compId: "articles-docs-studio-shortcuts", - crumb: "Remotion Studio", + id: "recorder/editing/music", + title: "Adding music", + relativePath: "docs/recorder/editing/music.mdx", + compId: "articles-docs-recorder-editing-music", + crumb: "Recorder", }, { - id: "studio/studio", - title: "Starting the Studio", - relativePath: "docs/studio/studio.mdx", - compId: "articles-docs-studio-studio", - crumb: "Timeline basics", + id: "recorder/editing/chapters", + title: "Chapters", + relativePath: "docs/recorder/editing/chapters.mdx", + compId: "articles-docs-recorder-editing-chapters", + crumb: "Recorder", }, { - id: "studio/update-default-props", - title: "updateDefaultProps()", - relativePath: "docs/studio/update-default-props.mdx", - compId: "articles-docs-studio-update-default-props", - crumb: "@remotion/studio", + id: "recorder/editing/captions", + title: "Edit captions", + relativePath: "docs/recorder/editing/captions.mdx", + compId: "articles-docs-recorder-editing-captions", + crumb: "Recorder", }, { - id: "studio/watch-public-folder", - title: "watchPublicFolder()", - relativePath: "docs/studio/watch-public-folder.mdx", - compId: "articles-docs-studio-watch-public-folder", - crumb: "@remotion/studio", + id: "recorder/editing/cutting-clips", + title: "Cutting clips", + relativePath: "docs/recorder/editing/cutting-clips.mdx", + compId: "articles-docs-recorder-editing-cutting-clips", + crumb: "Recorder", }, { - id: "studio/watch-static-file", - title: "watchStaticFile()", - relativePath: "docs/studio/watch-static-file.mdx", - compId: "articles-docs-studio-watch-static-file", - crumb: "@remotion/studio", + id: "recorder/editing/editing", + title: "Start editing", + relativePath: "docs/recorder/editing/editing.mdx", + compId: "articles-docs-recorder-editing-editing", + crumb: "Recorder", }, { - id: "studio/write-static-file", - title: "writeStaticFile()", - relativePath: "docs/studio/write-static-file.mdx", - compId: "articles-docs-studio-write-static-file", - crumb: "@remotion/studio", + id: "recorder/editing/endcard", + title: "Endcard", + relativePath: "docs/recorder/editing/endcard.mdx", + compId: "articles-docs-recorder-editing-endcard", + crumb: "Recorder", }, { - id: "support", - title: "Support Policy", - relativePath: "docs/support.mdx", - compId: "articles-docs-support", - crumb: "Let us help you", + id: "recorder/editing/transitions", + title: "Transitions", + relativePath: "docs/recorder/editing/transitions.mdx", + compId: "articles-docs-recorder-editing-transitions", + crumb: "Recorder", }, { - id: "tailwind/enable-tailwind", - title: "enableTailwind()", - relativePath: "docs/tailwind/enable-tailwind.mdx", - compId: "articles-docs-tailwind-enable-tailwind", - crumb: "@remotion/tailwind", + id: "recorder/editing/silence-removal", + title: "Silence removal", + relativePath: "docs/recorder/editing/silence-removal.mdx", + compId: "articles-docs-recorder-editing-silence-removal", + crumb: "Recorder", }, { - id: "tailwind", - title: "@remotion/tailwind", - relativePath: "docs/tailwind/overview.mdx", - compId: "articles-docs-tailwind-overview", - crumb: null, + id: "recorder/editing/layout", + title: "Layout", + relativePath: "docs/recorder/editing/layout.mdx", + compId: "articles-docs-recorder-editing-layout", + crumb: "Recorder", }, { - id: "tailwind-legacy", - title: "TailwindCSS v2 (Legacy)", - relativePath: "docs/tailwind-2.mdx", - compId: "articles-docs-tailwind-2", - crumb: "Legacy docs", + id: "recorder/our-recorder", + title: "Our Recorder", + relativePath: "docs/recorder/our-recorder.mdx", + compId: "articles-docs-recorder-our-recorder", + crumb: "Recorder", }, { - id: "tailwind", - title: "TailwindCSS", - relativePath: "docs/tailwind.mdx", - compId: "articles-docs-tailwind", - crumb: "text-lg font-bold", + id: "recorder/before-you-buy", + title: "Before you buy", + relativePath: "docs/recorder/before-you-buy.mdx", + compId: "articles-docs-recorder-before-you-buy", + crumb: "Recorder", }, { - id: "target-closed", - title: "'Target closed' error message", - relativePath: "docs/target-closed.mdx", - compId: "articles-docs-target-closed", - crumb: "Troubleshooting", + id: "recorder/setup", + title: "Setup", + relativePath: "docs/recorder/setup.mdx", + compId: "articles-docs-recorder-setup", + crumb: "Recorder", }, { - id: "terminology/bundle", - title: "Remotion Bundle", - relativePath: "docs/terminology/bundle.mdx", - compId: "articles-docs-terminology-bundle", - crumb: "Terminology", + id: "fonts-api", + title: "@remotion/fonts", + relativePath: "docs/fonts-api/index.mdx", + compId: "articles-docs-fonts-api-index", + crumb: null, }, { - id: "terminology/cloud-run-url", - title: "Cloud Run URL", - relativePath: "docs/terminology/cloud-run-url.mdx", - compId: "articles-docs-terminology-cloud-run-url", - crumb: "Terminology", + id: "fonts-api/load-font", + title: "loadFont()", + relativePath: "docs/fonts-api/load-font.mdx", + compId: "articles-docs-fonts-api-load-font", + crumb: "@remotion/fonts", }, { - id: "terminology/composition", - title: "Composition", - relativePath: "docs/terminology/composition.mdx", - compId: "articles-docs-terminology-composition", - crumb: "Terminology", + id: "authoring-packages", + title: "Authoring a Remotion library", + relativePath: "docs/creating-a-library.mdx", + compId: "articles-docs-creating-a-library", + crumb: "How to", }, { - id: "terminology/concurrency", - title: "Concurrency", - relativePath: "docs/terminology/concurrency.mdx", - compId: "articles-docs-terminology-concurrency", - crumb: "Terminology", + id: "use-buffer-state", + title: "useBufferState()", + relativePath: "docs/use-buffer-state.mdx", + compId: "articles-docs-use-buffer-state", + crumb: "API", }, { - id: "terminology/entry-point", - title: "Entry point", - relativePath: "docs/terminology/entry-point.mdx", - compId: "articles-docs-terminology-entry-point", - crumb: "Terminology", + id: "transforms", + title: "Transforms", + relativePath: "docs/transforms.mdx", + compId: "articles-docs-transforms", + crumb: "The fundamentals", }, { - id: "terminology/input-props", - title: "Input Props", - relativePath: "docs/terminology/input-props.mdx", - compId: "articles-docs-terminology-input-props", - crumb: "Terminology", + id: "use-current-frame", + title: "useCurrentFrame()", + relativePath: "docs/use-current-frame.mdx", + compId: "articles-docs-use-current-frame", + crumb: "API", }, { - id: "terminology/player", - title: "Remotion Player", - relativePath: "docs/terminology/player.mdx", - compId: "articles-docs-terminology-player", - crumb: "Terminology", + id: "enable-scss/overview", + title: "@remotion/enable-scss", + relativePath: "docs/enable-scss/overview.mdx", + compId: "articles-docs-enable-scss-overview", + crumb: null, }, { - id: "terminology/public-dir", - title: "Public directory", - relativePath: "docs/terminology/public-dir.mdx", - compId: "articles-docs-terminology-public-dir", - crumb: "Terminology", + id: "enable-scss/enable-scss", + title: "enableScss()", + relativePath: "docs/enable-scss/enable-scss.mdx", + compId: "articles-docs-enable-scss-enable-scss", + crumb: "@remotion/enable-scss", }, { - id: "terminology/remotion-root", - title: "Remotion Root", - relativePath: "docs/terminology/remotion-root.mdx", - compId: "articles-docs-terminology-remotion-root", - crumb: "Terminology", + id: "3-0-migration", + title: "v3.0 Migration", + relativePath: "docs/3-0-migration.mdx", + compId: "articles-docs-3-0-migration", + crumb: "Version Upgrade", }, { - id: "terminology/root-file", - title: "Root file", - relativePath: "docs/terminology/root-file.mdx", - compId: "articles-docs-terminology-root-file", - crumb: "Terminology", + id: "quality", + title: "Quality Guide", + relativePath: "docs/quality.mdx", + compId: "articles-docs-quality", + crumb: "Make it look good", }, { - id: "terminology/sequence", - title: "Sequence", - relativePath: "docs/terminology/sequence.mdx", - compId: "articles-docs-terminology-sequence", - crumb: "Terminology", + id: "composition", + title: "<Composition>", + relativePath: "docs/composition.mdx", + compId: "articles-docs-composition", + crumb: "API", }, { - id: "terminology/serve-url", - title: "Serve URL", - relativePath: "docs/terminology/serve-url.mdx", - compId: "articles-docs-terminology-serve-url", - crumb: "Terminology", + id: "audio", + title: "<Audio>", + relativePath: "docs/audio.mdx", + compId: "articles-docs-audio", + crumb: "How to", }, { - id: "terminology/service-name", - title: "Service Name", - relativePath: "docs/terminology/service-name.mdx", - compId: "articles-docs-terminology-service-name", - crumb: "Terminology", + id: "visualize-audio", + title: "visualizeAudio()", + relativePath: "docs/visualize-audio.mdx", + compId: "articles-docs-visualize-audio", + crumb: "@remotion/media-utils", }, { - id: "terminology/studio", - title: "Remotion Studio", - relativePath: "docs/terminology/studio.mdx", - compId: "articles-docs-terminology-studio", - crumb: "Terminology", + id: "react-18", + title: "Upgrade to React 18", + relativePath: "docs/react-18.mdx", + compId: "articles-docs-react-18", + crumb: "The new and shiny", }, { - id: "terminology", - title: "Terminology", - relativePath: "docs/terminology.mdx", - compId: "articles-docs-terminology", - crumb: "The Remotion dictionary", + id: "cloudrun-alpha", + title: "Cloud Run Alpha", + relativePath: "docs/cloudrun-alpha.mdx", + compId: "articles-docs-cloudrun-alpha", + crumb: "Version Upgrade", }, { - id: "the-fundamentals", - title: "The fundamentals", - relativePath: "docs/the-fundamentals.mdx", - compId: "articles-docs-the-fundamentals", - crumb: "Getting started", + id: "easing", + title: "Easing", + relativePath: "docs/easing.mdx", + compId: "articles-docs-easing", + crumb: "API", }, { - id: "third-party", - title: "Third party integrations", - relativePath: "docs/third-party.mdx", - compId: "articles-docs-third-party", - crumb: "Integrations", + id: "resources", + title: "List of resources", + relativePath: "docs/resources.mdx", + compId: "articles-docs-resources", + crumb: "Ecosystem", }, { - id: "three-canvas", - title: "<ThreeCanvas>", - relativePath: "docs/three-canvas.mdx", - compId: "articles-docs-three-canvas", - crumb: "@remotion/three", + id: "schemas", + title: "Defining a schema for your props", + relativePath: "docs/schemas.mdx", + compId: "articles-docs-schemas", + crumb: "How To", }, { - id: "three", - title: "@remotion/three", - relativePath: "docs/three.mdx", - compId: "articles-docs-three", + id: "slow-method-to-extract-frame", + title: "Slow method to extract frame", + relativePath: "docs/slow-method-to-extract-frame.mdx", + compId: "articles-docs-slow-method-to-extract-frame", + crumb: "Troubleshooting", + }, + { + id: "lambda", + title: "@remotion/lambda", + relativePath: "docs/lambda.mdx", + compId: "articles-docs-lambda", crumb: null, }, { - id: "timeout", - title: "Debugging timeouts", - relativePath: "docs/timeout.mdx", - compId: "articles-docs-timeout", - crumb: "Troubleshooting", + id: "transitions/timings/springtiming", + title: "springTiming()", + relativePath: "docs/transitions/timings/springtiming.mdx", + compId: "articles-docs-transitions-timings-springtiming", + crumb: "@remotion/transitions - Timings", }, { - id: "transforms", - title: "Transforms", - relativePath: "docs/transforms.mdx", - compId: "articles-docs-transforms", - crumb: "The fundamentals", + id: "transitions/timings/index", + title: "Timings", + relativePath: "docs/transitions/timings/index.mdx", + compId: "articles-docs-transitions-timings-index", + crumb: "@remotion/transitions", }, { - id: "transitions/audio-transitions", - title: "Transitioning with audio", - relativePath: "docs/transitions/audio-transitions.mdx", - compId: "articles-docs-transitions-audio-transitions", + id: "transitions/timings/custom", + title: "Custom timings", + relativePath: "docs/transitions/timings/custom.mdx", + compId: "articles-docs-transitions-timings-custom", crumb: "@remotion/transitions", }, + { + id: "transitions/timings/lineartiming", + title: "linearTiming()", + relativePath: "docs/transitions/timings/lineartiming.mdx", + compId: "articles-docs-transitions-timings-lineartiming", + crumb: "@remotion/transitions - Timings", + }, { id: "transitions/index", title: "@remotion/transitions", @@ -3225,6 +3128,20 @@ export const articles = [ compId: "articles-docs-transitions-presentations-clock-wipe", crumb: "@remotion/transitions - Presentations", }, + { + id: "transitions/presentations/index", + title: "Presentations", + relativePath: "docs/transitions/presentations/index.mdx", + compId: "articles-docs-transitions-presentations-index", + crumb: "@remotion/transitions", + }, + { + id: "transitions/presentations/custom", + title: "Custom presentations", + relativePath: "docs/transitions/presentations/custom.mdx", + compId: "articles-docs-transitions-presentations-custom", + crumb: "@remotion/transitions", + }, { id: "transitions/presentations/cube", title: "cube()", @@ -3233,313 +3150,410 @@ export const articles = [ crumb: "@remotion/transitions - Presentations", }, { - id: "transitions/presentations/custom", - title: "Custom presentations", - relativePath: "docs/transitions/presentations/custom.mdx", - compId: "articles-docs-transitions-presentations-custom", - crumb: "@remotion/transitions", + id: "transitions/presentations/wipe", + title: "wipe()", + relativePath: "docs/transitions/presentations/wipe.mdx", + compId: "articles-docs-transitions-presentations-wipe", + crumb: "@remotion/transitions - Presentations", + }, + { + id: "transitions/presentations/fade", + title: "fade()", + relativePath: "docs/transitions/presentations/fade.mdx", + compId: "articles-docs-transitions-presentations-fade", + crumb: "@remotion/transitions - Presentations", + }, + { + id: "transitions/presentations/slide", + title: "slide()", + relativePath: "docs/transitions/presentations/slide.mdx", + compId: "articles-docs-transitions-presentations-slide", + crumb: "@remotion/transitions - Presentations", + }, + { + id: "transitions/presentations/flip", + title: "flip()", + relativePath: "docs/transitions/presentations/flip.mdx", + compId: "articles-docs-transitions-presentations-flip", + crumb: "@remotion/transitions - Presentations", + }, + { + id: "transitions/transitionseries", + title: "<TransitionSeries>", + relativePath: "docs/transitions/transitionseries.mdx", + compId: "articles-docs-transitions-transitionseries", + crumb: "@remotion/transitions", + }, + { + id: "transitions/audio-transitions", + title: "Transitioning with audio", + relativePath: "docs/transitions/audio-transitions.mdx", + compId: "articles-docs-transitions-audio-transitions", + crumb: "@remotion/transitions", + }, + { + id: "video-vs-offthreadvideo", + title: "<Video> vs. <OffthreadVideo>", + relativePath: "docs/video-vs-offthreadvideo.mdx", + compId: "articles-docs-video-vs-offthreadvideo", + crumb: "Comparison", + }, + { + id: "ffmpeg", + title: "Installing FFmpeg", + relativePath: "docs/ffmpeg.mdx", + compId: "articles-docs-ffmpeg", + crumb: "(you don't have to)", + }, + { + id: "video", + title: "<Video>", + relativePath: "docs/video.mdx", + compId: "articles-docs-video", + crumb: "API", + }, + { + id: "measure-spring", + title: "measureSpring()", + relativePath: "docs/measure-spring.mdx", + compId: "articles-docs-measure-spring", + crumb: "API", + }, + { + id: "javascript", + title: "Plain JavaScript", + relativePath: "docs/jsx-support.mdx", + compId: "articles-docs-jsx-support", + crumb: "How To", + }, + { + id: "stills", + title: "Still images", + relativePath: "docs/stills.mdx", + compId: "articles-docs-stills", + crumb: "How to generate", + }, + { + id: "get-help", + title: "Get help", + relativePath: "docs/ask-for-help.mdx", + compId: "articles-docs-ask-for-help", + crumb: null, }, { - id: "transitions/presentations/fade", - title: "fade()", - relativePath: "docs/transitions/presentations/fade.mdx", - compId: "articles-docs-transitions-presentations-fade", - crumb: "@remotion/transitions - Presentations", + id: "still", + title: "<Still>", + relativePath: "docs/still.mdx", + compId: "articles-docs-still", + crumb: "API", }, { - id: "transitions/presentations/flip", - title: "flip()", - relativePath: "docs/transitions/presentations/flip.mdx", - compId: "articles-docs-transitions-presentations-flip", - crumb: "@remotion/transitions - Presentations", + id: "preload-video", + title: "preloadVideo()", + relativePath: "docs/preload/preload-video.mdx", + compId: "articles-docs-preload-preload-video", + crumb: "@remotion/player", }, { - id: "transitions/presentations/index", - title: "Presentations", - relativePath: "docs/transitions/presentations/index.mdx", - compId: "articles-docs-transitions-presentations-index", - crumb: "@remotion/transitions", + id: "preload-audio", + title: "preloadAudio()", + relativePath: "docs/preload/preload-audio.mdx", + compId: "articles-docs-preload-preload-audio", + crumb: "@remotion/preload", }, { - id: "transitions/presentations/slide", - title: "slide()", - relativePath: "docs/transitions/presentations/slide.mdx", - compId: "articles-docs-transitions-presentations-slide", - crumb: "@remotion/transitions - Presentations", + id: "preload-image", + title: "preloadImage()", + relativePath: "docs/preload/preload-image.mdx", + compId: "articles-docs-preload-preload-image", + crumb: "@remotion/preload", }, { - id: "transitions/presentations/wipe", - title: "wipe()", - relativePath: "docs/transitions/presentations/wipe.mdx", - compId: "articles-docs-transitions-presentations-wipe", - crumb: "@remotion/transitions - Presentations", + id: "preload-font", + title: "preloadFont()", + relativePath: "docs/preload/preload-font.mdx", + compId: "articles-docs-preload-preload-font", + crumb: "@remotion/preload", }, { - id: "transitions/timings/custom", - title: "Custom timings", - relativePath: "docs/transitions/timings/custom.mdx", - compId: "articles-docs-transitions-timings-custom", - crumb: "@remotion/transitions", + id: "preload", + title: "@remotion/preload", + relativePath: "docs/preload/preload.mdx", + compId: "articles-docs-preload-preload", + crumb: null, }, { - id: "transitions/timings/index", - title: "Timings", - relativePath: "docs/transitions/timings/index.mdx", - compId: "articles-docs-transitions-timings-index", - crumb: "@remotion/transitions", + id: "version", + title: "VERSION", + relativePath: "docs/version.mdx", + compId: "articles-docs-version", + crumb: "API", }, { - id: "transitions/timings/lineartiming", - title: "linearTiming()", - relativePath: "docs/transitions/timings/lineartiming.mdx", - compId: "articles-docs-transitions-timings-lineartiming", - crumb: "@remotion/transitions - Timings", + id: "prereleases", + title: "Testing prereleases", + relativePath: "docs/prereleases.mdx", + compId: "articles-docs-prereleases", + crumb: "Only the brave", }, { - id: "transitions/timings/springtiming", - title: "springTiming()", - relativePath: "docs/transitions/timings/springtiming.mdx", - compId: "articles-docs-transitions-timings-springtiming", - crumb: "@remotion/transitions - Timings", + id: "artifact", + title: "<Artifact>", + relativePath: "docs/artifact.mdx", + compId: "articles-docs-artifact", + crumb: "API", }, { - id: "transitions/transitionseries", - title: "<TransitionSeries>", - relativePath: "docs/transitions/transitionseries.mdx", - compId: "articles-docs-transitions-transitionseries", - crumb: "@remotion/transitions", + id: "license", + title: "License & Pricing", + relativePath: "docs/license.mdx", + compId: "articles-docs-license", + crumb: null, }, { - id: "transitioning", - title: "Transitions", - relativePath: "docs/transitions.mdx", - compId: "articles-docs-transitions", + id: "fonts", + title: "Using fonts", + relativePath: "docs/fonts.mdx", + compId: "articles-docs-fonts", crumb: "Techniques", }, { - id: "transparent-videos", - title: "Transparent videos", - relativePath: "docs/transparent-videos.mdx", - compId: "articles-docs-transparent-videos", - crumb: "Techniques", + id: "enametoolong", + title: "ENAMETOOLONG", + relativePath: "docs/enametoolong.mdx", + compId: "articles-docs-enametoolong", + crumb: "Troubleshooting", }, { - id: "troubleshooting/background-image", - title: "Flickering when using background-image or mask-image", - relativePath: "docs/troubleshooting/background-image.mdx", - compId: "articles-docs-troubleshooting-background-image", - crumb: "Common mistakes", + id: "get-waveform-portion", + title: "getWaveformPortion()", + relativePath: "docs/get-waveform-portion.mdx", + compId: "articles-docs-get-waveform-portion", + crumb: "@remotion/media-utils", }, { - id: "broken-fast-refresh", - title: "Fast Refresh not working", - relativePath: "docs/troubleshooting/broken-fast-refresh.mdx", - compId: "articles-docs-troubleshooting-broken-fast-refresh", - crumb: "Troubleshooting", + id: "webpack", + title: "Custom Webpack config", + relativePath: "docs/overwriting-webpack-config.mdx", + compId: "articles-docs-overwriting-webpack-config", + crumb: "How To", }, { - id: "troubleshooting/browser-launch", - title: "Failed to launch the browser process", - relativePath: "docs/troubleshooting/browser-launch.mdx", - compId: "articles-docs-troubleshooting-browser-launch", - crumb: "Troubleshooting", + id: '"null"', + title: "<Experimental.Null>", + relativePath: "docs/null.mdx", + compId: "articles-docs-null", + crumb: "Experimental API", }, { - id: "troubleshooting/bundling-bundle", - title: "Calling bundle() in bundled code", - relativePath: "docs/troubleshooting/bundling-bundle.mdx", - compId: "articles-docs-troubleshooting-bundling-bundle", - crumb: "Troubleshooting", + id: "parameterized-rendering", + title: "Parameterized videos", + relativePath: "docs/parameterized-rendering.mdx", + compId: "articles-docs-parameterized-rendering", + crumb: "How To", }, { - id: "troubleshooting/could-not-be-parsed-as-a-value-list", - title: "The source provided could not be parsed as a value list", - relativePath: - "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx", - compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list", - crumb: "Browser quirks", + id: "get-audio-data", + title: "getAudioData()", + relativePath: "docs/get-audio-data.mdx", + compId: "articles-docs-get-audio-data", + crumb: "@remotion/media-utils", }, { - id: "troubleshooting/debug-failed-render", - title: "Debugging render failures", - relativePath: "docs/troubleshooting/debug-failed-render.mdx", - compId: "articles-docs-troubleshooting-debug-failed-render", - crumb: "Troubleshooting", + id: "clipper", + title: "<Experimental.Clipper>", + relativePath: "docs/clipper.mdx", + compId: "articles-docs-clipper", + crumb: "Experimental API", }, { - id: "defaultprops-too-big", - title: "defaultProps too big - could not serialize", - relativePath: "docs/troubleshooting/defaultprops-too-big.mdx", - compId: "articles-docs-troubleshooting-defaultprops-too-big", - crumb: "Troubleshooting", + id: "random", + title: "random()", + relativePath: "docs/random.mdx", + compId: "articles-docs-random", + crumb: "API", }, { - id: "troubleshooting/delay-render-proxy", - title: "Loading <Img> with src http://localhost:3000/proxy", - relativePath: "docs/troubleshooting/delay-render-proxy.mdx", - compId: "articles-docs-troubleshooting-delay-render-proxy", - crumb: "Troubleshooting", + id: "remotion", + title: "remotion", + relativePath: "docs/remotion.mdx", + compId: "articles-docs-remotion", + crumb: "Core API Reference", }, { - id: "loading-root-component", - title: "Root component Timeout", - relativePath: "docs/troubleshooting/loading-root-component.mdx", - compId: "articles-docs-troubleshooting-loading-root-component", - crumb: "Troubleshooting", + id: "folder", + title: "<Folder>", + relativePath: "docs/folder.mdx", + compId: "articles-docs-folder", + crumb: "API", }, { - id: "troubleshooting/nextjs-image", - title: "Flickering when using Next.js <Image> tag", - relativePath: "docs/troubleshooting/nextjs-image.mdx", - compId: "articles-docs-troubleshooting-nextjs-image", - crumb: "Common mistakes", + id: "video-manipulation", + title: "Video manipulation", + relativePath: "docs/video-manipulation.mdx", + compId: "articles-docs-video-manipulation", + crumb: "How To", }, { - id: "rosetta", - title: "Apple Silicon under Rosetta", - relativePath: "docs/troubleshooting/rosetta.mdx", - compId: "articles-docs-troubleshooting-rosetta", - crumb: "Troubleshooting", + id: "use-offthread-video-texture", + title: "useOffthreadVideoTexture()", + relativePath: "docs/use-offthread-video-texture.mdx", + compId: "articles-docs-use-offthread-video-texture", + crumb: "@remotion/three", }, { - id: "troubleshooting/sigkill", - title: "Process quit with signal SIGKILL", - relativePath: "docs/troubleshooting/sigkill.mdx", - compId: "articles-docs-troubleshooting-sigkill", - crumb: "Troubleshooting", + id: "animation-math", + title: "Animation math", + relativePath: "docs/animation-math.mdx", + compId: "articles-docs-animation-math", + crumb: "Techniques", + }, + { + id: "render-all", + title: "Render all compositions", + relativePath: "docs/render-all.mdx", + compId: "articles-docs-render-all", + crumb: "Techniques", + }, + { + id: "layout-utils/index", + title: "@remotion/layout-utils", + relativePath: "docs/layout-utils/index.mdx", + compId: "articles-docs-layout-utils-index", + crumb: null, }, { - id: "troubleshooting/timed-out-page-function", - title: "Timed out evaluating page function", - relativePath: "docs/troubleshooting/timed-out-page-function.mdx", - compId: "articles-docs-troubleshooting-timed-out-page-function", - crumb: "Troubleshooting", + id: "layout-utils/best-practices", + title: "Best practices for @remotion/layout-utils", + relativePath: "docs/layout-utils/best-practices.mdx", + compId: "articles-docs-layout-utils-best-practices", + crumb: "@remotion/layout-utils", }, { - id: "player-flicker", - title: "Avoiding flickering in <Player>", - relativePath: "docs/troubleshooting/video-flicker.mdx", - compId: "articles-docs-troubleshooting-video-flicker", - crumb: "Frame-perfection", + id: "layout-utils/measure-text", + title: "measureText()", + relativePath: "docs/layout-utils/measure-text.mdx", + compId: "articles-docs-layout-utils-measure-text", + crumb: "@remotion/layout-utils", }, { - id: "upgrading", - title: "Upgrading Remotion", - relativePath: "docs/upgrading.mdx", - compId: "articles-docs-upgrading", - crumb: "upgrade remotion", + id: "layout-utils/fill-text-box", + title: "fillTextBox()", + relativePath: "docs/layout-utils/fill-text-box.mdx", + compId: "articles-docs-layout-utils-fill-text-box", + crumb: "@remotion/layout-utils", }, { - id: "use-audio-data", - title: "useAudioData()", - relativePath: "docs/use-audio-data.mdx", - compId: "articles-docs-use-audio-data", - crumb: "@remotion/media-utils", + id: "layout-utils/fit-text", + title: "fitText()", + relativePath: "docs/layout-utils/fit-text.mdx", + compId: "articles-docs-layout-utils-fit-text", + crumb: "@remotion/layout-utils", }, { - id: "use-buffer-state", - title: "useBufferState()", - relativePath: "docs/use-buffer-state.mdx", - compId: "articles-docs-use-buffer-state", - crumb: "API", + id: "buffer-state", + title: "Display a buffer state", + relativePath: "docs/buffer-state.mdx", + compId: "articles-docs-buffer-state", + crumb: "Building video apps", }, { - id: "use-current-frame", - title: "useCurrentFrame()", - relativePath: "docs/use-current-frame.mdx", - compId: "articles-docs-use-current-frame", - crumb: "API", + id: "staticfile-remote-urls", + title: "staticFile() does not support remote URLs", + relativePath: "docs/static-file-remote-urls.mdx", + compId: "articles-docs-static-file-remote-urls", + crumb: "Troubleshooting", }, { - id: "use-current-scale", - title: "useCurrentScale()", - relativePath: "docs/use-current-scale.mdx", - compId: "articles-docs-use-current-scale", - crumb: "API", + id: "player/index", + title: "@remotion/player", + relativePath: "docs/player/index.mdx", + compId: "articles-docs-player-index", + crumb: null, }, { - id: "use-img-and-iframe", - title: "<Img>, <Video> and <Audio>", - relativePath: "docs/use-img-and-iframe.mdx", - compId: "articles-docs-use-img-and-iframe", - crumb: "Best practices", + id: "integration", + title: "Code sharing", + relativePath: "docs/player/player-integration.mdx", + compId: "articles-docs-player-player-integration", + crumb: "@remotion/player", }, { - id: "use-offthread-video-texture", - title: "useOffthreadVideoTexture()", - relativePath: "docs/use-offthread-video-texture.mdx", - compId: "articles-docs-use-offthread-video-texture", - crumb: "@remotion/three", + id: "player/preloading", + title: "Preloading assets", + relativePath: "docs/player/preloading.mdx", + compId: "articles-docs-player-preloading", + crumb: "@remotion/player", }, { - id: "use-video-config", - title: "useVideoConfig()", - relativePath: "docs/use-video-config.mdx", - compId: "articles-docs-use-video-config", - crumb: "API", + id: "best-practices", + title: "Player - Best practices", + relativePath: "docs/player/best-practices.mdx", + compId: "articles-docs-player-best-practices", + crumb: "@remotion/player", }, { - id: "use-video-texture", - title: "useVideoTexture()", - relativePath: "docs/use-video-texture.mdx", - compId: "articles-docs-use-video-texture", - crumb: "@remotion/three", + id: "autoplay", + title: "Combatting autoplay issues", + relativePath: "docs/player/autoplay.mdx", + compId: "articles-docs-player-autoplay", + crumb: "@remotion/player", }, { - id: "using-audio", - title: "Using audio", - relativePath: "docs/using-audio.mdx", - compId: "articles-docs-using-audio", - crumb: "Techniques", + id: "player/premounting", + title: "Premounting", + relativePath: "docs/player/premounting.mdx", + compId: "articles-docs-player-premounting", + crumb: "@remotion/player", }, { - id: "using-randomness", - title: "Using randomness", - relativePath: "docs/using-randomness.mdx", - compId: "articles-docs-using-randomness", - crumb: "Roll the dice", + id: "player/api", + title: "<Player>", + relativePath: "docs/player/api.mdx", + compId: "articles-docs-player-api", + crumb: "@remotion/player", }, { - id: "version-mismatch", - title: "Version mismatch", - relativePath: "docs/version-mismatch.mdx", - compId: "articles-docs-version-mismatch", - crumb: "How to fix a", + id: "thumbnail", + title: "<Thumbnail>", + relativePath: "docs/player/thumbnail.mdx", + compId: "articles-docs-player-thumbnail", + crumb: "@remotion/player", }, { - id: "version", - title: "VERSION", - relativePath: "docs/version.mdx", - compId: "articles-docs-version", - crumb: "API", + id: "current-time", + title: "Displaying the current time", + relativePath: "docs/player/current-time.mdx", + compId: "articles-docs-player-current-time", + crumb: "@remotion/player", }, { - id: "video-manipulation", - title: "Video manipulation", - relativePath: "docs/video-manipulation.mdx", - compId: "articles-docs-video-manipulation", - crumb: "How To", + id: "examples", + title: "Examples for @remotion/player", + relativePath: "docs/player/player-examples.mdx", + compId: "articles-docs-player-player-examples", + crumb: "@remotion/player", }, { - id: "video-uploads", - title: "Handling user video uploads", - relativePath: "docs/video-uploads.mdx", - compId: "articles-docs-video-uploads", - crumb: "Building video apps", + id: "installation", + title: "Installation", + relativePath: "docs/player/installation.mdx", + compId: "articles-docs-player-installation", + crumb: "@remotion/player", }, { - id: "video-vs-offthreadvideo", - title: "<Video> vs. <OffthreadVideo>", - relativePath: "docs/video-vs-offthreadvideo.mdx", - compId: "articles-docs-video-vs-offthreadvideo", - crumb: "Comparison", + id: "buffer-state", + title: "The Player buffer state", + relativePath: "docs/player/buffer-state.mdx", + compId: "articles-docs-player-buffer-state", + crumb: "Best practices", }, { - id: "video", - title: "<Video>", - relativePath: "docs/video.mdx", - compId: "articles-docs-video", - crumb: "API", + id: "scaling", + title: "Sizing", + relativePath: "docs/player/scaling.mdx", + compId: "articles-docs-player-scaling", + crumb: "@remotion/player", }, { id: "visual-editing", @@ -3549,45 +3563,45 @@ export const articles = [ crumb: "How To", }, { - id: "visualize-audio", - title: "visualizeAudio()", - relativePath: "docs/visualize-audio.mdx", - compId: "articles-docs-visualize-audio", - crumb: "@remotion/media-utils", + id: "4-0-migration", + title: "v4.0 Migration", + relativePath: "docs/4-0-migration.mdx", + compId: "articles-docs-4-0-migration", + crumb: "Version Upgrade", }, { - id: "watchstaticfile", - title: "watchStaticFile()", - relativePath: "docs/watch-static-file.mdx", - compId: "articles-docs-watch-static-file", - crumb: "API", + id: "get-gif-duration-in-seconds", + title: "getGifDurationInSeconds()", + relativePath: "docs/gif/get-gif-duration-in-seconds.mdx", + compId: "articles-docs-gif-get-gif-duration-in-seconds", + crumb: "@remotion/gif", }, { - id: "wrong-composition-mount", - title: "Wrongly mounted <Composition>", - relativePath: "docs/wrong-composition-mount.mdx", - compId: "articles-docs-wrong-composition-mount", - crumb: "Troubleshooting", + id: "gif/preload-gif", + title: "preloadGif()", + relativePath: "docs/gif/preload-gif.mdx", + compId: "articles-docs-gif-preload-gif", + crumb: "@remotion/gif", }, { - id: "zod-types/index", - title: "@remotion/zod-types", - relativePath: "docs/zod-types/index.mdx", - compId: "articles-docs-zod-types-index", - crumb: "Schema", + id: "gif/index", + title: "@remotion/gif", + relativePath: "docs/gif/index.mdx", + compId: "articles-docs-gif-index", + crumb: null, }, { - id: "zod-types/z-color", - title: "zColor()", - relativePath: "docs/zod-types/z-color.mdx", - compId: "articles-docs-zod-types-z-color", - crumb: null, + id: "gif/gif", + title: "<Gif>", + relativePath: "docs/gif/gif.mdx", + compId: "articles-docs-gif-gif", + crumb: "@remotion/gif", }, { - id: "zod-types/z-textarea", - title: "zTextarea()", - relativePath: "docs/zod-types/z-textarea.mdx", - compId: "articles-docs-zod-types-z-textarea", - crumb: null, + id: "scaling", + title: "Output scaling", + relativePath: "docs/scaling.mdx", + compId: "articles-docs-scaling", + crumb: "How To", }, ]; diff --git a/packages/docs/static/generated/articles-docs-artifact.png b/packages/docs/static/generated/articles-docs-artifact.png new file mode 100644 index 0000000000000000000000000000000000000000..252834e9a8e518e6f7017fce56038dfe226857c5 GIT binary patch literal 32218 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 literal 0 HcmV?d00001 diff --git a/packages/docs/static/generated/articles-docs-miscellaneous-linux-dependencies.png b/packages/docs/static/generated/articles-docs-miscellaneous-linux-dependencies.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc841acfe32d8413f4d8bf85b4c01a4eea76d66 GIT binary patch literal 37868 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 literal 0 HcmV?d00001 From 7fc59d875742c976a141fa443a06190cc6c2bb6b Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 19 Jun 2024 15:29:40 +0200 Subject: [PATCH 15/36] awesome --- packages/cli/src/on-artifact.ts | 10 ++++++++-- packages/core/src/Artifact.tsx | 2 +- packages/core/src/CompositionManager.tsx | 2 +- packages/core/src/validation/validate-artifact.ts | 6 +++--- .../example/src/SubtitleArtifact/SubtitleArtifact.tsx | 9 ++++++++- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts index 7fa19e4c811..b8e7659426d 100644 --- a/packages/cli/src/on-artifact.ts +++ b/packages/cli/src/on-artifact.ts @@ -14,12 +14,18 @@ export const handleOnArtifact = ( process.cwd(), artifact.filename.replace('/', path.sep), ); - writeFileSync(artifact.filename, artifact.content); + + const stringOrUintArray = + typeof artifact.content === 'string' + ? artifact.content + : new Uint8Array(Object.values(artifact.content)); + + writeFileSync(artifact.filename, stringOrUintArray); initialProgress.received.push({ absoluteOutputDestination, filename: artifact.filename, - sizeInBytes: artifact.content.length, + sizeInBytes: stringOrUintArray.length, }); }; diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx index cca65033b33..28f9fe04461 100644 --- a/packages/core/src/Artifact.tsx +++ b/packages/core/src/Artifact.tsx @@ -6,7 +6,7 @@ import {useCurrentFrame} from './use-current-frame'; export const Artifact: React.FC<{ readonly filename: string; - readonly content: string; + readonly content: string | Uint8Array; }> = ({filename, content}) => { const {registerRenderAsset, unregisterRenderAsset} = useContext(RenderAssetManager); diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx index 7bed4510d9d..728851aa192 100644 --- a/packages/core/src/CompositionManager.tsx +++ b/packages/core/src/CompositionManager.tsx @@ -143,7 +143,7 @@ export type ArtifactAsset = { type: 'artifact'; id: string; filename: string; - content: string; + content: string | Uint8Array; frame: number; }; diff --git a/packages/core/src/validation/validate-artifact.ts b/packages/core/src/validation/validate-artifact.ts index 162e177b093..f3f4232b291 100644 --- a/packages/core/src/validation/validate-artifact.ts +++ b/packages/core/src/validation/validate-artifact.ts @@ -19,13 +19,13 @@ export const validateArtifactFilename = (filename: unknown) => { }; const validateContent = (content: unknown) => { - if (typeof content !== 'string') { + if (typeof content !== 'string' && !(content instanceof Uint8Array)) { throw new TypeError( - `The "content" must be a string, but you passed a value of type ${typeof content}`, + `The "content" must be a string or Uint8Array, but you passed a value of type ${typeof content}`, ); } - if (content.trim() === '') { + if (typeof content === 'string' && content.trim() === '') { throw new Error('The `content` must not be empty'); } }; diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx index 14ca160c160..fde60d1154b 100644 --- a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx +++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx @@ -5,6 +5,13 @@ export const SubtitleArtifact: React.FC = () => { const frame = useCurrentFrame(); return frame === 0 ? ( - <Artifact content="Hello World!" filename="hello.txt" /> + <Artifact + content={ + new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) + } + filename="hello-uint8.txt" + /> + ) : frame === 1 ? ( + <Artifact content="Hello World" filename="hello.txt" /> ) : null; }; From cff3cc991c69074259c4a40d1d4dc70dd1758ed2 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 19 Jun 2024 18:58:24 +0200 Subject: [PATCH 16/36] support uint8array --- packages/cli/src/on-artifact.ts | 9 +--- .../functions/helpers/serialize-artifact.ts | 51 +++++++++++++++++++ .../src/functions/helpers/stream-renderer.ts | 14 ++--- packages/lambda/src/functions/launch.ts | 4 +- packages/lambda/src/functions/renderer.ts | 3 +- .../src/functions/streaming/streaming.ts | 4 +- packages/renderer/src/index.ts | 8 +-- packages/renderer/src/render-frames.ts | 17 ++++--- packages/renderer/src/serialize-artifact.ts | 5 ++ 9 files changed, 84 insertions(+), 31 deletions(-) create mode 100644 packages/lambda/src/functions/helpers/serialize-artifact.ts create mode 100644 packages/renderer/src/serialize-artifact.ts diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts index b8e7659426d..595d3d946fb 100644 --- a/packages/cli/src/on-artifact.ts +++ b/packages/cli/src/on-artifact.ts @@ -15,17 +15,12 @@ export const handleOnArtifact = ( artifact.filename.replace('/', path.sep), ); - const stringOrUintArray = - typeof artifact.content === 'string' - ? artifact.content - : new Uint8Array(Object.values(artifact.content)); - - writeFileSync(artifact.filename, stringOrUintArray); + writeFileSync(artifact.filename, artifact.content); initialProgress.received.push({ absoluteOutputDestination, filename: artifact.filename, - sizeInBytes: stringOrUintArray.length, + sizeInBytes: artifact.content.length, }); }; diff --git a/packages/lambda/src/functions/helpers/serialize-artifact.ts b/packages/lambda/src/functions/helpers/serialize-artifact.ts new file mode 100644 index 00000000000..f65a856928c --- /dev/null +++ b/packages/lambda/src/functions/helpers/serialize-artifact.ts @@ -0,0 +1,51 @@ +import type {EmittedArtifact} from '@remotion/renderer'; + +export type SerializedArtifact = { + filename: string; + stringContent: string; + frame: number; + binary: boolean; +}; + +export const deserializeArtifact = ( + serializedArtifact: SerializedArtifact, +): EmittedArtifact => { + if (serializedArtifact.binary) { + const encoder = new TextEncoder(); + const content = encoder.encode(atob(serializedArtifact.stringContent)); + + return { + filename: serializedArtifact.filename, + content, + frame: serializedArtifact.frame, + }; + } + + return { + filename: serializedArtifact.filename, + content: serializedArtifact.stringContent, + frame: serializedArtifact.frame, + }; +}; + +export const serializeArtifact = ( + artifact: EmittedArtifact, +): SerializedArtifact => { + if (artifact.content instanceof Uint8Array) { + const decoder = new TextDecoder('utf8'); + const b64encoded = btoa(decoder.decode(artifact.content)); + return { + filename: artifact.filename, + stringContent: b64encoded, + frame: artifact.frame, + binary: true, + }; + } + + return { + filename: artifact.filename, + stringContent: artifact.content, + frame: artifact.frame, + binary: false, + }; +}; diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts index e7a041aaaa3..ea99ff99f60 100644 --- a/packages/lambda/src/functions/helpers/stream-renderer.ts +++ b/packages/lambda/src/functions/helpers/stream-renderer.ts @@ -1,4 +1,4 @@ -import type {EmittedAsset, LogLevel} from '@remotion/renderer'; +import type {EmittedArtifact, LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {writeFileSync} from 'fs'; import {join} from 'path'; @@ -8,6 +8,7 @@ import {callLambdaWithStreaming} from '../../shared/call-lambda'; import type {OnMessage} from '../streaming/streaming'; import {getCurrentRegionInFunction} from './get-current-region'; import type {OverallProgressHelper} from './overall-render-progress'; +import {deserializeArtifact} from './serialize-artifact'; type StreamRendererResponse = | { @@ -34,7 +35,7 @@ const streamRenderer = ({ overallProgress: OverallProgressHelper; files: string[]; logLevel: LogLevel; - onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean}; + onArtifact: (asset: EmittedArtifact) => {alreadyExisted: boolean}; }) => { if (payload.type !== LambdaRoutines.renderer) { throw new Error('Expected renderer type'); @@ -100,13 +101,14 @@ const streamRenderer = ({ } if (message.type === 'artifact-emitted') { + const artifact = deserializeArtifact(message.payload.artifact); RenderInternals.Log.info( {indent: false, logLevel}, `Received artifact on frame ${message.payload.artifact.frame}:`, - message.payload.artifact.filename, - message.payload.artifact.content.length + 'bytes.', + artifact.filename, + artifact.content.length + 'bytes.', ); - const {alreadyExisted} = onArtifact(message.payload.artifact); + const {alreadyExisted} = onArtifact(artifact); if (alreadyExisted) { return resolve({ type: 'error', @@ -191,7 +193,7 @@ export const streamRendererFunctionWithRetry = async ({ overallProgress: OverallProgressHelper; files: string[]; logLevel: LogLevel; - onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean}; + onArtifact: (asset: EmittedArtifact) => {alreadyExisted: boolean}; }): Promise<unknown> => { if (payload.type !== LambdaRoutines.renderer) { throw new Error('Expected renderer type'); diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts index f42a3873c29..2a4932f1d1a 100644 --- a/packages/lambda/src/functions/launch.ts +++ b/packages/lambda/src/functions/launch.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ -import type {EmittedAsset, LogOptions} from '@remotion/renderer'; +import type {EmittedArtifact, LogOptions} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {existsSync, mkdirSync, rmSync} from 'fs'; import {join} from 'path'; @@ -356,7 +356,7 @@ const innerLaunchHandler = async ({ const files: string[] = []; - const onArtifact = (artifact: EmittedAsset): {alreadyExisted: boolean} => { + const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => { if ( overallProgress .getReceivedAssets() diff --git a/packages/lambda/src/functions/renderer.ts b/packages/lambda/src/functions/renderer.ts index 439c63136d5..f6ac91c2b0a 100644 --- a/packages/lambda/src/functions/renderer.ts +++ b/packages/lambda/src/functions/renderer.ts @@ -28,6 +28,7 @@ import {getCurrentRegionInFunction} from './helpers/get-current-region'; import {startLeakDetection} from './helpers/leak-detection'; import {onDownloadsHelper} from './helpers/on-downloads-logger'; import type {RequestContext} from './helpers/request-context'; +import {serializeArtifact} from './helpers/serialize-artifact'; import {timer} from './helpers/timer'; import {getTmpDirStateIfENoSp} from './helpers/write-lambda-error'; import type {OnStream} from './streaming/streaming'; @@ -183,7 +184,7 @@ const renderHandler = async ({ onStream({ type: 'artifact-emitted', payload: { - artifact, + artifact: serializeArtifact(artifact), }, }) .then(() => { diff --git a/packages/lambda/src/functions/streaming/streaming.ts b/packages/lambda/src/functions/streaming/streaming.ts index 71966376ef3..d9215838dec 100644 --- a/packages/lambda/src/functions/streaming/streaming.ts +++ b/packages/lambda/src/functions/streaming/streaming.ts @@ -1,5 +1,5 @@ -import type {EmittedAsset} from '@remotion/renderer'; import {makeStreamPayloadMessage} from '@remotion/streaming'; +import type {SerializedArtifact} from '../helpers/serialize-artifact'; import type {LambdaErrorInfo} from '../helpers/write-lambda-error'; import type {RenderStillLambdaResponsePayload} from '../still'; @@ -90,7 +90,7 @@ export type StreamingPayload = | { type: typeof artifactEmitted; payload: { - artifact: EmittedAsset; + artifact: SerializedArtifact; }; }; diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts index 9b3973414ee..912d26d792c 100644 --- a/packages/renderer/src/index.ts +++ b/packages/renderer/src/index.ts @@ -108,12 +108,7 @@ export {X264Preset} from './options/x264-preset'; export {PixelFormat} from './pixel-format'; export {RemotionServer} from './prepare-server'; export {ProResProfile} from './prores-profile'; -export { - EmittedArtifact as EmittedAsset, - OnArtifact, - RenderFramesOptions, - renderFrames, -} from './render-frames'; +export {OnArtifact, RenderFramesOptions, renderFrames} from './render-frames'; export { InternalRenderMediaOptions, RenderMediaOnProgress, @@ -127,6 +122,7 @@ export { SelectCompositionOptions, selectComposition, } from './select-composition'; +export {EmittedArtifact} from './serialize-artifact'; export { StitchFramesToVideoOptions, stitchFramesToVideo, diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index 5b8dc820233..5bdc261ed56 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -53,6 +53,7 @@ import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate'; import type {BrowserReplacer} from './replace-browser'; import {handleBrowserCrash} from './replace-browser'; import {seekToFrame} from './seek-to-frame'; +import type {EmittedArtifact} from './serialize-artifact'; import {setPropsAndEnv} from './set-props-and-env'; import {takeFrameAndCompose} from './take-frame-and-compose'; import {truthy} from './truthy'; @@ -106,12 +107,6 @@ type InternalRenderFramesOptions = { onArtifact: OnArtifact | null; } & ToOptions<typeof optionsMap.renderFrames>; -export type EmittedArtifact = { - filename: string; - content: string | Uint8Array; - frame: number; -}; - type InnerRenderFramesOptions = { onStart: null | ((data: OnStartData) => void); onFrameUpdate: @@ -531,7 +526,15 @@ const innerRenderFrames = async ({ } } - onArtifact?.(artifact); + const stringOrUintArray = + typeof artifact.content === 'string' + ? artifact.content + : new Uint8Array(Object.values(artifact.content)); + + onArtifact?.({ + ...artifact, + content: stringOrUintArray, + }); } const compressedAssets = audioAndVideoAssets.map((asset) => { diff --git a/packages/renderer/src/serialize-artifact.ts b/packages/renderer/src/serialize-artifact.ts new file mode 100644 index 00000000000..45b067da23b --- /dev/null +++ b/packages/renderer/src/serialize-artifact.ts @@ -0,0 +1,5 @@ +export type EmittedArtifact = { + filename: string; + content: string | Uint8Array; + frame: number; +}; From 3821e955647f2428486465d52fa034f75d676741 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 19 Jun 2024 19:21:18 +0200 Subject: [PATCH 17/36] more abstractions --- packages/cli/src/render-flows/still.ts | 20 ++++-- .../functions/render-media-single-thread.ts | 5 +- .../functions/render-still-single-thread.ts | 3 + packages/lambda/src/functions/still.ts | 65 +++++++++++++++++-- packages/renderer/src/collect-assets.ts | 41 ++++++++++++ packages/renderer/src/render-frames.ts | 10 +-- packages/renderer/src/render-still.ts | 26 +++++++- .../renderer/src/take-frame-and-compose.ts | 13 +--- 8 files changed, 147 insertions(+), 36 deletions(-) create mode 100644 packages/renderer/src/collect-assets.ts diff --git a/packages/cli/src/render-flows/still.ts b/packages/cli/src/render-flows/still.ts index 3406481e2cd..83f86725327 100644 --- a/packages/cli/src/render-flows/still.ts +++ b/packages/cli/src/render-flows/still.ts @@ -14,6 +14,7 @@ import type { AggregateRenderProgress, JobProgressCallback, } from '@remotion/studio-server'; +import type {ArtifactProgress} from '@remotion/studio-shared'; import {existsSync, mkdirSync} from 'node:fs'; import path from 'node:path'; import {NoReactInternals} from 'remotion/no-react'; @@ -27,6 +28,7 @@ import {getCompositionWithDimensionOverride} from '../get-composition-with-dimen import {makeHyperlink} from '../hyperlinks/make-link'; import {Log} from '../log'; import {makeOnDownload} from '../make-on-download'; +import {handleOnArtifact} from '../on-artifact'; import {parsedCli, quietFlagProvided} from '../parsed-cli'; import type {OverwriteableCliOutput} from '../progress-bar'; import { @@ -124,15 +126,13 @@ export const renderStillFlow = async ({ const updateRenderProgress = ({ newline, printToConsole, - isUsingParallelEncoding, }: { newline: boolean; printToConsole: boolean; - isUsingParallelEncoding: boolean; }) => { const {output, progress, message} = makeRenderingAndStitchingProgress({ prog: aggregate, - isUsingParallelEncoding, + isUsingParallelEncoding: false, }); if (printToConsole) { renderProgress.update(updatesDontOverwrite ? message : output, newline); @@ -176,7 +176,6 @@ export const renderStillFlow = async ({ updateRenderProgress({ newline: false, printToConsole: true, - isUsingParallelEncoding: false, }); }, indentOutput: indent, @@ -299,6 +298,8 @@ export const renderStillFlow = async ({ const renderStart = Date.now(); + let artifactState: ArtifactProgress = {received: []}; + aggregate.rendering = { frames: 0, doneIn: null, @@ -309,7 +310,6 @@ export const renderStillFlow = async ({ updateRenderProgress({ newline: false, printToConsole: true, - isUsingParallelEncoding: false, }); const onDownload: RenderMediaOnDownload = makeOnDownload({ @@ -321,6 +321,14 @@ export const renderStillFlow = async ({ isUsingParallelEncoding: false, }); + const {onArtifact} = handleOnArtifact(artifactState, (progress) => { + artifactState = progress; + updateRenderProgress({ + newline: false, + printToConsole: true, + }); + }); + await RenderInternals.internalRenderStill({ composition: config, frame: stillFrame, @@ -352,6 +360,7 @@ export const renderStillFlow = async ({ offthreadVideoCacheSizeInBytes, binariesDirectory, onBrowserDownload, + onArtifact, }); aggregate.rendering = { @@ -363,7 +372,6 @@ export const renderStillFlow = async ({ updateRenderProgress({ newline: true, printToConsole: true, - isUsingParallelEncoding: false, }); Log.info( {indent, logLevel}, diff --git a/packages/cloudrun/src/functions/render-media-single-thread.ts b/packages/cloudrun/src/functions/render-media-single-thread.ts index 625d3421063..2b977ee6f71 100644 --- a/packages/cloudrun/src/functions/render-media-single-thread.ts +++ b/packages/cloudrun/src/functions/render-media-single-thread.ts @@ -64,9 +64,8 @@ export const renderMediaSingleThread = async ( enableMultiProcessOnLinux: true, }; - const onArtifact: OnArtifact = (artifact) => { - // TODO: Do something with the artifact - console.log(artifact); + const onArtifact: OnArtifact = () => { + throw new Error('Emitting artifacts is not supported in Cloud Run'); }; await RenderInternals.internalRenderMedia({ diff --git a/packages/cloudrun/src/functions/render-still-single-thread.ts b/packages/cloudrun/src/functions/render-still-single-thread.ts index d822f768cb4..a650953d8e3 100644 --- a/packages/cloudrun/src/functions/render-still-single-thread.ts +++ b/packages/cloudrun/src/functions/render-still-single-thread.ts @@ -98,6 +98,9 @@ export const renderStillSingleThread = async ( onBrowserDownload: () => { throw new Error('Should not download a browser in Cloud Run'); }, + onArtifact: () => { + throw new Error('Emitting artifacts is not supported in Cloud Run'); + }, }); Log.info({indent: false, logLevel: body.logLevel}, 'Still rendered'); diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts index 6df26d9a4a1..c741ed64e49 100644 --- a/packages/lambda/src/functions/still.ts +++ b/packages/lambda/src/functions/still.ts @@ -1,4 +1,4 @@ -import type {StillImageFormat} from '@remotion/renderer'; +import type {EmittedArtifact, StillImageFormat} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import fs from 'node:fs'; import path from 'node:path'; @@ -18,6 +18,7 @@ import type { import { LambdaRoutines, MAX_EPHEMERAL_STORAGE_IN_MB, + artifactName, overallProgressKey, } from '../shared/constants'; import {convertToServeUrl} from '../shared/convert-to-serve-url'; @@ -39,6 +40,7 @@ import {getCurrentRegionInFunction} from './helpers/get-current-region'; import {getOutputUrlFromMetadata} from './helpers/get-output-url-from-metadata'; import {lambdaWriteFile} from './helpers/io'; import {onDownloadsHelper} from './helpers/on-downloads-logger'; +import type {ReceivedAsset} from './helpers/overall-render-progress'; import {makeInitialOverallRenderProgress} from './helpers/overall-render-progress'; import {validateComposition} from './helpers/validate-composition'; import type {OnStream} from './streaming/streaming'; @@ -194,6 +196,58 @@ const innerStillHandler = async ({ throw new Error('Should not download a browser in Lambda'); }; + const receivedAssets: ReceivedAsset[] = []; + + const {key, renderBucketName, customCredentials} = getExpectedOutName( + renderMetadata, + bucketName, + getCredentialsFromOutName(lambdaParams.outName), + ); + + const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => { + if (receivedAssets.find((a) => a.filename === artifact.filename)) { + return {alreadyExisted: true}; + } + + const s3Key = artifactName(renderMetadata.renderId, artifact.filename); + receivedAssets.push({ + filename: artifact.filename, + sizeInBytes: artifact.content.length, + s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, + }); + + const startTime = Date.now(); + RenderInternals.Log.info( + {indent: false, logLevel: lambdaParams.logLevel}, + 'Writing artifact ' + artifact.filename + ' to S3', + ); + lambdaWriteFile({ + bucketName: renderBucketName, + key: s3Key, + body: artifact.content, + region, + privacy: lambdaParams.privacy, + expectedBucketOwner, + downloadBehavior: lambdaParams.downloadBehavior, + customCredentials, + }) + .then(() => { + RenderInternals.Log.info( + {indent: false, logLevel: lambdaParams.logLevel}, + `Wrote artifact to S3 in ${Date.now() - startTime}ms`, + ); + }) + .catch((err) => { + // TODO: Handle error + RenderInternals.Log.error( + {indent: false, logLevel: lambdaParams.logLevel}, + 'Failed to write artifact to S3', + err, + ); + }); + return {alreadyExisted: false}; + }; + await RenderInternals.internalRenderStill({ composition, output: outputPath, @@ -229,14 +283,9 @@ const innerStillHandler = async ({ offthreadVideoCacheSizeInBytes: lambdaParams.offthreadVideoCacheSizeInBytes, binariesDirectory: null, onBrowserDownload, + onArtifact, }); - const {key, renderBucketName, customCredentials} = getExpectedOutName( - renderMetadata, - bucketName, - getCredentialsFromOutName(lambdaParams.outName), - ); - const {size} = await fs.promises.stat(outputPath); await lambdaWriteFile({ @@ -284,6 +333,7 @@ const innerStillHandler = async ({ estimatedPrice: formatCostsInfo(estimatedPrice), renderId, outKey, + receivedAssets, }; onStream({ @@ -301,6 +351,7 @@ export type RenderStillLambdaResponsePayload = { sizeInBytes: number; estimatedPrice: CostsInfo; renderId: string; + receivedAssets: ReceivedAsset[]; }; export const stillHandler = async ( diff --git a/packages/renderer/src/collect-assets.ts b/packages/renderer/src/collect-assets.ts new file mode 100644 index 00000000000..334d33df73e --- /dev/null +++ b/packages/renderer/src/collect-assets.ts @@ -0,0 +1,41 @@ +import type {TRenderAsset} from 'remotion/no-react'; +import type {Page} from './browser/BrowserPage'; +import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate'; + +export const collectAssets = async ({ + frame, + freePage, + timeoutInMilliseconds, +}: { + frame: number; + freePage: Page; + timeoutInMilliseconds: number; +}) => { + const {value} = await puppeteerEvaluateWithCatch<TRenderAsset[]>({ + pageFunction: () => { + return window.remotion_collectAssets(); + }, + args: [], + frame, + page: freePage, + timeoutInMilliseconds, + }); + + const fixedArtifacts = value.map((asset) => { + if (asset.type !== 'artifact') { + return asset; + } + + const stringOrUintArray = + typeof asset.content === 'string' + ? asset.content + : new Uint8Array(Object.values(asset.content)); + + return { + ...asset, + content: stringOrUintArray, + }; + }); + + return fixedArtifacts; +}; diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index 5bdc261ed56..b338772df0d 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -526,15 +526,7 @@ const innerRenderFrames = async ({ } } - const stringOrUintArray = - typeof artifact.content === 'string' - ? artifact.content - : new Uint8Array(Object.values(artifact.content)); - - onArtifact?.({ - ...artifact, - content: stringOrUintArray, - }); + onArtifact?.(artifact); } const compressedAssets = audioAndVideoAssets.map((asset) => { diff --git a/packages/renderer/src/render-still.ts b/packages/renderer/src/render-still.ts index 54b8453188d..14796a1ce34 100644 --- a/packages/renderer/src/render-still.ts +++ b/packages/renderer/src/render-still.ts @@ -17,6 +17,7 @@ import type {Compositor} from './compositor/compositor'; import {convertToPositiveFrameIndex} from './convert-to-positive-frame-index'; import {ensureOutputDirectory} from './ensure-output-directory'; import {handleJavascriptException} from './error-handling/handle-javascript-exception'; +import {onlyArtifact} from './filter-asset-types'; import {findRemotionRoot} from './find-closest-package-json'; import type {StillImageFormat} from './image-format'; import { @@ -34,6 +35,7 @@ import {DEFAULT_OVERWRITE} from './overwrite'; import type {RemotionServer} from './prepare-server'; import {makeOrReuseServer} from './prepare-server'; import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate'; +import type {OnArtifact} from './render-frames'; import {seekToFrame} from './seek-to-frame'; import {setPropsAndEnv} from './set-props-and-env'; import {takeFrameAndCompose} from './take-frame-and-compose'; @@ -68,6 +70,7 @@ type InternalRenderStillOptions = { serveUrl: string; port: number | null; offthreadVideoCacheSizeInBytes: number | null; + onArtifact: OnArtifact | null; } & ToOptions<typeof optionsMap.renderStill>; export type RenderStillOptions = { @@ -99,6 +102,7 @@ export type RenderStillOptions = { * @deprecated Renamed to `jpegQuality` */ quality?: never; + onArtifact?: OnArtifact; } & Partial<ToOptions<typeof optionsMap.renderStill>>; type CleanupFn = () => Promise<unknown>; @@ -130,6 +134,7 @@ const innerRenderStill = async ({ indent, serializedResolvedPropsWithCustomSchema, onBrowserDownload, + onArtifact, }: InternalRenderStillOptions & { downloadMap: DownloadMap; serveUrl: string; @@ -316,7 +321,7 @@ const innerRenderStill = async ({ attempt: 0, }); - const {buffer} = await takeFrameAndCompose({ + const {buffer, collectedAssets} = await takeFrameAndCompose({ frame: stillFrame, freePage: page, height: composition.height, @@ -331,6 +336,23 @@ const innerRenderStill = async ({ timeoutInMilliseconds, }); + const artifactAssets = onlyArtifact(collectedAssets); + const previousArtifactAssets = []; + + for (const artifact of artifactAssets) { + for (const previousArtifact of previousArtifactAssets) { + if (artifact.filename === previousArtifact.filename) { + throw new Error( + `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`, + ); + } + } + + previousArtifactAssets.push(artifact); + + onArtifact?.(artifact); + } + await cleanup(); return {buffer: output ? null : buffer}; @@ -443,6 +465,7 @@ export const renderStill = ( logLevel: passedLogLevel, binariesDirectory, onBrowserDownload, + onArtifact, } = options; if (typeof jpegQuality !== 'undefined' && imageFormat !== 'jpeg') { @@ -499,5 +522,6 @@ export const renderStill = ( onBrowserDownload: onBrowserDownload ?? defaultBrowserDownloadProgress({indent, logLevel, api: 'renderStill()'}), + onArtifact: onArtifact ?? null, }); }; diff --git a/packages/renderer/src/take-frame-and-compose.ts b/packages/renderer/src/take-frame-and-compose.ts index 6324d63dfdb..8f47880a9e2 100644 --- a/packages/renderer/src/take-frame-and-compose.ts +++ b/packages/renderer/src/take-frame-and-compose.ts @@ -3,6 +3,7 @@ import path from 'node:path'; import type {ClipRegion, TRenderAsset} from 'remotion/no-react'; import type {DownloadMap} from './assets/download-map'; import type {Page} from './browser/BrowserPage'; +import {collectAssets} from './collect-assets'; import {compose} from './compositor/compose'; import type {Compositor} from './compositor/compositor'; import type {StillImageFormat, VideoImageFormat} from './image-format'; @@ -37,7 +38,7 @@ export const takeFrameAndCompose = async ({ compositor: Compositor; timeoutInMilliseconds: number; }): Promise<{buffer: Buffer | null; collectedAssets: TRenderAsset[]}> => { - const [{value: clipRegion}, {value: collectedAssets}] = await Promise.all([ + const [{value: clipRegion}, collectedAssets] = await Promise.all([ puppeteerEvaluateWithCatch<ClipRegion | null>({ pageFunction: () => { if (typeof window.remotion_getClipRegion === 'undefined') { @@ -51,15 +52,7 @@ export const takeFrameAndCompose = async ({ page: freePage, timeoutInMilliseconds, }), - puppeteerEvaluateWithCatch<TRenderAsset[]>({ - pageFunction: () => { - return window.remotion_collectAssets(); - }, - args: [], - frame, - page: freePage, - timeoutInMilliseconds, - }), + collectAssets({frame, freePage, timeoutInMilliseconds}), ]); if (imageFormat === 'none') { From 4b7bc6505f066ef171e4c3d98af1dccbfbe37e62 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 19 Jun 2024 19:24:35 +0200 Subject: [PATCH 18/36] Update still.ts --- packages/cli/src/render-flows/still.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/render-flows/still.ts b/packages/cli/src/render-flows/still.ts index 83f86725327..8ffa426fa50 100644 --- a/packages/cli/src/render-flows/still.ts +++ b/packages/cli/src/render-flows/still.ts @@ -14,7 +14,6 @@ import type { AggregateRenderProgress, JobProgressCallback, } from '@remotion/studio-server'; -import type {ArtifactProgress} from '@remotion/studio-shared'; import {existsSync, mkdirSync} from 'node:fs'; import path from 'node:path'; import {NoReactInternals} from 'remotion/no-react'; @@ -298,8 +297,6 @@ export const renderStillFlow = async ({ const renderStart = Date.now(); - let artifactState: ArtifactProgress = {received: []}; - aggregate.rendering = { frames: 0, doneIn: null, @@ -321,8 +318,9 @@ export const renderStillFlow = async ({ isUsingParallelEncoding: false, }); - const {onArtifact} = handleOnArtifact(artifactState, (progress) => { - artifactState = progress; + const {onArtifact} = handleOnArtifact(aggregate.artifactState, (progress) => { + aggregate.artifactState = progress; + updateRenderProgress({ newline: false, printToConsole: true, From 8474cfd20a221e84d13a32dde744b6b16afe52ac Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 19 Jun 2024 20:05:19 +0200 Subject: [PATCH 19/36] lambda improvements --- packages/example/runlambda.sh | 2 +- .../lambda/src/api/render-still-on-lambda.ts | 3 + .../src/cli/commands/render/progress.ts | 10 ++-- .../lambda/src/cli/commands/render/render.ts | 60 +++++++++++++------ packages/lambda/src/cli/commands/still.ts | 53 ++++++++++++---- .../helpers/overall-render-progress.ts | 1 + packages/lambda/src/functions/launch.ts | 1 + packages/lambda/src/functions/still.ts | 1 + 8 files changed, 96 insertions(+), 35 deletions(-) diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh index 935c37f18bd..0c9bd57010b 100644 --- a/packages/example/runlambda.sh +++ b/packages/example/runlambda.sh @@ -7,4 +7,4 @@ cd example bunx remotion lambda functions rmall -f bunx remotion lambda functions deploy --memory=3000 --disk=10000 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry -bunx remotion lambda render testbed-v6 subtitle --log=verbose --delete-after="1-day" +bunx remotion lambda still testbed-v6 subtitle --log=verbose --delete-after="1-day" out-there.png diff --git a/packages/lambda/src/api/render-still-on-lambda.ts b/packages/lambda/src/api/render-still-on-lambda.ts index 6b9c6c297ce..dd4c2d8ce21 100644 --- a/packages/lambda/src/api/render-still-on-lambda.ts +++ b/packages/lambda/src/api/render-still-on-lambda.ts @@ -5,6 +5,7 @@ import type { } from '@remotion/renderer'; import type {BrowserSafeApis} from '@remotion/renderer/client'; import {NoReactAPIs} from '@remotion/renderer/pure'; +import type {ReceivedAsset} from '../functions/helpers/overall-render-progress'; import type {RenderStillLambdaResponsePayload} from '../functions/still'; import type {AwsRegion} from '../pricing/aws-regions'; import {callLambdaWithStreaming} from '../shared/call-lambda'; @@ -67,6 +68,7 @@ export type RenderStillOnLambdaOutput = { bucketName: string; renderId: string; cloudWatchLogs: string; + receivedAssets: ReceivedAsset[]; }; const internalRenderStillOnLambda = async ( @@ -130,6 +132,7 @@ const internalRenderStillOnLambda = async ( renderId: res.renderId, rendererFunctionName: null, }), + receivedAssets: res.receivedAssets, }; } catch (err) { if ((err as Error).stack?.includes('UnrecognizedClientException')) { diff --git a/packages/lambda/src/cli/commands/render/progress.ts b/packages/lambda/src/cli/commands/render/progress.ts index 316a6a14fe4..cfd637d60fb 100644 --- a/packages/lambda/src/cli/commands/render/progress.ts +++ b/packages/lambda/src/cli/commands/render/progress.ts @@ -2,6 +2,7 @@ import {CliInternals} from '@remotion/cli'; import {RenderInternals} from '@remotion/renderer'; import {NoReactInternals} from 'remotion/no-react'; import type {RenderProgress} from '../../../defaults'; +import type {ReceivedAsset} from '../../../functions/helpers/overall-render-progress'; import {truthy} from '../../../shared/truthy'; type LambdaInvokeProgress = { @@ -200,8 +201,7 @@ const makeTopRow = (overall: RenderProgress) => { return CliInternals.chalk.gray(str); }; -const makeArtifactProgress = (progress: RenderProgress) => { - const {artifactProgress} = progress; +export const makeArtifactProgress = (artifactProgress: ReceivedAsset[]) => { if (artifactProgress.length === 0) { return null; } @@ -209,12 +209,12 @@ const makeArtifactProgress = (progress: RenderProgress) => { return artifactProgress .map((artifact) => { return [ - CliInternals.chalk.blue('+'.padEnd(CliInternals.LABEL_WIDTH)), + CliInternals.chalk.blue('+ S3'.padEnd(CliInternals.LABEL_WIDTH)), CliInternals.chalk.blue( CliInternals.makeHyperlink({ url: artifact.s3Url, fallback: artifact.filename, - text: artifact.filename, + text: artifact.s3Key, }), ), CliInternals.chalk.gray( @@ -239,8 +239,8 @@ export const makeProgressString = ({ ...makeInvokeProgress(overall), ...makeRenderProgress(overall), makeCombinationProgress(overall), - makeArtifactProgress(overall), downloadInfo ? makeDownloadProgress(downloadInfo) : null, + makeArtifactProgress(overall.artifactProgress), ] .filter(NoReactInternals.truthy) .join('\n'); diff --git a/packages/lambda/src/cli/commands/render/render.ts b/packages/lambda/src/cli/commands/render/render.ts index 332cd2c4768..a193a4dfe2a 100644 --- a/packages/lambda/src/cli/commands/render/render.ts +++ b/packages/lambda/src/cli/commands/render/render.ts @@ -3,6 +3,7 @@ import {ConfigInternals} from '@remotion/cli/config'; import type {ChromiumOptions, LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {BrowserSafeApis} from '@remotion/renderer/client'; +import path from 'path'; import {NoReactInternals} from 'remotion/no-react'; import {downloadMedia} from '../../../api/download-media'; import {getRenderProgress} from '../../../api/get-render-progress'; @@ -428,16 +429,11 @@ export const renderCommand = async ( ); if (newStatus.done) { - progressBar.update( - makeProgressString({ - downloadInfo: null, - overall: newStatus, - }), - false, - ); + let downloadOrNothing; + if (downloadName) { const downloadStart = Date.now(); - const {outputPath, sizeInBytes} = await downloadMedia({ + const download = await downloadMedia({ bucketName: res.bucketName, outPath: downloadName, region: getAwsRegion(), @@ -457,29 +453,55 @@ export const renderCommand = async ( ); }, }); + downloadOrNothing = download; progressBar.update( makeProgressString({ downloadInfo: { doneIn: Date.now() - downloadStart, - downloaded: sizeInBytes, - totalSize: sizeInBytes, + downloaded: download.sizeInBytes, + totalSize: download.sizeInBytes, }, overall: newStatus, }), false, ); - Log.info({indent: false, logLevel}); - Log.info({indent: false, logLevel}); + } + + Log.info({indent: false, logLevel}); + Log.info( + {indent: false, logLevel}, + CliInternals.chalk.blue('+ S3 '.padEnd(CliInternals.LABEL_WIDTH)), + CliInternals.chalk.blue( + CliInternals.makeHyperlink({ + fallback: newStatus.outputFile as string, + text: newStatus.outKey as string, + url: newStatus.outputFile as string, + }), + ), + CliInternals.chalk.gray( + CliInternals.formatBytes(newStatus.outputSizeInBytes as number), + ), + ); + + if (downloadOrNothing) { + const relativeOutputPath = path.relative( + process.cwd(), + downloadOrNothing.outputPath, + ); Log.info( {indent: false, logLevel}, - 'Done!', - outputPath, - CliInternals.formatBytes(sizeInBytes), + CliInternals.chalk.blue('↓'.padEnd(CliInternals.LABEL_WIDTH)), + CliInternals.chalk.blue( + CliInternals.makeHyperlink({ + url: `file://${downloadOrNothing.outputPath}`, + text: relativeOutputPath, + fallback: downloadOrNothing.outputPath, + }), + ), + CliInternals.chalk.gray( + CliInternals.formatBytes(downloadOrNothing.sizeInBytes), + ), ); - } else { - Log.info({indent: false, logLevel}); - Log.info({indent: false, logLevel}); - Log.info({indent: false, logLevel}, 'Done! ' + newStatus.outputFile); } Log.info( diff --git a/packages/lambda/src/cli/commands/still.ts b/packages/lambda/src/cli/commands/still.ts index 328e99cad1e..4560da05c77 100644 --- a/packages/lambda/src/cli/commands/still.ts +++ b/packages/lambda/src/cli/commands/still.ts @@ -3,6 +3,7 @@ import {ConfigInternals} from '@remotion/cli/config'; import type {ChromiumOptions, LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {BrowserSafeApis} from '@remotion/renderer/client'; +import path from 'path'; import {NoReactInternals} from 'remotion/no-react'; import {downloadMedia} from '../../api/download-media'; import {renderStillOnLambda} from '../../api/render-still-on-lambda'; @@ -19,6 +20,7 @@ import {getAwsRegion} from '../get-aws-region'; import {findFunctionName} from '../helpers/find-function-name'; import {quit} from '../helpers/quit'; import {Log} from '../log'; +import {makeArtifactProgress} from './render/progress'; const { offthreadVideoCacheSizeInBytesOption, @@ -235,18 +237,46 @@ export const stillCommand = async ( ); Log.verbose( {indent: false, logLevel}, - `CloudWatch logs (if enabled): ${cloudWatchLogs}`, + `${CliInternals.makeHyperlink({ + text: 'CloudWatch Logs', + url: cloudWatchLogs, + fallback: `CloudWatch Logs: ${cloudWatchLogs}`, + })} (if enabled)`, ); Log.verbose( {indent: false, logLevel}, - `Lambda Insights (if enabled): ${lambdaInsightsUrl}`, + `${CliInternals.makeHyperlink({ + text: 'Lambda Insights', + url: lambdaInsightsUrl, + fallback: `Lambda Insights: ${lambdaInsightsUrl}`, + })} (if enabled)`, ); }, deleteAfter, }); + Log.info( + { + indent: false, + logLevel, + }, + makeArtifactProgress(res.receivedAssets), + ); + + Log.info( + {indent: false, logLevel}, + chalk.blue('+ S3'.padEnd(CliInternals.LABEL_WIDTH)), + chalk.blue( + CliInternals.makeHyperlink({ + fallback: res.url, + url: res.url, + text: res.outKey, + }), + ), + chalk.gray(formatBytes(res.sizeInBytes)), + ); + if (downloadName) { - Log.info({indent: false, logLevel}, 'Finished rendering. Downloading...'); const {outputPath, sizeInBytes} = await downloadMedia({ bucketName: res.bucketName, outPath: downloadName, @@ -254,15 +284,18 @@ export const stillCommand = async ( renderId: res.renderId, logLevel, }); + const relativePath = path.relative(process.cwd(), outputPath); Log.info( {indent: false, logLevel}, - 'Done!', - outputPath, - formatBytes(sizeInBytes), + chalk.blue('↓'.padEnd(CliInternals.LABEL_WIDTH)), + chalk.blue( + CliInternals.makeHyperlink({ + url: 'file://' + outputPath, + text: relativePath, + fallback: outputPath, + }), + ), + chalk.gray(formatBytes(sizeInBytes)), ); - } else { - Log.info({indent: false, logLevel}, `Finished still!`); - Log.info({indent: false, logLevel}); - Log.info({indent: false, logLevel}, res.url); } }; diff --git a/packages/lambda/src/functions/helpers/overall-render-progress.ts b/packages/lambda/src/functions/helpers/overall-render-progress.ts index 68a793d98c2..710ad3c7b44 100644 --- a/packages/lambda/src/functions/helpers/overall-render-progress.ts +++ b/packages/lambda/src/functions/helpers/overall-render-progress.ts @@ -33,6 +33,7 @@ export type ReceivedAsset = { filename: string; sizeInBytes: number; s3Url: string; + s3Key: string; }; export type OverallProgressHelper = { diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts index 2a4932f1d1a..9c15005c11c 100644 --- a/packages/lambda/src/functions/launch.ts +++ b/packages/lambda/src/functions/launch.ts @@ -371,6 +371,7 @@ const innerLaunchHandler = async ({ filename: artifact.filename, sizeInBytes: artifact.content.length, s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, + s3Key, }); const start = Date.now(); diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts index c741ed64e49..49219e0a702 100644 --- a/packages/lambda/src/functions/still.ts +++ b/packages/lambda/src/functions/still.ts @@ -214,6 +214,7 @@ const innerStillHandler = async ({ filename: artifact.filename, sizeInBytes: artifact.content.length, s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, + s3Key, }); const startTime = Date.now(); From 1b95674ec6b6572be708d9ae4621c3cd745182ca Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 19 Jun 2024 20:11:17 +0200 Subject: [PATCH 20/36] Update still.ts --- packages/lambda/src/cli/commands/still.ts | 28 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/lambda/src/cli/commands/still.ts b/packages/lambda/src/cli/commands/still.ts index 4560da05c77..9c17a9ba290 100644 --- a/packages/lambda/src/cli/commands/still.ts +++ b/packages/lambda/src/cli/commands/still.ts @@ -12,6 +12,7 @@ import { DEFAULT_MAX_RETRIES, DEFAULT_OUTPUT_PRIVACY, } from '../../shared/constants'; +import {getS3RenderUrl} from '../../shared/get-aws-urls'; import {validatePrivacy} from '../../shared/validate-privacy'; import {validateMaxRetries} from '../../shared/validate-retries'; import {validateServeUrl} from '../../shared/validate-serveurl'; @@ -199,7 +200,14 @@ export const stillCommand = async ( Log.info( {indent: false, logLevel}, CliInternals.chalk.gray( - `functionName = ${functionName}, imageFormat = ${imageFormat} (${imageFormatReason})`, + `Function: ${CliInternals.makeHyperlink({text: functionName, fallback: functionName, url: `https://${getAwsRegion()}.console.aws.amazon.com/lambda/home#/functions/${functionName}?tab=code`})}`, + ), + ); + + Log.info( + {indent: false, logLevel}, + CliInternals.chalk.gray( + `Image Format = ${imageFormat} (${imageFormatReason})`, ), ); @@ -230,11 +238,7 @@ export const stillCommand = async ( scale, forceHeight: height, forceWidth: width, - onInit: ({cloudWatchLogs, renderId, lambdaInsightsUrl}) => { - Log.info( - {indent: false, logLevel}, - chalk.gray(`Render invoked with ID = ${renderId}`), - ); + onInit: ({cloudWatchLogs, lambdaInsightsUrl}) => { Log.verbose( {indent: false, logLevel}, `${CliInternals.makeHyperlink({ @@ -254,6 +258,18 @@ export const stillCommand = async ( }, deleteAfter, }); + Log.info( + {indent: false, logLevel}, + CliInternals.chalk.gray( + `Render ID: ${CliInternals.makeHyperlink({text: res.renderId, fallback: res.renderId, url: getS3RenderUrl({bucketName: res.bucketName, renderId: res.renderId, region: getAwsRegion()})})}`, + ), + ); + Log.info( + {indent: false, logLevel}, + CliInternals.chalk.gray( + `Bucket: ${CliInternals.makeHyperlink({text: res.bucketName, fallback: res.bucketName, url: `https://${getAwsRegion()}.console.aws.amazon.com/s3/buckets/${res.bucketName}/?region=${getAwsRegion()}`})}`, + ), + ); Log.info( { From 85d1531e6e3a0b71524c90db4a2db3de31fd6624 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Wed, 19 Jun 2024 20:14:39 +0200 Subject: [PATCH 21/36] Update render.ts --- packages/lambda/src/cli/commands/render/render.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/lambda/src/cli/commands/render/render.ts b/packages/lambda/src/cli/commands/render/render.ts index a193a4dfe2a..77a4be8be9a 100644 --- a/packages/lambda/src/cli/commands/render/render.ts +++ b/packages/lambda/src/cli/commands/render/render.ts @@ -504,6 +504,7 @@ export const renderCommand = async ( ); } + Log.info({indent: false, logLevel}); Log.info( {indent: false, logLevel}, [ From 8d2cc13f99bc96946b1998a5cf9b4d5a4d6003ba Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 11:40:11 +0200 Subject: [PATCH 22/36] update documentation --- packages/docs/docs/artifact.mdx | 5 +- packages/docs/docs/artifacts.mdx | 193 + packages/docs/sidebars.js | 1 + packages/docs/src/data/articles.ts | 5087 +++++++++-------- .../generated/articles-docs-artifacts.png | Bin 0 -> 37466 bytes .../lambda/src/api/render-still-on-lambda.ts | 6 +- .../src/cli/commands/render/progress.ts | 6 +- packages/lambda/src/cli/commands/still.ts | 2 +- .../helpers/create-post-render-data.ts | 2 +- .../src/functions/helpers/get-progress.ts | 4 +- .../helpers/overall-render-progress.ts | 18 +- packages/lambda/src/functions/launch.ts | 4 +- packages/lambda/src/functions/still.ts | 12 +- packages/lambda/src/shared/constants.ts | 6 +- packages/studio-shared/src/index.ts | 2 +- packages/studio-shared/src/render-job.ts | 4 +- 16 files changed, 2777 insertions(+), 2575 deletions(-) create mode 100644 packages/docs/docs/artifacts.mdx create mode 100644 packages/docs/static/generated/articles-docs-artifacts.png diff --git a/packages/docs/docs/artifact.mdx b/packages/docs/docs/artifact.mdx index 146c33e5068..4193d3e0e4a 100644 --- a/packages/docs/docs/artifact.mdx +++ b/packages/docs/docs/artifact.mdx @@ -6,7 +6,7 @@ crumb: API # `<Artifact>`<AvailableFrom v="4.0.176"/> -By rendering an `<Artifact>` tag in your Remotion markup, an extra file will get emitted during rendering. +By rendering an `<Artifact>` tag in your Remotion markup, [an extra file will get emitted during rendering](/docs/emitting-artifacts). ```tsx twoslash title="MyComp.tsx" import { Artifact, useCurrentFrame } from "remotion"; @@ -55,8 +55,9 @@ Must match the regex `/^([0-9a-zA-Z-!_.*'()/:&$@=;+,?]+)/g`. ### `content` -A string or `UInt8Array` that is the content of the file that will be emitted. +A `string` or `Uint8Array` that is the content of the file that will be emitted. Don't consider an `Uint8Array` to be faster, because it needs to be serialized. ## See also +- [Emitting Artifacts](/docs/artifacts) - [Source code for this component](https://github.com/remotion-dev/remotion/blob/main/packages/core/src/components/Artifact.tsx) diff --git a/packages/docs/docs/artifacts.mdx b/packages/docs/docs/artifacts.mdx new file mode 100644 index 00000000000..619223cf28e --- /dev/null +++ b/packages/docs/docs/artifacts.mdx @@ -0,0 +1,193 @@ +--- +image: /generated/articles-docs-artifacts.png +title: Emitting Artifacts +id: artifacts +crumb: "Techniques" +--- + +# Emitting Artifacts<AvailableFrom v="4.0.176" /> + +Sometimes you wish to generate additional files when rendering your video. For example: + +- A `.srt` subtitle file +- A `.txt` containing chapters of the video +- A `CREDITS` file for the assets used in the video +- Debug information from the render. + +You can use the [`<Artifact>`](/docs/artifact) component to emit arbitrary files from your video. + +:::note +Emitting artifacts is not currently supported by `@remotion/cloudrun`. +::: + +## Example + +```tsx twoslash title="MyComp.tsx" +// @filename: subtitles.tsx + +export const generateSubtitles = () => { + return ``; +}; +// @filename: MyComp.tsx +// ---cut--- +import React from "react"; +import { Artifact, useCurrentFrame } from "remotion"; +import { generateSubtitles } from "./subtitles"; + +export const MyComp: React.FC = () => { + const frame = useCurrentFrame(); + return ( + <> + {frame === 0 ? ( + <Artifact filename="captions.srt" content={generateSubtitles()} /> + ) : null} + </> + ); +}; +``` + +## Rules of artifacts + +<Step>1</Step> The asset should only be rendered for one single frame of the video. +Otherwise, the asset will get emitted multiple times. <br /> + +<Step>2</Step> It is possible to emit multiple assets, but they may not have the +same filename. +<br /> + +<Step>3</Step> For the <code>content</code> prop it is possible to pass a <code> + string +</code> +, or a <code>Uint8Array</code> for binary data. Passing an <code> + Uint8Array +</code> should not be considered faster due to it having to be serialized. +<br /> + +## Receiving artifacts + +### In the CLI or Studio + +Artifacts get saved to `out/[composition-id]/[filename]` when rendering a video. + +### Using `renderMedia()`, `renderStill()` or `renderFrames()` + +Use the [`onArtifact`](/docs/renderer/render-media#onartifact) callback to receive the artifacts. + +```tsx twoslash title="render.mjs" +// @module: es2022 +// @target: es2017 +import type { VideoConfig } from "remotion"; +import fs from "fs"; + +const composition: VideoConfig = { + width: 100, + height: 100, + fps: 30, + defaultProps: {}, + props: {}, + defaultCodec: null, + id: "hi", + durationInFrames: 100, +}; +const serveUrl = "http://localhost:8080"; +const inputProps = {}; +// ---cut--- +import { renderMedia, OnArtifact } from "@remotion/renderer"; + +const onArtifact: OnArtifact = (artifact) => { + console.log(artifact.filename); // string + console.log(artifact.content); // string | Uint8Array + console.log(artifact.frame); // number, frame in the composition which emitted this + + // Example action: Write the artifact to disk + fs.writeFileSync(artifact.filename, artifact.content); +}; + +await renderMedia({ + composition, + serveUrl, + onArtifact, + codec: "h264", + inputProps, +}); +``` + +### Using the Remotion Lambda CLI + +When using [`npx remotion lambda render`](/docs/lambda/cli/render) or [`npx remotion lambda still`](/docs/lambda/cli/still), artifacts get saved to the S3 bucket under the key `renders/[render-id]/artifacts/[filename]`. + +They will get logged to the console and you can click them to download them. +The `--privacy` option also applies to artifacts. + +### Using `renderMediaOnLambda()` + +When using [`renderMediaOnLambda()`](/docs/lambda/rendermediaonlambda), artifacts get saved to the S3 bucket under the key `renders/[render-id]/artifacts/[filename]`. + +You can obtain a list of currently received assets from [`getRenderProgress()`](/docs/lambda/getrenderprogress#artifacts). + +```tsx twoslash title="progress.ts" +// @module: es2022 +// @target: es2017 +import { getRenderProgress } from "@remotion/lambda/client"; + +const renderProgress = await getRenderProgress({ + renderId: "hi", + functionName: "hi", + bucketName: "hi", + region: "eu-central-1", +}); + +for (const artifact of renderProgress.artifacts) { + console.log(artifact.filename); // "hello-world.txt" + console.log(artifact.sizeInBytes); // 12 + console.log(artifact.s3Url); // "https://s3.eu-central-1.amazonaws.com/remotion-lambda-abcdef/renders/abcdef/artifacts/hello-world.txt" + console.log(artifact.s3Key); // "renders/abcdef/artifacts/hello-world.txt" +} +``` + +### Using `renderStillOnLambda()` + +When using [`renderStillOnLambda()`](/docs/lambda/renderstillonlambda), artifacts get saved to the S3 bucket under the key `renders/[render-id]/artifacts/[filename]`. + +You can obtain a list of received assets from [`artifacts`](/docs/lambda/renderstillonlambda#artifacts) field of `renderStillOnLambda()`. + +```tsx twoslash title="still.ts" +const serveUrl = "http://localhost:8080"; +const inputProps = {}; +const functionName = "hi"; +const composition = "hi"; +const privacy = "public" as const; +const imageFormat = "png" as const; +const region = "eu-central-1" as const; + +// ---cut--- +// @module: es2022 +// @target: es2017 +import { renderStillOnLambda } from "@remotion/lambda/client"; + +const stillResponse = await renderStillOnLambda({ + functionName, + region, + serveUrl, + composition, + inputProps, + imageFormat, + privacy, +}); + +for (const artifact of stillResponse.artifacts) { + console.log(artifact.filename); // "hello-world.txt" + console.log(artifact.sizeInBytes); // 12 + console.log(artifact.s3Url); // "https://s3.eu-central-1.amazonaws.com/remotion-lambda-abcdef/renders/abcdef/artifacts/hello-world.txt" + console.log(artifact.s3Key); // "renders/abcdef/artifacts/hello-world.txt" +} +``` + +### Using Cloud Run + +In the Cloud Run Alpha, emitting artifacts is not supported and will throw an error. +We plan on revising Cloud Run to use the same runtime as Lambda in the future and will bring this feature along. + +## See also + +- [`<Artifact>`](/docs/artifact) diff --git a/packages/docs/sidebars.js b/packages/docs/sidebars.js index 3f56d1f2350..a7e5b2514be 100644 --- a/packages/docs/sidebars.js +++ b/packages/docs/sidebars.js @@ -630,6 +630,7 @@ module.exports = { "render-all", "miscellaneous/video-formats", "video-vs-offthreadvideo", + "artifacts", ], }, { diff --git a/packages/docs/src/data/articles.ts b/packages/docs/src/data/articles.ts index 42e185e56a3..bbfc3875903 100644 --- a/packages/docs/src/data/articles.ts +++ b/packages/docs/src/data/articles.ts @@ -1,80 +1,73 @@ export const articles = [ { - id: "figma", - title: "Import from Figma", - relativePath: "docs/figma.mdx", - compId: "articles-docs-figma", - crumb: "The best of both", + id: "2-0-migration", + title: "v2.0 Migration", + relativePath: "docs/2-0-migration.mdx", + compId: "articles-docs-2-0-migration", + crumb: "Version Upgrade", }, { - id: "env-variables", - title: "Environment variables", - relativePath: "docs/env-variables.mdx", - compId: "articles-docs-env-variables", - crumb: "How To", + id: "3-0-migration", + title: "v3.0 Migration", + relativePath: "docs/3-0-migration.mdx", + compId: "articles-docs-3-0-migration", + crumb: "Version Upgrade", }, { - id: "skia", - title: "@remotion/skia", - relativePath: "docs/skia/skia.mdx", - compId: "articles-docs-skia-skia", - crumb: null, + id: "4-0-alpha", + title: "v4.0 Alpha", + relativePath: "docs/4-0-alpha.mdx", + compId: "articles-docs-4-0-alpha", + crumb: "Version Upgrade", }, { - id: "skia/enable-skia", - title: "enableSkia()", - relativePath: "docs/skia/enable-skia.mdx", - compId: "articles-docs-skia-enable-skia", - crumb: "@remotion/skia", + id: "4-0-migration", + title: "v4.0 Migration", + relativePath: "docs/4-0-migration.mdx", + compId: "articles-docs-4-0-migration", + crumb: "Version Upgrade", }, { - id: "skia/skia-canvas", - title: "<SkiaCanvas />", - relativePath: "docs/skia/skia-canvas.mdx", - compId: "articles-docs-skia-skia-canvas", - crumb: "@remotion/skia", + id: "5-0-migration", + title: "v5.0 Migration", + relativePath: "docs/5-0-migration.mdx", + compId: "articles-docs-5-0-migration", + crumb: "Version Upgrade", }, { - id: "spring", - title: "spring()", - relativePath: "docs/spring.mdx", - compId: "articles-docs-spring", + id: "absolute-fill", + title: "<AbsoluteFill>", + relativePath: "docs/absolute-fill.mdx", + compId: "articles-docs-absolute-fill", crumb: "API", }, { - id: "audio-visualization", - title: "Audio visualization", - relativePath: "docs/audio-visualization.mdx", - compId: "articles-docs-audio-visualization", - crumb: "Techniques", - }, - { - id: "react-native", - title: "React Native", - relativePath: "docs/react-native.mdx", - compId: "articles-docs-react-native", - crumb: null, + id: "acknowledgements", + title: "Acknowledgements", + relativePath: "docs/acknowledgements.mdx", + compId: "articles-docs-acknowledgements", + crumb: "Credits", }, { - id: "use-current-scale", - title: "useCurrentScale()", - relativePath: "docs/use-current-scale.mdx", - compId: "articles-docs-use-current-scale", - crumb: "API", + id: "after-effects", + title: "Import from After Effects", + relativePath: "docs/after-effects.mdx", + compId: "articles-docs-after-effects", + crumb: "Integrations", }, { - id: "config", - title: "Configuration file", - relativePath: "docs/config.mdx", - compId: "articles-docs-config", - crumb: "remotion.config.ts", + id: "animating-properties", + title: "Animating properties", + relativePath: "docs/animating-properties.mdx", + compId: "articles-docs-animating-properties", + crumb: "The basics", }, { - id: "make-transform", - title: "makeTransform()", - relativePath: "docs/animation-utils/make-transform.mdx", - compId: "articles-docs-animation-utils-make-transform", - crumb: "@remotion/animation-utils", + id: "animation-math", + title: "Animation math", + relativePath: "docs/animation-math.mdx", + compId: "articles-docs-animation-math", + crumb: "Techniques", }, { id: "animation-utils/index", @@ -91,25 +84,60 @@ export const articles = [ crumb: "@remotion/animation-utils", }, { - id: "4-0-alpha", - title: "v4.0 Alpha", - relativePath: "docs/4-0-alpha.mdx", - compId: "articles-docs-4-0-alpha", - crumb: "Version Upgrade", + id: "make-transform", + title: "makeTransform()", + relativePath: "docs/animation-utils/make-transform.mdx", + compId: "articles-docs-animation-utils-make-transform", + crumb: "@remotion/animation-utils", }, { - id: "use-video-config", - title: "useVideoConfig()", - relativePath: "docs/use-video-config.mdx", - compId: "articles-docs-use-video-config", + id: "api", + title: "API overview", + relativePath: "docs/api.mdx", + compId: "articles-docs-api", + crumb: null, + }, + { + id: "artifact", + title: "<Artifact>", + relativePath: "docs/artifact.mdx", + compId: "articles-docs-artifact", crumb: "API", }, { - id: "legacy-babel", - title: "Using legacy Babel transpilation", - relativePath: "docs/legacy-babel-loader.mdx", - compId: "articles-docs-legacy-babel-loader", - crumb: "How To", + id: "artifacts", + title: "Emitting Artifacts", + relativePath: "docs/artifacts.mdx", + compId: "articles-docs-artifacts", + crumb: "Techniques", + }, + { + id: "get-help", + title: "Get help", + relativePath: "docs/ask-for-help.mdx", + compId: "articles-docs-ask-for-help", + crumb: null, + }, + { + id: "audio-visualization", + title: "Audio visualization", + relativePath: "docs/audio-visualization.mdx", + compId: "articles-docs-audio-visualization", + crumb: "Techniques", + }, + { + id: "audio", + title: "<Audio>", + relativePath: "docs/audio.mdx", + compId: "articles-docs-audio", + crumb: "How to", + }, + { + id: "audio-buffer-to-data-url", + title: "audioBufferToDataUrl()", + relativePath: "docs/audiobuffertodataurl.mdx", + compId: "articles-docs-audiobuffertodataurl", + crumb: "@remotion/media-utils", }, { id: "brownfield", @@ -119,487 +147,508 @@ export const articles = [ crumb: "Brownfield integration", }, { - id: "studio/studio", - title: "Starting the Studio", - relativePath: "docs/studio/studio.mdx", - compId: "articles-docs-studio-studio", - crumb: "Timeline basics", + id: "buffer-state", + title: "Display a buffer state", + relativePath: "docs/buffer-state.mdx", + compId: "articles-docs-buffer-state", + crumb: "Building video apps", }, { - id: "studio/quick-switcher", - title: "Quick Switcher", - relativePath: "docs/studio/quick-switcher.mdx", - compId: "articles-docs-studio-quick-switcher", - crumb: "Remotion Studio", + id: "bun", + title: "Bun support", + relativePath: "docs/bun.mdx", + compId: "articles-docs-bun", + crumb: "bun bun bun bun bun", }, { - id: "studio/restart-studio", - title: "restartStudio()", - relativePath: "docs/studio/restart-studio.mdx", - compId: "articles-docs-studio-restart-studio", - crumb: "@remotion/studio", + id: "bundle", + title: "bundle()", + relativePath: "docs/bundle.mdx", + compId: "articles-docs-bundle", + crumb: "@remotion/bundler", }, { - id: "studio/focus-default-props-path", - title: "focusDefaultPropsPath()", - relativePath: "docs/studio/focus-default-props-path.mdx", - compId: "articles-docs-studio-focus-default-props-path", - crumb: "@remotion/studio", + id: "bundler", + title: "@remotion/bundler", + relativePath: "docs/bundler.mdx", + compId: "articles-docs-bundler", + crumb: null, }, { - id: "studio/save-default-props", - title: "saveDefaultProps()", - relativePath: "docs/studio/save-default-props.mdx", - compId: "articles-docs-studio-save-default-props", - crumb: "@remotion/studio", + id: "calculate-metadata", + title: "calculateMetadata()", + relativePath: "docs/calculate-metadata.mdx", + compId: "articles-docs-calculate-metadata", + crumb: "API", }, { - id: "studio/deploy-static", - title: "Deploy Remotion Studio as static site", - relativePath: "docs/studio/deploy-static.mdx", - compId: "articles-docs-studio-deploy-static", - crumb: "Remotion Studio", + id: "cancel-render", + title: "cancelRender()", + relativePath: "docs/cancel-render.mdx", + compId: "articles-docs-cancel-render", + crumb: "How to", }, { - id: "studio/watch-public-folder", - title: "watchPublicFolder()", - relativePath: "docs/studio/watch-public-folder.mdx", - compId: "articles-docs-studio-watch-public-folder", - crumb: "@remotion/studio", + id: "chromium-flags", + title: "Chromium flags", + relativePath: "docs/chromium-flags.mdx", + compId: "articles-docs-chromium-flags", + crumb: "Tweaks", }, { - id: "studio/deploy-server", - title: "Deploy the Remotion Studio on a VPS", - relativePath: "docs/studio/deploy-server.mdx", - compId: "articles-docs-studio-deploy-server", - crumb: "Remotion Studio", + id: "cli/benchmark", + title: "npx remotion benchmark", + relativePath: "docs/cli/benchmark.mdx", + compId: "articles-docs-cli-benchmark", + crumb: "CLI Reference", }, { - id: "studio/get-static-files", - title: "getStaticFiles()", - relativePath: "docs/studio/get-static-files.mdx", - compId: "articles-docs-studio-get-static-files", - crumb: "@remotion/studio", + id: "cli/browser/ensure", + title: "npx remotion browser ensure", + relativePath: "docs/cli/browser/ensure.mdx", + compId: "articles-docs-cli-browser-ensure", + crumb: "@remotion/cli", }, { - id: "studio/watch-static-file", - title: "watchStaticFile()", - relativePath: "docs/studio/watch-static-file.mdx", - compId: "articles-docs-studio-watch-static-file", - crumb: "@remotion/studio", + id: "cli/browser/index", + title: "npx remotion browser", + relativePath: "docs/cli/browser/index.mdx", + compId: "articles-docs-cli-browser-index", + crumb: "@remotion/cli", }, { - id: "studio/shortcuts", - title: "Keyboard navigation", - relativePath: "docs/studio/shortcuts.mdx", - compId: "articles-docs-studio-shortcuts", - crumb: "Remotion Studio", + id: "cli/bundle", + title: "npx remotion bundle", + relativePath: "docs/cli/bundle.mdx", + compId: "articles-docs-cli-bundle", + crumb: "CLI Reference", }, { - id: "studio/api", - title: "@remotion/studio", - relativePath: "docs/studio/api.mdx", - compId: "articles-docs-studio-api", + id: "cli", + title: "Command line reference", + relativePath: "docs/cli/cli.mdx", + compId: "articles-docs-cli-cli", crumb: null, }, { - id: "studio/reevaluate-composition", - title: "reevaluateComposition()", - relativePath: "docs/studio/reevaluate-composition.mdx", - compId: "articles-docs-studio-reevaluate-composition", - crumb: "@remotion/studio", + id: "cli/compositions", + title: "npx remotion compositions", + relativePath: "docs/cli/compositions.mdx", + compId: "articles-docs-cli-compositions", + crumb: "CLI Reference", }, { - id: "studio/write-static-file", - title: "writeStaticFile()", - relativePath: "docs/studio/write-static-file.mdx", - compId: "articles-docs-studio-write-static-file", - crumb: "@remotion/studio", + id: "ffmpeg", + title: "npx remotion ffmpeg", + relativePath: "docs/cli/ffmpeg.mdx", + compId: "articles-docs-cli-ffmpeg", + crumb: "@remotion/cli", }, { - id: "studio/update-default-props", - title: "updateDefaultProps()", - relativePath: "docs/studio/update-default-props.mdx", - compId: "articles-docs-studio-update-default-props", - crumb: "@remotion/studio", + id: "ffprobe", + title: "npx remotion ffprobe", + relativePath: "docs/cli/ffprobe.mdx", + compId: "articles-docs-cli-ffprobe", + crumb: "@remotion/cli", }, { - id: "studio/delete-static-file", - title: "deleteStaticFile()", - relativePath: "docs/studio/delete-static-file.mdx", - compId: "articles-docs-studio-delete-static-file", - crumb: "@remotion/studio", + id: "cli/gpu", + title: "npx remotion gpu", + relativePath: "docs/cli/gpu.mdx", + compId: "articles-docs-cli-gpu", + crumb: "CLI Reference", }, { - id: "video-uploads", - title: "Handling user video uploads", - relativePath: "docs/video-uploads.mdx", - compId: "articles-docs-video-uploads", - crumb: "Building video apps", + id: "cli/help", + title: "npx remotion help", + relativePath: "docs/cli/help.mdx", + compId: "articles-docs-cli-help", + crumb: "CLI Reference", }, { - id: "bundler", - title: "@remotion/bundler", - relativePath: "docs/bundler.mdx", - compId: "articles-docs-bundler", - crumb: null, + id: "cli/install", + title: "npx remotion install", + relativePath: "docs/cli/install.mdx", + compId: "articles-docs-cli-install", + crumb: "CLI Reference", }, { - id: "using-audio", - title: "Using audio", - relativePath: "docs/using-audio.mdx", - compId: "articles-docs-using-audio", - crumb: "Techniques", + id: "cli/render", + title: "npx remotion render", + relativePath: "docs/cli/render.mdx", + compId: "articles-docs-cli-render", + crumb: "CLI Reference", }, { - id: "get-compositions", - title: "getCompositions()", - relativePath: "docs/renderer/get-compositions.mdx", - compId: "articles-docs-renderer-get-compositions", - crumb: "@remotion/renderer", + id: "cli/still", + title: "npx remotion still", + relativePath: "docs/cli/still.mdx", + compId: "articles-docs-cli-still", + crumb: "CLI Reference", }, { - id: "make-cancel-signal", - title: "makeCancelSignal()", - relativePath: "docs/renderer/make-cancel-signal.mdx", - compId: "articles-docs-renderer-make-cancel-signal", - crumb: "@remotion/renderer", + id: "cli/studio", + title: "npx remotion studio", + relativePath: "docs/cli/studio.mdx", + compId: "articles-docs-cli-studio", + crumb: "CLI Reference", }, { - id: "extract-audio", - title: "extractAudio()", - relativePath: "docs/renderer/extract-audio.mdx", - compId: "articles-docs-renderer-extract-audio", - crumb: "@remotion/renderer", + id: "cli/upgrade", + title: "npx remotion upgrade", + relativePath: "docs/cli/upgrade.mdx", + compId: "articles-docs-cli-upgrade", + crumb: "CLI Reference", }, { - id: "get-video-metadata", - title: "getVideoMetadata()", - relativePath: "docs/renderer/get-video-metadata.mdx", - compId: "articles-docs-renderer-get-video-metadata", - crumb: "@remotion/renderer", + id: "cli/versions", + title: "npx remotion versions", + relativePath: "docs/cli/versions.mdx", + compId: "articles-docs-cli-versions", + crumb: "CLI Reference", }, { - id: "select-composition", - title: "selectComposition()", - relativePath: "docs/renderer/select-composition.mdx", - compId: "articles-docs-renderer-select-composition", - crumb: "@remotion/renderer", + id: "clipper", + title: "<Experimental.Clipper>", + relativePath: "docs/clipper.mdx", + compId: "articles-docs-clipper", + crumb: "Experimental API", }, { - id: "ensure-ffmpeg", - title: "ensureFfmpeg()", - relativePath: "docs/renderer/ensure-ffmpeg.mdx", - compId: "articles-docs-renderer-ensure-ffmpeg", - crumb: "@remotion/renderer", + id: "cloudrun/api", + title: "@remotion/cloudrun", + relativePath: "docs/cloudrun/api.mdx", + compId: "articles-docs-cloudrun-api", + crumb: "Render videos without servers on GCP", }, { - id: "get-can-extract-frames-fast", - title: "getCanExtractFramesFast()", - relativePath: "docs/renderer/get-can-extract-frames-fast.mdx", - compId: "articles-docs-renderer-get-can-extract-frames-fast", - crumb: "@remotion/renderer", + id: "checklist", + title: "Production Checklist", + relativePath: "docs/cloudrun/checklist.mdx", + compId: "articles-docs-cloudrun-checklist", + crumb: "Cloud Run", }, { - id: "get-silent-parts", - title: "getSilentParts()", - relativePath: "docs/renderer/get-silent-parts.mdx", - compId: "articles-docs-renderer-get-silent-parts", - crumb: "@remotion/renderer", + id: "permissions", + title: "npx remotion cloudrun permissions", + relativePath: "docs/cloudrun/cli/permissions.mdx", + compId: "articles-docs-cloudrun-cli-permissions", + crumb: "Cloud Run CLI Reference", }, { - id: "open-browser", - title: "openBrowser()", - relativePath: "docs/renderer/open-browser.mdx", - compId: "articles-docs-renderer-open-browser", - crumb: "@remotion/renderer", + id: "regions", + title: "npx remotion cloudrun regions", + relativePath: "docs/cloudrun/cli/regions.mdx", + compId: "articles-docs-cloudrun-cli-regions", + crumb: "Cloud Run CLI Reference", }, { - id: "stitch-frames-to-video", - title: "stitchFramesToVideo()", - relativePath: "docs/renderer/stitch-frames-to-video.mdx", - compId: "articles-docs-renderer-stitch-frames-to-video", - crumb: "@remotion/renderer", + id: "render", + title: "npx remotion cloudrun render", + relativePath: "docs/cloudrun/cli/render.mdx", + compId: "articles-docs-cloudrun-cli-render", + crumb: "Cloud Run CLI Reference", }, { - id: "ensure-ffprobe", - title: "ensureFfprobe()", - relativePath: "docs/renderer/ensure-ffprobe.mdx", - compId: "articles-docs-renderer-ensure-ffprobe", - crumb: "@remotion/renderer", + id: "services", + title: "npx remotion cloudrun services", + relativePath: "docs/cloudrun/cli/services.mdx", + compId: "articles-docs-cloudrun-cli-services", + crumb: "Cloud Run CLI Reference", }, { - id: "render-media", - title: "renderMedia()", - relativePath: "docs/renderer/render-media.mdx", - compId: "articles-docs-renderer-render-media", - crumb: "@remotion/renderer", + id: "sites", + title: "npx remotion cloudrun sites", + relativePath: "docs/cloudrun/cli/sites.mdx", + compId: "articles-docs-cloudrun-cli-sites", + crumb: "Cloud Run CLI Reference", }, { - id: "ensure-browser", - title: "ensureBrowser()", - relativePath: "docs/renderer/ensure-browser.mdx", - compId: "articles-docs-renderer-ensure-browser", - crumb: "@remotion/renderer", + id: "still", + title: "npx remotion cloudrun still", + relativePath: "docs/cloudrun/cli/still.mdx", + compId: "articles-docs-cloudrun-cli-still", + crumb: "Cloud Run CLI Reference", }, { - id: "render-still", - title: "renderStill()", - relativePath: "docs/renderer/render-still.mdx", - compId: "articles-docs-renderer-render-still", - crumb: "@remotion/renderer", + id: "cli", + title: "@remotion/cloudrun - CLI", + relativePath: "docs/cloudrun/cli.mdx", + compId: "articles-docs-cloudrun-cli", + crumb: null, }, { - id: "render-frames", - title: "renderFrames()", - relativePath: "docs/renderer/render-frames.mdx", - compId: "articles-docs-renderer-render-frames", - crumb: "@remotion/renderer", + id: "deleteservice", + title: "deleteService()", + relativePath: "docs/cloudrun/deleteservice.mdx", + compId: "articles-docs-cloudrun-deleteservice", + crumb: "Cloud Run API", }, { - id: "bundle", - title: "bundle()", - relativePath: "docs/bundle.mdx", - compId: "articles-docs-bundle", - crumb: "@remotion/bundler", + id: "deletesite", + title: "deleteSite()", + relativePath: "docs/cloudrun/deletesite.mdx", + compId: "articles-docs-cloudrun-deletesite", + crumb: "Cloud Run API", }, { - id: "img", - title: "<Img>", - relativePath: "docs/img.mdx", - compId: "articles-docs-img", - crumb: "API", + id: "deployservice", + title: "deployService()", + relativePath: "docs/cloudrun/deployservice.mdx", + compId: "articles-docs-cloudrun-deployservice", + crumb: "Cloud Run API", }, { - id: "encoding", - title: "Encoding Guide", - relativePath: "docs/encoding.mdx", - compId: "articles-docs-encoding", - crumb: "Codecs and more", + id: "deploysite", + title: "deploySite()", + relativePath: "docs/cloudrun/deploysite.mdx", + compId: "articles-docs-cloudrun-deploysite", + crumb: "Cloud Run API", }, { - id: "get-remotion-environment", - title: "getRemotionEnvironment()", - relativePath: "docs/get-remotion-environment.mdx", - compId: "articles-docs-get-remotion-environment", - crumb: "API", + id: "generate-env", + title: "Generate .env File", + relativePath: "docs/cloudrun/generate-env.mdx", + compId: "articles-docs-cloudrun-generate-env", + crumb: "Cloud Run", }, { - id: "sequence", - title: "<Sequence>", - relativePath: "docs/sequence.mdx", - compId: "articles-docs-sequence", - crumb: "API", + id: "getserviceinfo", + title: "getServiceInfo()", + relativePath: "docs/cloudrun/getServiceinfo.mdx", + compId: "articles-docs-cloudrun-getServiceinfo", + crumb: "Cloud Run API", }, { - id: "the-fundamentals", - title: "The fundamentals", - relativePath: "docs/the-fundamentals.mdx", - compId: "articles-docs-the-fundamentals", - crumb: "Getting started", + id: "getorcreatebucket", + title: "getOrCreateBucket()", + relativePath: "docs/cloudrun/getorcreatebucket.mdx", + compId: "articles-docs-cloudrun-getorcreatebucket", + crumb: "Cloud Run API", }, { - id: "ssr", - title: "Server-Side Rendering", - relativePath: "docs/ssr.mdx", - compId: "articles-docs-ssr", - crumb: "The power of", + id: "getregions", + title: "getRegions()", + relativePath: "docs/cloudrun/getregions.mdx", + compId: "articles-docs-cloudrun-getregions", + crumb: "Cloud Run API", }, { - id: "after-effects", - title: "Import from After Effects", - relativePath: "docs/after-effects.mdx", - compId: "articles-docs-after-effects", - crumb: "Integrations", + id: "getservices", + title: "getServices()", + relativePath: "docs/cloudrun/getservices.mdx", + compId: "articles-docs-cloudrun-getservices", + crumb: "Cloud Run API", }, { - id: "assets", - title: "Importing assets", - relativePath: "docs/importing-assets.mdx", - compId: "articles-docs-importing-assets", - crumb: "How To", + id: "getsites", + title: "getSites()", + relativePath: "docs/cloudrun/getsites.mdx", + compId: "articles-docs-cloudrun-getsites", + crumb: "Cloud Run API", }, { - id: "get-video-metadata", - title: "getVideoMetadata()", - relativePath: "docs/get-video-metadata.mdx", - compId: "articles-docs-get-video-metadata", - crumb: "@remotion/media-utils", + id: "instancecount", + title: "Instance Count", + relativePath: "docs/cloudrun/instancecount.mdx", + compId: "articles-docs-cloudrun-instancecount", + crumb: "Cloud Run", }, { - id: "media-playback-error", - title: "Could not play video/audio with src", - relativePath: "docs/media-playback-error.mdx", - compId: "articles-docs-media-playback-error", - crumb: "Troubleshooting", + id: "cloudrun/light-client", + title: "Light client", + relativePath: "docs/cloudrun/light-client.mdx", + compId: "articles-docs-cloudrun-light-client", + crumb: "Cloud Run", }, { - id: "staticfile-relative-paths", - title: "staticFile() does not support relative paths", - relativePath: "docs/static-file-relative-paths.mdx", - compId: "articles-docs-static-file-relative-paths", - crumb: "Troubleshooting", + id: "cloudrun/limits", + title: "Cloud Run Limits", + relativePath: "docs/cloudrun/limits.mdx", + compId: "articles-docs-cloudrun-limits", + crumb: "Cloud Run", }, { - id: "interpolate", - title: "interpolate()", - relativePath: "docs/interpolate.mdx", - compId: "articles-docs-interpolate", - crumb: "API", + id: "cloudrun/multiple-buckets", + title: "Multiple buckets in Remotion Cloud Run", + relativePath: "docs/cloudrun/multiple-buckets.mdx", + compId: "articles-docs-cloudrun-multiple-buckets", + crumb: "@remotion/cloudrun", }, { - id: "render-as-gif", - title: "Rendering GIFs", - relativePath: "docs/render-as-gif.mdx", - compId: "articles-docs-render-as-gif", - crumb: "Techniques", + id: "cloudrun/permissions", + title: "Permissions", + relativePath: "docs/cloudrun/permissions.mdx", + compId: "articles-docs-cloudrun-permissions", + crumb: "Cloud Run", }, { - id: "webpack-dynamic-imports", - title: "Webpack dynamic imports", - relativePath: "docs/dynamic-import.mdx", - compId: "articles-docs-dynamic-import", - crumb: "Knowledge Base", + id: "region-selection", + title: "Region selection", + relativePath: "docs/cloudrun/region-selection.mdx", + compId: "articles-docs-cloudrun-region-selection", + crumb: "Cloud Run", }, { - id: "terminology/studio", - title: "Remotion Studio", - relativePath: "docs/terminology/studio.mdx", - compId: "articles-docs-terminology-studio", - crumb: "Terminology", + id: "rendermediaoncloudrun", + title: "renderMediaOnCloudrun()", + relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx", + compId: "articles-docs-cloudrun-rendermediaoncloudrun", + crumb: "Cloud Run API", }, { - id: "terminology/bundle", - title: "Remotion Bundle", - relativePath: "docs/terminology/bundle.mdx", - compId: "articles-docs-terminology-bundle", - crumb: "Terminology", + id: "renderstilloncloudrun", + title: "renderStillOnCloudrun()", + relativePath: "docs/cloudrun/renderstilloncloudrun.mdx", + compId: "articles-docs-cloudrun-renderstilloncloudrun", + crumb: "Cloud Run API", }, { - id: "terminology/sequence", - title: "Sequence", - relativePath: "docs/terminology/sequence.mdx", - compId: "articles-docs-terminology-sequence", - crumb: "Terminology", + id: "setup", + title: "Setup", + relativePath: "docs/cloudrun/setup.mdx", + compId: "articles-docs-cloudrun-setup", + crumb: "Cloud Run", }, { - id: "terminology/concurrency", - title: "Concurrency", - relativePath: "docs/terminology/concurrency.mdx", - compId: "articles-docs-terminology-concurrency", - crumb: "Terminology", + id: "speculateservicename", + title: "speculateServiceName()", + relativePath: "docs/cloudrun/speculateservicename.mdx", + compId: "articles-docs-cloudrun-speculateservicename", + crumb: "Cloud Run API", }, { - id: "terminology/entry-point", - title: "Entry point", - relativePath: "docs/terminology/entry-point.mdx", - compId: "articles-docs-terminology-entry-point", - crumb: "Terminology", + id: "testpermissions", + title: "testPermissions()", + relativePath: "docs/cloudrun/testpermissions.mdx", + compId: "articles-docs-cloudrun-testpermissions", + crumb: "Cloud Run API", }, { - id: "terminology/player", - title: "Remotion Player", - relativePath: "docs/terminology/player.mdx", - compId: "articles-docs-terminology-player", - crumb: "Terminology", + id: "uninstall", + title: "Uninstall Cloud Run", + relativePath: "docs/cloudrun/uninstall.mdx", + compId: "articles-docs-cloudrun-uninstall", + crumb: null, }, { - id: "terminology/cloud-run-url", - title: "Cloud Run URL", - relativePath: "docs/terminology/cloud-run-url.mdx", - compId: "articles-docs-terminology-cloud-run-url", - crumb: "Terminology", + id: "upgrading", + title: "Upgrading Cloud Run", + relativePath: "docs/cloudrun/upgrading.mdx", + compId: "articles-docs-cloudrun-upgrading", + crumb: null, }, { - id: "terminology/input-props", - title: "Input Props", - relativePath: "docs/terminology/input-props.mdx", - compId: "articles-docs-terminology-input-props", - crumb: "Terminology", + id: "cloudrun-alpha", + title: "Cloud Run Alpha", + relativePath: "docs/cloudrun-alpha.mdx", + compId: "articles-docs-cloudrun-alpha", + crumb: "Version Upgrade", }, { - id: "terminology/public-dir", - title: "Public directory", - relativePath: "docs/terminology/public-dir.mdx", - compId: "articles-docs-terminology-public-dir", - crumb: "Terminology", + id: "cloudrun", + title: "@remotion/cloudrun", + relativePath: "docs/cloudrun.mdx", + compId: "articles-docs-cloudrun", + crumb: null, }, { - id: "terminology/root-file", - title: "Root file", - relativePath: "docs/terminology/root-file.mdx", - compId: "articles-docs-terminology-root-file", - crumb: "Terminology", + id: "compare/motion-canvas", + title: "How does Remotion compare to Motion Canvas?", + relativePath: "docs/compare/motion-canvas.mdx", + compId: "articles-docs-compare-motion-canvas", + crumb: "FAQ", }, { - id: "terminology/service-name", - title: "Service Name", - relativePath: "docs/terminology/service-name.mdx", - compId: "articles-docs-terminology-service-name", - crumb: "Terminology", + id: "composition", + title: "<Composition>", + relativePath: "docs/composition.mdx", + compId: "articles-docs-composition", + crumb: "API", }, { - id: "terminology/serve-url", - title: "Serve URL", - relativePath: "docs/terminology/serve-url.mdx", - compId: "articles-docs-terminology-serve-url", - crumb: "Terminology", + id: "config", + title: "Configuration file", + relativePath: "docs/config.mdx", + compId: "articles-docs-config", + crumb: "remotion.config.ts", }, { - id: "terminology/composition", - title: "Composition", - relativePath: "docs/terminology/composition.mdx", - compId: "articles-docs-terminology-composition", - crumb: "Terminology", + id: "continue-render", + title: "continueRender()", + relativePath: "docs/continue-render.mdx", + compId: "articles-docs-continue-render", + crumb: "API", }, { - id: "terminology/remotion-root", - title: "Remotion Root", - relativePath: "docs/terminology/remotion-root.mdx", - compId: "articles-docs-terminology-remotion-root", - crumb: "Terminology", + id: "contributing/bounty", + title: "Take and solve bounty issues", + relativePath: "docs/contributing/bounty.mdx", + compId: "articles-docs-contributing-bounty", + crumb: "Contributing", }, { - id: "motion-blur/camera-motion-blur", - title: "<CameraMotionBlur>", - relativePath: "docs/motion-blur/camera-motion-blur.mdx", - compId: "articles-docs-motion-blur-camera-motion-blur", - crumb: "Realistic camera effect", + id: "contributing/docs", + title: "Contributing to the documentation", + relativePath: "docs/contributing/docs.mdx", + compId: "articles-docs-contributing-docs", + crumb: "Contributing", }, { - id: "motion-blur/index", - title: "@remotion/motion-blur", - relativePath: "docs/motion-blur/index.mdx", - compId: "articles-docs-motion-blur-index", - crumb: null, + id: "contributing/feature", + title: "Implementing a new feature", + relativePath: "docs/contributing/feature.mdx", + compId: "articles-docs-contributing-feature", + crumb: "Contributing", }, { - id: "motion-blur/motion-blur", - title: "<MotionBlur>", - relativePath: "docs/motion-blur/motion-blur.mdx", - compId: "articles-docs-motion-blur-motion-blur", - crumb: null, + id: "contributing/formatting", + title: "Formatting in the Remotion repo", + relativePath: "docs/contributing/formatting.mdx", + compId: "articles-docs-contributing-formatting", + crumb: "Contributing", }, { - id: "motion-blur/trail", - title: "<Trail>", - relativePath: "docs/motion-blur/trail.mdx", - compId: "articles-docs-motion-blur-trail", - crumb: "Motion blur", + id: "contributing/index", + title: "Contributing to Remotion", + relativePath: "docs/contributing/index.mdx", + compId: "articles-docs-contributing-index", + crumb: "How to be awesome", }, { - id: "freeze", - title: "<Freeze>", - relativePath: "docs/freeze.mdx", - compId: "articles-docs-freeze", - crumb: "API", + id: "contributing/ineligible", + title: "Ineligible for bounties", + relativePath: "docs/contributing/ineligible.mdx", + compId: "articles-docs-contributing-ineligible", + crumb: "Contributing", }, { - id: "terminology", - title: "Terminology", - relativePath: "docs/terminology.mdx", - compId: "articles-docs-terminology", - crumb: "The Remotion dictionary", + id: "contributing/option", + title: "Implementing a new option", + relativePath: "docs/contributing/option.mdx", + compId: "articles-docs-contributing-option", + crumb: "Contributing", + }, + { + id: "contributing/presentation", + title: "Contribute your own presentation", + relativePath: "docs/contributing/presentation.mdx", + compId: "articles-docs-contributing-presentation", + crumb: "Contributing", + }, + { + id: "contributing/rust", + title: "Contributing Rust code", + relativePath: "docs/contributing/rust.mdx", + compId: "articles-docs-contributing-rust", + crumb: "Contributing", + }, + { + id: "authoring-packages", + title: "Authoring a Remotion library", + relativePath: "docs/creating-a-library.mdx", + compId: "articles-docs-creating-a-library", + crumb: "How to", }, { id: "data-fetching", @@ -609,102 +658,158 @@ export const articles = [ crumb: "How to", }, { - id: "target-closed", - title: "'Target closed' error message", - relativePath: "docs/target-closed.mdx", - compId: "articles-docs-target-closed", - crumb: "Troubleshooting", + id: "dataset-render", + title: "Render videos programmatically from a dataset", + relativePath: "docs/dataset-render.mdx", + compId: "articles-docs-dataset-render", + crumb: "Tutorials", }, { - id: "wrong-composition-mount", - title: "Wrongly mounted <Composition>", - relativePath: "docs/wrong-composition-mount.mdx", - compId: "articles-docs-wrong-composition-mount", + id: "delay-render", + title: "delayRender() and continueRender()", + relativePath: "docs/delay-render.mdx", + compId: "articles-docs-delay-render", + crumb: "How to", + }, + { + id: "docker", + title: "Dockerizing a Remotion app", + relativePath: "docs/docker.mdx", + compId: "articles-docs-docker", + crumb: "Building video apps", + }, + { + id: "webpack-dynamic-imports", + title: "Webpack dynamic imports", + relativePath: "docs/dynamic-import.mdx", + compId: "articles-docs-dynamic-import", + crumb: "Knowledge Base", + }, + { + id: "dynamic-metadata", + title: "Variable duration and dimensions", + relativePath: "docs/dynamic-metadata.mdx", + compId: "articles-docs-dynamic-metadata", + crumb: "How To", + }, + { + id: "easing", + title: "Easing", + relativePath: "docs/easing.mdx", + compId: "articles-docs-easing", + crumb: "API", + }, + { + id: "enable-scss/enable-scss", + title: "enableScss()", + relativePath: "docs/enable-scss/enable-scss.mdx", + compId: "articles-docs-enable-scss-enable-scss", + crumb: "@remotion/enable-scss", + }, + { + id: "enable-scss/overview", + title: "@remotion/enable-scss", + relativePath: "docs/enable-scss/overview.mdx", + compId: "articles-docs-enable-scss-overview", + crumb: null, + }, + { + id: "enametoolong", + title: "ENAMETOOLONG", + relativePath: "docs/enametoolong.mdx", + compId: "articles-docs-enametoolong", crumb: "Troubleshooting", }, { - id: "flickering", - title: "Flickering", - relativePath: "docs/flickering.mdx", - compId: "articles-docs-flickering", - crumb: "How to avoid", + id: "encoding", + title: "Encoding Guide", + relativePath: "docs/encoding.mdx", + compId: "articles-docs-encoding", + crumb: "Codecs and more", }, { - id: "contributing/ineligible", - title: "Ineligible for bounties", - relativePath: "docs/contributing/ineligible.mdx", - compId: "articles-docs-contributing-ineligible", - crumb: "Contributing", + id: "env-variables", + title: "Environment variables", + relativePath: "docs/env-variables.mdx", + compId: "articles-docs-env-variables", + crumb: "How To", }, { - id: "contributing/bounty", - title: "Take and solve bounty issues", - relativePath: "docs/contributing/bounty.mdx", - compId: "articles-docs-contributing-bounty", - crumb: "Contributing", + id: "ffmpeg", + title: "Installing FFmpeg", + relativePath: "docs/ffmpeg.mdx", + compId: "articles-docs-ffmpeg", + crumb: "(you don't have to)", }, { - id: "contributing/rust", - title: "Contributing Rust code", - relativePath: "docs/contributing/rust.mdx", - compId: "articles-docs-contributing-rust", - crumb: "Contributing", + id: "figma", + title: "Import from Figma", + relativePath: "docs/figma.mdx", + compId: "articles-docs-figma", + crumb: "The best of both", + }, + { + id: "flickering", + title: "Flickering", + relativePath: "docs/flickering.mdx", + compId: "articles-docs-flickering", + crumb: "How to avoid", }, { - id: "contributing/option", - title: "Implementing a new option", - relativePath: "docs/contributing/option.mdx", - compId: "articles-docs-contributing-option", - crumb: "Contributing", + id: "folder", + title: "<Folder>", + relativePath: "docs/folder.mdx", + compId: "articles-docs-folder", + crumb: "API", }, { - id: "contributing/presentation", - title: "Contribute your own presentation", - relativePath: "docs/contributing/presentation.mdx", - compId: "articles-docs-contributing-presentation", - crumb: "Contributing", + id: "font-picker", + title: "Build a Google Font picker", + relativePath: "docs/font-picker.mdx", + compId: "articles-docs-font-picker", + crumb: "Building video apps", }, { - id: "contributing/feature", - title: "Implementing a new feature", - relativePath: "docs/contributing/feature.mdx", - compId: "articles-docs-contributing-feature", - crumb: "Contributing", + id: "fonts-api", + title: "@remotion/fonts", + relativePath: "docs/fonts-api/index.mdx", + compId: "articles-docs-fonts-api-index", + crumb: null, }, { - id: "contributing/index", - title: "Contributing to Remotion", - relativePath: "docs/contributing/index.mdx", - compId: "articles-docs-contributing-index", - crumb: "How to be awesome", + id: "fonts-api/load-font", + title: "loadFont()", + relativePath: "docs/fonts-api/load-font.mdx", + compId: "articles-docs-fonts-api-load-font", + crumb: "@remotion/fonts", }, { - id: "contributing/formatting", - title: "Formatting in the Remotion repo", - relativePath: "docs/contributing/formatting.mdx", - compId: "articles-docs-contributing-formatting", - crumb: "Contributing", + id: "fonts", + title: "Using fonts", + relativePath: "docs/fonts.mdx", + compId: "articles-docs-fonts", + crumb: "Techniques", }, { - id: "contributing/docs", - title: "Contributing to the documentation", - relativePath: "docs/contributing/docs.mdx", - compId: "articles-docs-contributing-docs", - crumb: "Contributing", + id: "freeze", + title: "<Freeze>", + relativePath: "docs/freeze.mdx", + compId: "articles-docs-freeze", + crumb: "API", }, { - id: "third-party", - title: "Third party integrations", - relativePath: "docs/third-party.mdx", - compId: "articles-docs-third-party", - crumb: "Integrations", + id: "get-audio-data", + title: "getAudioData()", + relativePath: "docs/get-audio-data.mdx", + compId: "articles-docs-get-audio-data", + crumb: "@remotion/media-utils", }, { - id: "tailwind", - title: "TailwindCSS", - relativePath: "docs/tailwind.mdx", - compId: "articles-docs-tailwind", - crumb: "text-lg font-bold", + id: "get-audio-duration-in-seconds", + title: "getAudioDurationInSeconds()", + relativePath: "docs/get-audio-duration-in-seconds.mdx", + compId: "articles-docs-get-audio-duration-in-seconds", + crumb: "@remotion/media-utils", }, { id: "get-image-dimensions", @@ -714,115 +819,150 @@ export const articles = [ crumb: "@remotion/media-utils", }, { - id: "cancel-render", - title: "cancelRender()", - relativePath: "docs/cancel-render.mdx", - compId: "articles-docs-cancel-render", - crumb: "How to", + id: "get-input-props", + title: "getInputProps()", + relativePath: "docs/get-input-props.mdx", + compId: "articles-docs-get-input-props", + crumb: "API", }, { - id: "spline", - title: "Import from Spline", - relativePath: "docs/spline.mdx", - compId: "articles-docs-spline", - crumb: "Integrations", + id: "get-remotion-environment", + title: "getRemotionEnvironment()", + relativePath: "docs/get-remotion-environment.mdx", + compId: "articles-docs-get-remotion-environment", + crumb: "API", }, { - id: "use-video-texture", - title: "useVideoTexture()", - relativePath: "docs/use-video-texture.mdx", - compId: "articles-docs-use-video-texture", - crumb: "@remotion/three", + id: "getstaticfiles", + title: "getStaticFiles()", + relativePath: "docs/get-static-files.mdx", + compId: "articles-docs-get-static-files", + crumb: "API", }, { - id: "gpu", - title: "Using the GPU", - relativePath: "docs/gpu.mdx", - compId: "articles-docs-gpu", - crumb: "Need for Speed", + id: "get-video-metadata", + title: "getVideoMetadata()", + relativePath: "docs/get-video-metadata.mdx", + compId: "articles-docs-get-video-metadata", + crumb: "@remotion/media-utils", }, { - id: "timeout", - title: "Debugging timeouts", - relativePath: "docs/timeout.mdx", - compId: "articles-docs-timeout", - crumb: "Troubleshooting", + id: "get-waveform-portion", + title: "getWaveformPortion()", + relativePath: "docs/get-waveform-portion.mdx", + compId: "articles-docs-get-waveform-portion", + crumb: "@remotion/media-utils", }, { - id: "renderer", - title: "@remotion/renderer", - relativePath: "docs/renderer.mdx", - compId: "articles-docs-renderer", - crumb: null, + id: "getting-started", + title: "Creating a new project", + relativePath: "docs/getting-started.mdx", + compId: "articles-docs-getting-started", + crumb: "Let's begin!", }, { - id: "three", - title: "@remotion/three", - relativePath: "docs/three.mdx", - compId: "articles-docs-three", + id: "get-gif-duration-in-seconds", + title: "getGifDurationInSeconds()", + relativePath: "docs/gif/get-gif-duration-in-seconds.mdx", + compId: "articles-docs-gif-get-gif-duration-in-seconds", + crumb: "@remotion/gif", + }, + { + id: "gif/gif", + title: "<Gif>", + relativePath: "docs/gif/gif.mdx", + compId: "articles-docs-gif-gif", + crumb: "@remotion/gif", + }, + { + id: "gif/index", + title: "@remotion/gif", + relativePath: "docs/gif/index.mdx", + compId: "articles-docs-gif-index", crumb: null, }, { - id: "absolute-fill", - title: "<AbsoluteFill>", - relativePath: "docs/absolute-fill.mdx", - compId: "articles-docs-absolute-fill", - crumb: "API", + id: "gif/preload-gif", + title: "preloadGif()", + relativePath: "docs/gif/preload-gif.mdx", + compId: "articles-docs-gif-preload-gif", + crumb: "@remotion/gif", }, { - id: "offthreadvideo", - title: "<OffthreadVideo>", - relativePath: "docs/offthreadvideo.mdx", - compId: "articles-docs-offthreadvideo", - crumb: "API", + id: "google-fonts/get-available-fonts", + title: "getAvailableFonts()", + relativePath: "docs/google-fonts/get-available-fonts.mdx", + compId: "articles-docs-google-fonts-get-available-fonts", + crumb: "@remotion/google-fonts", }, { - id: "dynamic-metadata", - title: "Variable duration and dimensions", - relativePath: "docs/dynamic-metadata.mdx", - compId: "articles-docs-dynamic-metadata", - crumb: "How To", + id: "google-fonts/get-info", + title: "getInfo()", + relativePath: "docs/google-fonts/get-info.mdx", + compId: "articles-docs-google-fonts-get-info", + crumb: "@remotion/google-fonts", }, { - id: "zod-types/z-textarea", - title: "zTextarea()", - relativePath: "docs/zod-types/z-textarea.mdx", - compId: "articles-docs-zod-types-z-textarea", + id: "google-fonts", + title: "@remotion/google-fonts", + relativePath: "docs/google-fonts/index.mdx", + compId: "articles-docs-google-fonts-index", crumb: null, }, { - id: "zod-types/z-color", - title: "zColor()", - relativePath: "docs/zod-types/z-color.mdx", - compId: "articles-docs-zod-types-z-color", - crumb: null, + id: "google-fonts/load-font", + title: "loadFont()", + relativePath: "docs/google-fonts/load-font.mdx", + compId: "articles-docs-google-fonts-load-font", + crumb: "@remotion/google-fonts", }, { - id: "zod-types/index", - title: "@remotion/zod-types", - relativePath: "docs/zod-types/index.mdx", - compId: "articles-docs-zod-types-index", - crumb: "Schema", + id: "gpu", + title: "Using the GPU", + relativePath: "docs/gpu.mdx", + compId: "articles-docs-gpu", + crumb: "Need for Speed", }, { - id: "non-seekable-media", - title: "Non-seekable media", - relativePath: "docs/non-seekable-media.mdx", - compId: "articles-docs-non-seekable-media", - crumb: "Troubleshooting", + id: "props-resolution", + title: "How props get resolved", + relativePath: "docs/how-props-flow.mdx", + compId: "articles-docs-how-props-flow", + crumb: "Parameterized videos", }, { - id: "docker", - title: "Dockerizing a Remotion app", - relativePath: "docs/docker.mdx", - compId: "articles-docs-docker", - crumb: "Building video apps", + id: "iframe", + title: "<IFrame>", + relativePath: "docs/iframe.mdx", + compId: "articles-docs-iframe", + crumb: "API", + }, + { + id: "img", + title: "<Img>", + relativePath: "docs/img.mdx", + compId: "articles-docs-img", + crumb: "API", + }, + { + id: "assets", + title: "Importing assets", + relativePath: "docs/importing-assets.mdx", + compId: "articles-docs-importing-assets", + crumb: "How To", + }, + { + id: "install-whisper-cpp/convert-to-captions", + title: "convertToCaptions()", + relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx", + compId: "articles-docs-install-whisper-cpp-convert-to-captions", + crumb: "@remotion/install-whisper-cpp", }, { - id: "install-whisper-cpp/install-whisper-cpp", - title: "installWhisperCpp()", - relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx", - compId: "articles-docs-install-whisper-cpp-install-whisper-cpp", + id: "install-whisper-cpp/download-whisper-model", + title: "downloadWhisperModel()", + relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx", + compId: "articles-docs-install-whisper-cpp-download-whisper-model", crumb: "@remotion/install-whisper-cpp", }, { @@ -832,6 +972,13 @@ export const articles = [ compId: "articles-docs-install-whisper-cpp-index", crumb: "Transcribe audio locally", }, + { + id: "install-whisper-cpp/install-whisper-cpp", + title: "installWhisperCpp()", + relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx", + compId: "articles-docs-install-whisper-cpp-install-whisper-cpp", + crumb: "@remotion/install-whisper-cpp", + }, { id: "install-whisper-cpp/transcribe", title: "transcribe()", @@ -840,706 +987,710 @@ export const articles = [ crumb: "@remotion/install-whisper-cpp", }, { - id: "install-whisper-cpp/convert-to-captions", - title: "convertToCaptions()", - relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx", - compId: "articles-docs-install-whisper-cpp-convert-to-captions", - crumb: "@remotion/install-whisper-cpp", - }, - { - id: "install-whisper-cpp/download-whisper-model", - title: "downloadWhisperModel()", - relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx", - compId: "articles-docs-install-whisper-cpp-download-whisper-model", - crumb: "@remotion/install-whisper-cpp", + id: "interpolate-colors", + title: "interpolateColors()", + relativePath: "docs/interpolate-colors.mdx", + compId: "articles-docs-interpolate-colors", + crumb: "API", }, { - id: "get-audio-duration-in-seconds", - title: "getAudioDurationInSeconds()", - relativePath: "docs/get-audio-duration-in-seconds.mdx", - compId: "articles-docs-get-audio-duration-in-seconds", - crumb: "@remotion/media-utils", + id: "interpolate", + title: "interpolate()", + relativePath: "docs/interpolate.mdx", + compId: "articles-docs-interpolate", + crumb: "API", }, { - id: "dataset-render", - title: "Render videos programmatically from a dataset", - relativePath: "docs/dataset-render.mdx", - compId: "articles-docs-dataset-render", - crumb: "Tutorials", + id: "javascript", + title: "Plain JavaScript", + relativePath: "docs/jsx-support.mdx", + compId: "articles-docs-jsx-support", + crumb: "How To", }, { - id: "prefetch", - title: "prefetch()", - relativePath: "docs/prefetch.mdx", - compId: "articles-docs-prefetch", - crumb: "API", + id: "lambda/api", + title: "@remotion/lambda", + relativePath: "docs/lambda/api.mdx", + compId: "articles-docs-lambda-api", + crumb: "Render videos without servers on AWS", }, { - id: "audio-buffer-to-data-url", - title: "audioBufferToDataUrl()", - relativePath: "docs/audiobuffertodataurl.mdx", - compId: "articles-docs-audiobuffertodataurl", - crumb: "@remotion/media-utils", + id: "authentication", + title: "Authentication", + relativePath: "docs/lambda/authentication.mdx", + compId: "articles-docs-lambda-authentication", + crumb: "Lambda", }, { - id: "animating-properties", - title: "Animating properties", - relativePath: "docs/animating-properties.mdx", - compId: "articles-docs-animating-properties", - crumb: "The basics", + id: "autodelete", + title: "Auto-delete renders", + relativePath: "docs/lambda/autodelete.mdx", + compId: "articles-docs-lambda-autodelete", + crumb: "Lambda API", }, { - id: "getstaticfiles", - title: "getStaticFiles()", - relativePath: "docs/get-static-files.mdx", - compId: "articles-docs-get-static-files", - crumb: "API", + id: "lambda/bucket-naming", + title: "Bucket names in Remotion Lambda", + relativePath: "docs/lambda/bucket-naming.mdx", + compId: "articles-docs-lambda-bucket-naming", + crumb: "@remotion/lambda", }, { - id: "paths/reverse-path", - title: "reversePath()", - relativePath: "docs/paths/reverse-path.mdx", - compId: "articles-docs-paths-reverse-path", - crumb: "@remotion/paths", + id: "changelog", + title: "Prerelease Changelog", + relativePath: "docs/lambda/changelog.mdx", + compId: "articles-docs-lambda-changelog", + crumb: null, }, { - id: "paths/evolve-path", - title: "evolvePath()", - relativePath: "docs/paths/evolve-path.mdx", - compId: "articles-docs-paths-evolve-path", - crumb: "@remotion/paths", + id: "checklist", + title: "Production Checklist", + relativePath: "docs/lambda/checklist.mdx", + compId: "articles-docs-lambda-checklist", + crumb: "Lambda", }, { - id: "paths/get-subpaths", - title: "getSubpaths()", - relativePath: "docs/paths/get-subpaths.mdx", - compId: "articles-docs-paths-get-subpaths", - crumb: "@remotion/paths", + id: "lambda/cli/compositions", + title: "npx remotion lambda compositions", + relativePath: "docs/lambda/cli/compositions.mdx", + compId: "articles-docs-lambda-cli-compositions", + crumb: "Lambda CLI Reference", }, { - id: "paths/get-point-at-length", - title: "getPointAtLength()", - relativePath: "docs/paths/get-point-at-length.mdx", - compId: "articles-docs-paths-get-point-at-length", - crumb: "@remotion/paths", + id: "functions", + title: "npx remotion lambda functions", + relativePath: "docs/lambda/cli/functions.mdx", + compId: "articles-docs-lambda-cli-functions", + crumb: "Lambda CLI Reference", }, { - id: "paths/get-length", - title: "getLength()", - relativePath: "docs/paths/get-length.mdx", - compId: "articles-docs-paths-get-length", - crumb: "@remotion/paths", + id: "policies", + title: "npx remotion lambda policies", + relativePath: "docs/lambda/cli/policies.mdx", + compId: "articles-docs-lambda-cli-policies", + crumb: "Lambda CLI Reference", }, { - id: "paths/scale-path", - title: "scalePath()", - relativePath: "docs/paths/scale-path.mdx", - compId: "articles-docs-paths-scale-path", - crumb: "@remotion/paths", + id: "quotas", + title: "npx remotion lambda quotas", + relativePath: "docs/lambda/cli/quotas.mdx", + compId: "articles-docs-lambda-cli-quotas", + crumb: "Lambda CLI Reference", }, { - id: "paths/get-parts", - title: "getParts()", - relativePath: "docs/paths/get-parts.mdx", - compId: "articles-docs-paths-get-parts", - crumb: "@remotion/paths", + id: "regions", + title: "npx remotion lambda regions", + relativePath: "docs/lambda/cli/regions.mdx", + compId: "articles-docs-lambda-cli-regions", + crumb: "Lambda CLI Reference", }, { - id: "paths/get-tangent-at-length", - title: "getTangentAtLength()", - relativePath: "docs/paths/get-tangent-at-length.mdx", - compId: "articles-docs-paths-get-tangent-at-length", - crumb: "@remotion/paths", + id: "render", + title: "npx remotion lambda render", + relativePath: "docs/lambda/cli/render.mdx", + compId: "articles-docs-lambda-cli-render", + crumb: "Lambda CLI Reference", }, { - id: "paths/get-instruction-index-at-length", - title: "getInstructionIndexAtLength()", - relativePath: "docs/paths/get-instruction-index-at-length.mdx", - compId: "articles-docs-paths-get-instruction-index-at-length", - crumb: "@remotion/paths", + id: "sites", + title: "npx remotion lambda sites", + relativePath: "docs/lambda/cli/sites.mdx", + compId: "articles-docs-lambda-cli-sites", + crumb: "Lambda CLI Reference", }, { - id: "paths/extend-viewbox", - title: "extendViewBox()", - relativePath: "docs/paths/extend-viewbox.mdx", - compId: "articles-docs-paths-extend-viewbox", - crumb: "@remotion/paths", + id: "still", + title: "npx remotion lambda still", + relativePath: "docs/lambda/cli/still.mdx", + compId: "articles-docs-lambda-cli-still", + crumb: "Lambda CLI Reference", }, { - id: "paths/parse-path", - title: "parsePath()", - relativePath: "docs/paths/parse-path.mdx", - compId: "articles-docs-paths-parse-path", - crumb: "@remotion/paths", + id: "cli", + title: "@remotion/lambda - CLI", + relativePath: "docs/lambda/cli.mdx", + compId: "articles-docs-lambda-cli", + crumb: null, }, { - id: "paths/translate-path", - title: "translatePath()", - relativePath: "docs/paths/translate-path.mdx", - compId: "articles-docs-paths-translate-path", - crumb: "@remotion/paths", + id: "concurrency", + title: "Concurrency", + relativePath: "docs/lambda/concurrency.mdx", + compId: "articles-docs-lambda-concurrency", + crumb: "Lambda", }, { - id: "paths/normalize-path", - title: "normalizePath()", - relativePath: "docs/paths/normalize-path.mdx", - compId: "articles-docs-paths-normalize-path", - crumb: "@remotion/paths", + id: "optimizing-cost", + title: "Optimizing for cost", + relativePath: "docs/lambda/cost.mdx", + compId: "articles-docs-lambda-cost", + crumb: "Lambda", }, { - id: "paths/index", - title: "@remotion/paths", - relativePath: "docs/paths/index.mdx", - compId: "articles-docs-paths-index", - crumb: "SVG", + id: "custom-destination", + title: "Customizing Lambda output destination", + relativePath: "docs/lambda/custom-destination.mdx", + compId: "articles-docs-lambda-custom-destination", + crumb: "Lambda", }, { - id: "paths/get-bounding-box", - title: "getBoundingBox()", - relativePath: "docs/paths/get-bounding-box.mdx", - compId: "articles-docs-paths-get-bounding-box", - crumb: "@remotion/paths", + id: "lambda/custom-layers", + title: "Custom Layers", + relativePath: "docs/lambda/custom-layers.mdx", + compId: "articles-docs-lambda-custom-layers", + crumb: "Lambda", }, { - id: "paths/warp-path", - title: "warpPath()", - relativePath: "docs/paths/warp-path.mdx", - compId: "articles-docs-paths-warp-path", - crumb: "@remotion/paths", + id: "deletefunction", + title: "deleteFunction()", + relativePath: "docs/lambda/deletefunction.mdx", + compId: "articles-docs-lambda-deletefunction", + crumb: "Lambda API", }, { - id: "paths/interpolate-path", - title: "interpolatePath()", - relativePath: "docs/paths/interpolate-path.mdx", - compId: "articles-docs-paths-interpolate-path", - crumb: "@remotion/paths", + id: "deleterender", + title: "deleteRender()", + relativePath: "docs/lambda/deleterender.mdx", + compId: "articles-docs-lambda-deleterender", + crumb: "Lambda API", }, { - id: "paths/serialize-instructions", - title: "serializeInstructions()", - relativePath: "docs/paths/serialize-instructions.mdx", - compId: "articles-docs-paths-serialize-instructions", - crumb: "@remotion/paths", + id: "deletesite", + title: "deleteSite()", + relativePath: "docs/lambda/deletesite.mdx", + compId: "articles-docs-lambda-deletesite", + crumb: "Lambda API", }, { - id: "paths/reset-path", - title: "resetPath()", - relativePath: "docs/paths/reset-path.mdx", - compId: "articles-docs-paths-reset-path", - crumb: "@remotion/paths", + id: "deployfunction", + title: "deployFunction()", + relativePath: "docs/lambda/deployfunction.mdx", + compId: "articles-docs-lambda-deployfunction", + crumb: "Lambda API", }, { - id: "paths/reduce-instructions", - title: "reduceInstructions()", - relativePath: "docs/paths/reduce-instructions.mdx", - compId: "articles-docs-paths-reduce-instructions", - crumb: "@remotion/paths", + id: "deploysite", + title: "deploySite()", + relativePath: "docs/lambda/deploysite.mdx", + compId: "articles-docs-lambda-deploysite", + crumb: "Lambda API", }, { - id: "rive/index", - title: "@remotion/rive", - relativePath: "docs/rive/index.mdx", - compId: "articles-docs-rive-index", - crumb: "Integrations", + id: "disk-size", + title: "Disk size", + relativePath: "docs/lambda/disk-size.mdx", + compId: "articles-docs-lambda-disk-size", + crumb: "Lambda", }, { - id: "rive/remotionrivecanvas", - title: "<RemotionRiveCanvas>", - relativePath: "docs/rive/remotionrivecanvas.mdx", - compId: "articles-docs-rive-remotionrivecanvas", - crumb: "@remotion/rive", + id: "downloadmedia", + title: "downloadMedia()", + relativePath: "docs/lambda/downloadmedia.mdx", + compId: "articles-docs-lambda-downloadmedia", + crumb: "Lambda API", }, { - id: "layers", - title: "Layers", - relativePath: "docs/layers.mdx", - compId: "articles-docs-layers", - crumb: "Designing videos", + id: "downloadvideo", + title: "downloadVideo()", + relativePath: "docs/lambda/downloadvideo.mdx", + compId: "articles-docs-lambda-downloadvideo", + crumb: "Lambda API", }, { - id: "use-img-and-iframe", - title: "<Img>, <Video> and <Audio>", - relativePath: "docs/use-img-and-iframe.mdx", - compId: "articles-docs-use-img-and-iframe", - crumb: "Best practices", + id: "estimateprice", + title: "estimatePrice()", + relativePath: "docs/lambda/estimateprice.mdx", + compId: "articles-docs-lambda-estimateprice", + crumb: "Lambda API", }, { - id: "font-picker", - title: "Build a Google Font picker", - relativePath: "docs/font-picker.mdx", - compId: "articles-docs-font-picker", - crumb: "Building video apps", + id: "lambda/faq", + title: "FAQ", + relativePath: "docs/lambda/faq.mdx", + compId: "articles-docs-lambda-faq", + crumb: "Lambda", }, { - id: "support", - title: "Support Policy", - relativePath: "docs/support.mdx", - compId: "articles-docs-support", - crumb: "Let us help you", + id: "feb-2022-outage", + title: "February 2022 Outage", + relativePath: "docs/lambda/feb-2022-outage.mdx", + compId: "articles-docs-lambda-feb-2022-outage", + crumb: null, }, { - id: "register-root", - title: "registerRoot()", - relativePath: "docs/register-root.mdx", - compId: "articles-docs-register-root", - crumb: "API", + id: "lambda/feb-2023-incident", + title: "Upgrade your Lambda functions to prevent breakage", + relativePath: "docs/lambda/feb-2023-incident.mdx", + compId: "articles-docs-lambda-feb-2023-incident", + crumb: "DevOps advisory", }, { - id: "props-resolution", - title: "How props get resolved", - relativePath: "docs/how-props-flow.mdx", - compId: "articles-docs-how-props-flow", - crumb: "Parameterized videos", + id: "getawsclient", + title: "getAwsClient()", + relativePath: "docs/lambda/getawsclient.mdx", + compId: "articles-docs-lambda-getawsclient", + crumb: "Lambda API", }, { - id: "preview", - title: "Preview your video", - relativePath: "docs/preview.mdx", - compId: "articles-docs-preview", - crumb: "Timeline basics", + id: "getcompositionsonlambda", + title: "getCompositionsOnLambda()", + relativePath: "docs/lambda/getcompositionsonlambda.mdx", + compId: "articles-docs-lambda-getcompositionsonlambda", + crumb: "Lambda API", }, { - id: "shapes/triangle", - title: "<Triangle />", - relativePath: "docs/shapes/triangle.mdx", - compId: "articles-docs-shapes-triangle", - crumb: "@remotion/shapes", + id: "getfunctioninfo", + title: "getFunctionInfo()", + relativePath: "docs/lambda/getfunctioninfo.mdx", + compId: "articles-docs-lambda-getfunctioninfo", + crumb: "Lambda API", }, { - id: "shapes/rect", - title: "<Rect />", - relativePath: "docs/shapes/rect.mdx", - compId: "articles-docs-shapes-rect", - crumb: "@remotion/shapes", + id: "getfunctions", + title: "getFunctions()", + relativePath: "docs/lambda/getfunctions.mdx", + compId: "articles-docs-lambda-getfunctions", + crumb: "Lambda API", }, { - id: "shapes/polygon", - title: "<Polygon />", - relativePath: "docs/shapes/polygon.mdx", - compId: "articles-docs-shapes-polygon", - crumb: "@remotion/shapes", + id: "getorcreatebucket", + title: "getOrCreateBucket()", + relativePath: "docs/lambda/getorcreatebucket.mdx", + compId: "articles-docs-lambda-getorcreatebucket", + crumb: "Lambda API", }, { - id: "shapes/circle", - title: "<Circle />", - relativePath: "docs/shapes/circle.mdx", - compId: "articles-docs-shapes-circle", - crumb: "@remotion/shapes", + id: "getregions", + title: "getRegions()", + relativePath: "docs/lambda/getregions.mdx", + compId: "articles-docs-lambda-getregions", + crumb: "Lambda API", }, { - id: "shapes/make-rect", - title: "makeRect()", - relativePath: "docs/shapes/make-rect.mdx", - compId: "articles-docs-shapes-make-rect", - crumb: "@remotion/shapes", + id: "getrenderprogress", + title: "getRenderProgress()", + relativePath: "docs/lambda/getrenderprogress.mdx", + compId: "articles-docs-lambda-getrenderprogress", + crumb: "Lambda API", }, { - id: "shapes/index", - title: "@remotion/shapes", - relativePath: "docs/shapes/index.mdx", - compId: "articles-docs-shapes-index", - crumb: "SVG component library", + id: "getrolepolicy", + title: "getRolePolicy()", + relativePath: "docs/lambda/getrolepolicy.mdx", + compId: "articles-docs-lambda-getrolepolicy", + crumb: "Lambda API", }, { - id: "shapes/ellipse", - title: "<Ellipse />", - relativePath: "docs/shapes/ellipse.mdx", - compId: "articles-docs-shapes-ellipse", - crumb: "@remotion/shapes", + id: "getsites", + title: "getSites()", + relativePath: "docs/lambda/getsites.mdx", + compId: "articles-docs-lambda-getsites", + crumb: "Lambda API", }, { - id: "shapes/make-triangle", - title: "makeTriangle()", - relativePath: "docs/shapes/make-triangle.mdx", - compId: "articles-docs-shapes-make-triangle", - crumb: "@remotion/shapes", + id: "getuserpolicy", + title: "getUserPolicy()", + relativePath: "docs/lambda/getuserpolicy.mdx", + compId: "articles-docs-lambda-getuserpolicy", + crumb: "Lambda API", }, { - id: "shapes/pie", - title: "<Pie />", - relativePath: "docs/shapes/pie.mdx", - compId: "articles-docs-shapes-pie", - crumb: "@remotion/shapes", + id: "lambda/go", + title: "Triggering renders from Go", + relativePath: "docs/lambda/go.mdx", + compId: "articles-docs-lambda-go", + crumb: "@remotion/lambda", }, { - id: "shapes/make-star", - title: "makeStar()", - relativePath: "docs/shapes/make-star.mdx", - compId: "articles-docs-shapes-make-star", - crumb: "@remotion/shapes", + id: "how-lambda-works", + title: "How Remotion Lambda works", + relativePath: "docs/lambda/how-lambda-works.mdx", + compId: "articles-docs-lambda-how-lambda-works", + crumb: "Lambda", }, { - id: "shapes/make-pie", - title: "makePie()", - relativePath: "docs/shapes/make-pie.mdx", - compId: "articles-docs-shapes-make-pie", - crumb: "@remotion/shapes", + id: "lambda/insights", + title: "Enable Lambda Insights", + relativePath: "docs/lambda/insights.mdx", + compId: "articles-docs-lambda-insights", + crumb: "Lambda", }, { - id: "shapes/make-ellipse", - title: "makeEllipse()", - relativePath: "docs/shapes/make-ellipse.mdx", - compId: "articles-docs-shapes-make-ellipse", - crumb: "@remotion/shapes", + id: "lambda/light-client", + title: "Light client", + relativePath: "docs/lambda/light-client.mdx", + compId: "articles-docs-lambda-light-client", + crumb: "Lambda", + }, + { + id: "lambda/limits", + title: "Lambda Limits", + relativePath: "docs/lambda/limits.mdx", + compId: "articles-docs-lambda-limits", + crumb: "Lambda", }, { - id: "shapes/star", - title: "<Star />", - relativePath: "docs/shapes/star.mdx", - compId: "articles-docs-shapes-star", - crumb: "@remotion/shapes", + id: "lambda/multiple-buckets", + title: "Multiple buckets in Remotion Lambda", + relativePath: "docs/lambda/multiple-buckets.mdx", + compId: "articles-docs-lambda-multiple-buckets", + crumb: "@remotion/lambda", }, { - id: "shapes/make-polygon", - title: "makePolygon()", - relativePath: "docs/shapes/make-polygon.mdx", - compId: "articles-docs-shapes-make-polygon", - crumb: "@remotion/shapes", + id: "naming-convention", + title: "Function naming convention", + relativePath: "docs/lambda/naming-convention.mdx", + compId: "articles-docs-lambda-naming-convention", + crumb: "Lambda", }, { - id: "shapes/make-circle", - title: "makeCircle()", - relativePath: "docs/shapes/make-circle.mdx", - compId: "articles-docs-shapes-make-circle", - crumb: "@remotion/shapes", + id: "lambda/permissions", + title: "Permissions", + relativePath: "docs/lambda/permissions.mdx", + compId: "articles-docs-lambda-permissions", + crumb: "Lambda", }, { - id: "series", - title: "<Series>", - relativePath: "docs/series.mdx", - compId: "articles-docs-series", - crumb: "API", + id: "lambda/php", + title: "Triggering renders from PHP", + relativePath: "docs/lambda/php.mdx", + compId: "articles-docs-lambda-php", + crumb: "@remotion/lambda", }, { - id: "bun", - title: "Bun support", - relativePath: "docs/bun.mdx", - compId: "articles-docs-bun", - crumb: "bun bun bun bun bun", + id: "presignurl", + title: "presignUrl()", + relativePath: "docs/lambda/presignurl.mdx", + compId: "articles-docs-lambda-presignurl", + crumb: "Lambda API", }, { - id: "loading-root-component", - title: "Root component Timeout", - relativePath: "docs/troubleshooting/loading-root-component.mdx", - compId: "articles-docs-troubleshooting-loading-root-component", - crumb: "Troubleshooting", + id: "lambda/python", + title: "Triggering renders from Python", + relativePath: "docs/lambda/python.mdx", + compId: "articles-docs-lambda-python", + crumb: "@remotion/lambda", }, { - id: "troubleshooting/background-image", - title: "Flickering when using background-image or mask-image", - relativePath: "docs/troubleshooting/background-image.mdx", - compId: "articles-docs-troubleshooting-background-image", - crumb: "Common mistakes", + id: "region-selection", + title: "Region selection", + relativePath: "docs/lambda/region-selection.mdx", + compId: "articles-docs-lambda-region-selection", + crumb: "Lambda", }, { - id: "rosetta", - title: "Apple Silicon under Rosetta", - relativePath: "docs/troubleshooting/rosetta.mdx", - compId: "articles-docs-troubleshooting-rosetta", - crumb: "Troubleshooting", + id: "rendermediaonlambda", + title: "renderMediaOnLambda()", + relativePath: "docs/lambda/rendermediaonlambda.mdx", + compId: "articles-docs-lambda-rendermediaonlambda", + crumb: "Lambda API", }, { - id: "troubleshooting/delay-render-proxy", - title: "Loading <Img> with src http://localhost:3000/proxy", - relativePath: "docs/troubleshooting/delay-render-proxy.mdx", - compId: "articles-docs-troubleshooting-delay-render-proxy", - crumb: "Troubleshooting", + id: "renderstillonlambda", + title: "renderStillOnLambda()", + relativePath: "docs/lambda/renderstillonlambda.mdx", + compId: "articles-docs-lambda-renderstillonlambda", + crumb: "Lambda API", }, { - id: "broken-fast-refresh", - title: "Fast Refresh not working", - relativePath: "docs/troubleshooting/broken-fast-refresh.mdx", - compId: "articles-docs-troubleshooting-broken-fast-refresh", - crumb: "Troubleshooting", + id: "rendervideoonlambda", + title: "renderVideoOnLambda()", + relativePath: "docs/lambda/rendervideoonlambda.mdx", + compId: "articles-docs-lambda-rendervideoonlambda", + crumb: "Lambda API", }, { - id: "troubleshooting/timed-out-page-function", - title: "Timed out evaluating page function", - relativePath: "docs/troubleshooting/timed-out-page-function.mdx", - compId: "articles-docs-troubleshooting-timed-out-page-function", - crumb: "Troubleshooting", + id: "runtime", + title: "Runtime", + relativePath: "docs/lambda/runtime.mdx", + compId: "articles-docs-lambda-runtime", + crumb: "Lambda", }, { - id: "troubleshooting/browser-launch", - title: "Failed to launch the browser process", - relativePath: "docs/troubleshooting/browser-launch.mdx", - compId: "articles-docs-troubleshooting-browser-launch", - crumb: "Troubleshooting", + id: "lambda/s3-public-access", + title: "Cannot create a S3 bucket using Remotion", + relativePath: "docs/lambda/s3-public-access.mdx", + compId: "articles-docs-lambda-s3-public-access", + crumb: "DevOps advisory", }, { - id: "troubleshooting/sigkill", - title: "Process quit with signal SIGKILL", - relativePath: "docs/troubleshooting/sigkill.mdx", - compId: "articles-docs-troubleshooting-sigkill", - crumb: "Troubleshooting", + id: "lambda/serverless-framework-integration", + title: "Using the Serverless Framework with Remotion Lambda", + relativePath: "docs/lambda/serverless-framework-integration.mdx", + compId: "articles-docs-lambda-serverless-framework-integration", + crumb: "@remotion/lambda", }, { - id: "troubleshooting/nextjs-image", - title: "Flickering when using Next.js <Image> tag", - relativePath: "docs/troubleshooting/nextjs-image.mdx", - compId: "articles-docs-troubleshooting-nextjs-image", - crumb: "Common mistakes", + id: "setup", + title: "Setup", + relativePath: "docs/lambda/setup.mdx", + compId: "articles-docs-lambda-setup", + crumb: "Lambda", }, { - id: "player-flicker", - title: "Avoiding flickering in <Player>", - relativePath: "docs/troubleshooting/video-flicker.mdx", - compId: "articles-docs-troubleshooting-video-flicker", - crumb: "Frame-perfection", + id: "simulatepermissions", + title: "simulatePermissions()", + relativePath: "docs/lambda/simulatepermissions.mdx", + compId: "articles-docs-lambda-simulatepermissions", + crumb: "Lambda API", }, { - id: "troubleshooting/debug-failed-render", - title: "Debugging render failures", - relativePath: "docs/troubleshooting/debug-failed-render.mdx", - compId: "articles-docs-troubleshooting-debug-failed-render", - crumb: "Troubleshooting", + id: "speculatefunctionname", + title: "speculateFunctionName()", + relativePath: "docs/lambda/speculateFunctionName.mdx", + compId: "articles-docs-lambda-speculateFunctionName", + crumb: "Lambda API", }, { - id: "defaultprops-too-big", - title: "defaultProps too big - could not serialize", - relativePath: "docs/troubleshooting/defaultprops-too-big.mdx", - compId: "articles-docs-troubleshooting-defaultprops-too-big", - crumb: "Troubleshooting", + id: "optimizing-speed", + title: "Optimizing for speed", + relativePath: "docs/lambda/speed.mdx", + compId: "articles-docs-lambda-speed", + crumb: "Lambda", }, { - id: "troubleshooting/bundling-bundle", - title: "Calling bundle() in bundled code", - relativePath: "docs/troubleshooting/bundling-bundle.mdx", - compId: "articles-docs-troubleshooting-bundling-bundle", - crumb: "Troubleshooting", + id: "lambda/sqs", + title: "Using Lambda with SQS", + relativePath: "docs/lambda/sqs.mdx", + compId: "articles-docs-lambda-sqs", + crumb: "@remotion/lambda", }, { - id: "troubleshooting/could-not-be-parsed-as-a-value-list", - title: "The source provided could not be parsed as a value list", - relativePath: - "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx", - compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list", - crumb: "Browser quirks", + id: "bucket-disallows-acl", + title: "The bucket does not allow ACLs", + relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx", + compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl", + crumb: "Lambda Troubleshooting", }, { - id: "cli/studio", - title: "npx remotion studio", - relativePath: "docs/cli/studio.mdx", - compId: "articles-docs-cli-studio", - crumb: "CLI Reference", + id: "debug", + title: "Debugging failed Lambda renders", + relativePath: "docs/lambda/troubleshooting/debug.mdx", + compId: "articles-docs-lambda-troubleshooting-debug", + crumb: "Lambda", }, { - id: "cli", - title: "Command line reference", - relativePath: "docs/cli/cli.mdx", - compId: "articles-docs-cli-cli", - crumb: null, + id: "permissions", + title: "AWS Permissions Troubleshooting", + relativePath: "docs/lambda/troubleshooting/permissions.mdx", + compId: "articles-docs-lambda-troubleshooting-permissions", + crumb: "Lambda Troubleshooting", }, { - id: "cli/bundle", - title: "npx remotion bundle", - relativePath: "docs/cli/bundle.mdx", - compId: "articles-docs-cli-bundle", - crumb: "CLI Reference", + id: "rate-limit", + title: "AWS Rate Limit Troubleshooting", + relativePath: "docs/lambda/troubleshooting/rate-limit.mdx", + compId: "articles-docs-lambda-troubleshooting-rate-limit", + crumb: "Lambda Troubleshooting", }, { - id: "cli/gpu", - title: "npx remotion gpu", - relativePath: "docs/cli/gpu.mdx", - compId: "articles-docs-cli-gpu", - crumb: "CLI Reference", + id: "lambda/troubleshooting/security-token", + title: "'\"The security token included in the request is invalid\"'", + relativePath: "docs/lambda/troubleshooting/security-token.mdx", + compId: "articles-docs-lambda-troubleshooting-security-token", + crumb: "Lambda", }, { - id: "cli/install", - title: "npx remotion install", - relativePath: "docs/cli/install.mdx", - compId: "articles-docs-cli-install", - crumb: "CLI Reference", + id: "unrecognizedclientexception", + title: "UnrecognizedClientException", + relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx", + compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception", + crumb: "Lambda Troubleshooting", }, { - id: "cli/versions", - title: "npx remotion versions", - relativePath: "docs/cli/versions.mdx", - compId: "articles-docs-cli-versions", - crumb: "CLI Reference", + id: "uninstall", + title: "Uninstall Lambda", + relativePath: "docs/lambda/uninstall.mdx", + compId: "articles-docs-lambda-uninstall", + crumb: null, + }, + { + id: "upgrading", + title: "Upgrading Lambda", + relativePath: "docs/lambda/upgrading.mdx", + compId: "articles-docs-lambda-upgrading", + crumb: null, }, { - id: "cli/browser/index", - title: "npx remotion browser", - relativePath: "docs/cli/browser/index.mdx", - compId: "articles-docs-cli-browser-index", - crumb: "@remotion/cli", + id: "validatewebhooksignature", + title: "validateWebhookSignature()", + relativePath: "docs/lambda/validatewebhooksignature.mdx", + compId: "articles-docs-lambda-validatewebhooksignature", + crumb: "Lambda API", }, { - id: "cli/browser/ensure", - title: "npx remotion browser ensure", - relativePath: "docs/cli/browser/ensure.mdx", - compId: "articles-docs-cli-browser-ensure", - crumb: "@remotion/cli", + id: "lambda/webhooks", + title: "Webhooks", + relativePath: "docs/lambda/webhooks.mdx", + compId: "articles-docs-lambda-webhooks", + crumb: "Lambda", }, { - id: "ffprobe", - title: "npx remotion ffprobe", - relativePath: "docs/cli/ffprobe.mdx", - compId: "articles-docs-cli-ffprobe", - crumb: "@remotion/cli", + id: "lambda/without-iam/ec2", + title: "Authenticating Lambda with EC2", + relativePath: "docs/lambda/without-iam/ec2.mdx", + compId: "articles-docs-lambda-without-iam-ec2", + crumb: "@remotion/lambda", }, { - id: "cli/render", - title: "npx remotion render", - relativePath: "docs/cli/render.mdx", - compId: "articles-docs-cli-render", - crumb: "CLI Reference", + id: "lambda/without-iam/example", + title: "Example setup without IAM user", + relativePath: "docs/lambda/without-iam/example.mdx", + compId: "articles-docs-lambda-without-iam-example", + crumb: "IAM Roles Example", }, { - id: "cli/compositions", - title: "npx remotion compositions", - relativePath: "docs/cli/compositions.mdx", - compId: "articles-docs-cli-compositions", - crumb: "CLI Reference", + id: "lambda/without-iam/index", + title: "Using Lambda with IAM roles", + relativePath: "docs/lambda/without-iam/index.mdx", + compId: "articles-docs-lambda-without-iam-index", + crumb: "Lambda without IAM", }, { - id: "cli/benchmark", - title: "npx remotion benchmark", - relativePath: "docs/cli/benchmark.mdx", - compId: "articles-docs-cli-benchmark", - crumb: "CLI Reference", + id: "lambda", + title: "@remotion/lambda", + relativePath: "docs/lambda.mdx", + compId: "articles-docs-lambda", + crumb: null, }, { - id: "ffmpeg", - title: "npx remotion ffmpeg", - relativePath: "docs/cli/ffmpeg.mdx", - compId: "articles-docs-cli-ffmpeg", - crumb: "@remotion/cli", + id: "layers", + title: "Layers", + relativePath: "docs/layers.mdx", + compId: "articles-docs-layers", + crumb: "Designing videos", }, { - id: "cli/still", - title: "npx remotion still", - relativePath: "docs/cli/still.mdx", - compId: "articles-docs-cli-still", - crumb: "CLI Reference", + id: "layout-utils/best-practices", + title: "Best practices for @remotion/layout-utils", + relativePath: "docs/layout-utils/best-practices.mdx", + compId: "articles-docs-layout-utils-best-practices", + crumb: "@remotion/layout-utils", }, { - id: "cli/help", - title: "npx remotion help", - relativePath: "docs/cli/help.mdx", - compId: "articles-docs-cli-help", - crumb: "CLI Reference", + id: "layout-utils/fill-text-box", + title: "fillTextBox()", + relativePath: "docs/layout-utils/fill-text-box.mdx", + compId: "articles-docs-layout-utils-fill-text-box", + crumb: "@remotion/layout-utils", }, { - id: "cli/upgrade", - title: "npx remotion upgrade", - relativePath: "docs/cli/upgrade.mdx", - compId: "articles-docs-cli-upgrade", - crumb: "CLI Reference", + id: "layout-utils/fit-text", + title: "fitText()", + relativePath: "docs/layout-utils/fit-text.mdx", + compId: "articles-docs-layout-utils-fit-text", + crumb: "@remotion/layout-utils", }, { - id: "watchstaticfile", - title: "watchStaticFile()", - relativePath: "docs/watch-static-file.mdx", - compId: "articles-docs-watch-static-file", - crumb: "API", + id: "layout-utils/index", + title: "@remotion/layout-utils", + relativePath: "docs/layout-utils/index.mdx", + compId: "articles-docs-layout-utils-index", + crumb: null, }, { - id: "chromium-flags", - title: "Chromium flags", - relativePath: "docs/chromium-flags.mdx", - compId: "articles-docs-chromium-flags", - crumb: "Tweaks", + id: "layout-utils/measure-text", + title: "measureText()", + relativePath: "docs/layout-utils/measure-text.mdx", + compId: "articles-docs-layout-utils-measure-text", + crumb: "@remotion/layout-utils", }, { - id: "noise-visualization", - title: "Noise visualization", - relativePath: "docs/noise-visualization.mdx", - compId: "articles-docs-noise-visualization", - crumb: "Techniques", + id: "legacy-babel", + title: "Using legacy Babel transpilation", + relativePath: "docs/legacy-babel-loader.mdx", + compId: "articles-docs-legacy-babel-loader", + crumb: "How To", }, { - id: "acknowledgements", - title: "Acknowledgements", - relativePath: "docs/acknowledgements.mdx", - compId: "articles-docs-acknowledgements", - crumb: "Credits", + id: "license", + title: "License & Pricing", + relativePath: "docs/license.mdx", + compId: "articles-docs-license", + crumb: null, }, { - id: "miscellaneous/snippets/accelerated-video", - title: "Change the speed of a video over time", - relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx", - compId: "articles-docs-miscellaneous-snippets-accelerated-video", - crumb: "Snippets", + id: "loop", + title: "<Loop>", + relativePath: "docs/loop.mdx", + compId: "articles-docs-loop", + crumb: "API", }, { - id: "miscellaneous/snippets/fps-converter", - title: "FPS converter", - relativePath: "docs/miscellaneous/snippets/fps-converter.mdx", - compId: "articles-docs-miscellaneous-snippets-fps-converter", - crumb: "Snippets", + id: "getlottiemetadata", + title: "getLottieMetadata()", + relativePath: "docs/lottie/getlottiemetadata.mdx", + compId: "articles-docs-lottie-getlottiemetadata", + crumb: "@remotion/lottie", }, { - id: "hls", - title: "HLS support (HTTP Live Streaming)", - relativePath: "docs/miscellaneous/snippets/hls.mdx", - compId: "articles-docs-miscellaneous-snippets-hls", - crumb: "Video", + id: "lottie-index", + title: "@remotion/lottie", + relativePath: "docs/lottie/index.mdx", + compId: "articles-docs-lottie-index", + crumb: null, }, { - id: "miscellaneous/snippets/offthread-video-while-rendering", - title: "<OffthreadVideo /> while rendering", - relativePath: - "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx", - compId: - "articles-docs-miscellaneous-snippets-offthread-video-while-rendering", - crumb: "Snippets", + id: "lottie-comp", + title: "<Lottie>", + relativePath: "docs/lottie/lottie-comp.mdx", + compId: "articles-docs-lottie-lottie-comp", + crumb: "@remotion/lottie", }, { - id: "miscellaneous/snippets/player-in-iframe", - title: "Embedding a <Player> into an <iframe>", - relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx", - compId: "articles-docs-miscellaneous-snippets-player-in-iframe", - crumb: "Snippets", + id: "lottie-lottiefiles", + title: "Finding Lottie files to use", + relativePath: "docs/lottie/lottie-lottiefiles.mdx", + compId: "articles-docs-lottie-lottie-lottiefiles", + crumb: "Resources", }, { - id: "miscellaneous/snippets/combine-compositions", - title: "How do I combine compositions?", - relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx", - compId: "articles-docs-miscellaneous-snippets-combine-compositions", - crumb: "FAQ", + id: "lottie-remote", + title: "Loading Lottie animations from a URL", + relativePath: "docs/lottie/lottie-remote.mdx", + compId: "articles-docs-lottie-lottie-remote", + crumb: "@remotion/lottie", }, { - id: "miscellaneous/snippets/align-duration", - title: "How do I make the composition the same duration as my video?", - relativePath: "docs/miscellaneous/snippets/align-duration.mdx", - compId: "articles-docs-miscellaneous-snippets-align-duration", - crumb: "FAQ", + id: "lottie-staticfile", + title: "Loading Lottie animations from staticFile()", + relativePath: "docs/lottie/lottie-staticfile.mdx", + compId: "articles-docs-lottie-lottie-staticfile", + crumb: "@remotion/lottie", }, { - id: "miscellaneous/snippets/use-delay-render", - title: "useDelayRender()", - relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx", - compId: "articles-docs-miscellaneous-snippets-use-delay-render", - crumb: "Snippets", + id: "measure-spring", + title: "measureSpring()", + relativePath: "docs/measure-spring.mdx", + compId: "articles-docs-measure-spring", + crumb: "API", }, { - id: "miscellaneous/linux-single-process", - title: "Multiple cores on Linux", - relativePath: "docs/miscellaneous/linux-single-process.mdx", - compId: "articles-docs-miscellaneous-linux-single-process", - crumb: "Server-side rendering", + id: "measuring", + title: "Measuring DOM nodes", + relativePath: "docs/measuring.mdx", + compId: "articles-docs-measuring", + crumb: "How To", }, { - id: "miscellaneous/vercel-functions", - title: "Can I render videos using Vercel Serverless functions?", - relativePath: "docs/miscellaneous/vercel-functions.mdx", - compId: "articles-docs-miscellaneous-vercel-functions", - crumb: "FAQ", + id: "media-playback-error", + title: "Could not play video/audio with src", + relativePath: "docs/media-playback-error.mdx", + compId: "articles-docs-media-playback-error", + crumb: "Troubleshooting", }, { - id: "miscellaneous/chrome-headless-shell", - title: "Chrome Headless Shell", - relativePath: "docs/miscellaneous/chrome-headless-shell.mdx", - compId: "articles-docs-miscellaneous-chrome-headless-shell", - crumb: "FAQ", + id: "media-utils/index", + title: "@remotion/media-utils", + relativePath: "docs/media-utils/index.mdx", + compId: "articles-docs-media-utils-index", + crumb: null, }, { - id: "miscellaneous/render-in-browser", - title: "Can I render videos in the browser?", - relativePath: "docs/miscellaneous/render-in-browser.mdx", - compId: "articles-docs-miscellaneous-render-in-browser", + id: "miscellaneous/absolute-paths", + title: "Files with absolute paths", + relativePath: "docs/miscellaneous/absolute-paths.mdx", + compId: "articles-docs-miscellaneous-absolute-paths", crumb: "FAQ", }, { @@ -1549,13 +1700,6 @@ export const articles = [ compId: "articles-docs-miscellaneous-automatic-duration", crumb: "FAQ", }, - { - id: "typescript-aliases", - title: "TypeScript aliases", - relativePath: "docs/miscellaneous/ts-aliases.mdx", - compId: "articles-docs-miscellaneous-ts-aliases", - crumb: "How to", - }, { id: "miscellaneous/changing-temp-dir", title: "Changing the temporary directory", @@ -1564,17 +1708,10 @@ export const articles = [ crumb: "Advanced configuration", }, { - id: "embed-studio", - title: "Can I embed the Remotion Studio?", - relativePath: "docs/miscellaneous/embed-remotion-studio.mdx", - compId: "articles-docs-miscellaneous-embed-remotion-studio", - crumb: "FAQ", - }, - { - id: "miscellaneous/render-on-edge", - title: "Can I render videos on the edge?", - relativePath: "docs/miscellaneous/render-on-edge.mdx", - compId: "articles-docs-miscellaneous-render-on-edge", + id: "miscellaneous/chrome-headless-shell", + title: "Chrome Headless Shell", + relativePath: "docs/miscellaneous/chrome-headless-shell.mdx", + compId: "articles-docs-miscellaneous-chrome-headless-shell", crumb: "FAQ", }, { @@ -1591,6 +1728,13 @@ export const articles = [ compId: "articles-docs-miscellaneous-cloud-gpu", crumb: "FAQ", }, + { + id: "embed-studio", + title: "Can I embed the Remotion Studio?", + relativePath: "docs/miscellaneous/embed-remotion-studio.mdx", + compId: "articles-docs-miscellaneous-embed-remotion-studio", + crumb: "FAQ", + }, { id: "miscellaneous/linux-dependencies", title: "Linux Dependencies", @@ -1599,11 +1743,11 @@ export const articles = [ crumb: "FAQ", }, { - id: "miscellaneous/video-formats", - title: "Which video formats does Remotion support?", - relativePath: "docs/miscellaneous/video-formats.mdx", - compId: "articles-docs-miscellaneous-video-formats", - crumb: "FAQ", + id: "miscellaneous/linux-single-process", + title: "Multiple cores on Linux", + relativePath: "docs/miscellaneous/linux-single-process.mdx", + compId: "articles-docs-miscellaneous-linux-single-process", + crumb: "Server-side rendering", }, { id: "miscellaneous/live-streaming", @@ -1613,333 +1757,181 @@ export const articles = [ crumb: "FAQ", }, { - id: "miscellaneous/absolute-paths", - title: "Files with absolute paths", - relativePath: "docs/miscellaneous/absolute-paths.mdx", - compId: "articles-docs-miscellaneous-absolute-paths", + id: "miscellaneous/render-in-browser", + title: "Can I render videos in the browser?", + relativePath: "docs/miscellaneous/render-in-browser.mdx", + compId: "articles-docs-miscellaneous-render-in-browser", crumb: "FAQ", }, { - id: "use-audio-data", - title: "useAudioData()", - relativePath: "docs/use-audio-data.mdx", - compId: "articles-docs-use-audio-data", - crumb: "@remotion/media-utils", - }, - { - id: "calculate-metadata", - title: "calculateMetadata()", - relativePath: "docs/calculate-metadata.mdx", - compId: "articles-docs-calculate-metadata", - crumb: "API", - }, - { - id: "passing-props", - title: "Passing props to a composition", - relativePath: "docs/passing-props.mdx", - compId: "articles-docs-passing-props", - crumb: "How To", - }, - { - id: "standalone", - title: "Useful functions outside of Remotion", - relativePath: "docs/standalone.mdx", - compId: "articles-docs-standalone", - crumb: null, - }, - { - id: "ssr-node", - title: "Rendering using SSR APIs", - relativePath: "docs/ssr-node.mdx", - compId: "articles-docs-ssr-node", - crumb: "Server-side rendering", - }, - { - id: "tailwind", - title: "@remotion/tailwind", - relativePath: "docs/tailwind/overview.mdx", - compId: "articles-docs-tailwind-overview", - crumb: null, - }, - { - id: "tailwind/enable-tailwind", - title: "enableTailwind()", - relativePath: "docs/tailwind/enable-tailwind.mdx", - compId: "articles-docs-tailwind-enable-tailwind", - crumb: "@remotion/tailwind", - }, - { - id: "upgrading", - title: "Upgrading Remotion", - relativePath: "docs/upgrading.mdx", - compId: "articles-docs-upgrading", - crumb: "upgrade remotion", - }, - { - id: "getregions", - title: "getRegions()", - relativePath: "docs/cloudrun/getregions.mdx", - compId: "articles-docs-cloudrun-getregions", - crumb: "Cloud Run API", - }, - { - id: "cloudrun/multiple-buckets", - title: "Multiple buckets in Remotion Cloud Run", - relativePath: "docs/cloudrun/multiple-buckets.mdx", - compId: "articles-docs-cloudrun-multiple-buckets", - crumb: "@remotion/cloudrun", - }, - { - id: "instancecount", - title: "Instance Count", - relativePath: "docs/cloudrun/instancecount.mdx", - compId: "articles-docs-cloudrun-instancecount", - crumb: "Cloud Run", - }, - { - id: "cli", - title: "@remotion/cloudrun - CLI", - relativePath: "docs/cloudrun/cli.mdx", - compId: "articles-docs-cloudrun-cli", - crumb: null, - }, - { - id: "deployservice", - title: "deployService()", - relativePath: "docs/cloudrun/deployservice.mdx", - compId: "articles-docs-cloudrun-deployservice", - crumb: "Cloud Run API", - }, - { - id: "getsites", - title: "getSites()", - relativePath: "docs/cloudrun/getsites.mdx", - compId: "articles-docs-cloudrun-getsites", - crumb: "Cloud Run API", - }, - { - id: "deleteservice", - title: "deleteService()", - relativePath: "docs/cloudrun/deleteservice.mdx", - compId: "articles-docs-cloudrun-deleteservice", - crumb: "Cloud Run API", - }, - { - id: "generate-env", - title: "Generate .env File", - relativePath: "docs/cloudrun/generate-env.mdx", - compId: "articles-docs-cloudrun-generate-env", - crumb: "Cloud Run", - }, - { - id: "cloudrun/permissions", - title: "Permissions", - relativePath: "docs/cloudrun/permissions.mdx", - compId: "articles-docs-cloudrun-permissions", - crumb: "Cloud Run", - }, - { - id: "renderstilloncloudrun", - title: "renderStillOnCloudrun()", - relativePath: "docs/cloudrun/renderstilloncloudrun.mdx", - compId: "articles-docs-cloudrun-renderstilloncloudrun", - crumb: "Cloud Run API", - }, - { - id: "getorcreatebucket", - title: "getOrCreateBucket()", - relativePath: "docs/cloudrun/getorcreatebucket.mdx", - compId: "articles-docs-cloudrun-getorcreatebucket", - crumb: "Cloud Run API", - }, - { - id: "testpermissions", - title: "testPermissions()", - relativePath: "docs/cloudrun/testpermissions.mdx", - compId: "articles-docs-cloudrun-testpermissions", - crumb: "Cloud Run API", - }, - { - id: "cloudrun/light-client", - title: "Light client", - relativePath: "docs/cloudrun/light-client.mdx", - compId: "articles-docs-cloudrun-light-client", - crumb: "Cloud Run", - }, - { - id: "services", - title: "npx remotion cloudrun services", - relativePath: "docs/cloudrun/cli/services.mdx", - compId: "articles-docs-cloudrun-cli-services", - crumb: "Cloud Run CLI Reference", - }, - { - id: "permissions", - title: "npx remotion cloudrun permissions", - relativePath: "docs/cloudrun/cli/permissions.mdx", - compId: "articles-docs-cloudrun-cli-permissions", - crumb: "Cloud Run CLI Reference", - }, - { - id: "sites", - title: "npx remotion cloudrun sites", - relativePath: "docs/cloudrun/cli/sites.mdx", - compId: "articles-docs-cloudrun-cli-sites", - crumb: "Cloud Run CLI Reference", + id: "miscellaneous/render-on-edge", + title: "Can I render videos on the edge?", + relativePath: "docs/miscellaneous/render-on-edge.mdx", + compId: "articles-docs-miscellaneous-render-on-edge", + crumb: "FAQ", }, { - id: "render", - title: "npx remotion cloudrun render", - relativePath: "docs/cloudrun/cli/render.mdx", - compId: "articles-docs-cloudrun-cli-render", - crumb: "Cloud Run CLI Reference", + id: "miscellaneous/snippets/accelerated-video", + title: "Change the speed of a video over time", + relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx", + compId: "articles-docs-miscellaneous-snippets-accelerated-video", + crumb: "Snippets", }, { - id: "regions", - title: "npx remotion cloudrun regions", - relativePath: "docs/cloudrun/cli/regions.mdx", - compId: "articles-docs-cloudrun-cli-regions", - crumb: "Cloud Run CLI Reference", + id: "miscellaneous/snippets/align-duration", + title: "How do I make the composition the same duration as my video?", + relativePath: "docs/miscellaneous/snippets/align-duration.mdx", + compId: "articles-docs-miscellaneous-snippets-align-duration", + crumb: "FAQ", }, { - id: "still", - title: "npx remotion cloudrun still", - relativePath: "docs/cloudrun/cli/still.mdx", - compId: "articles-docs-cloudrun-cli-still", - crumb: "Cloud Run CLI Reference", + id: "miscellaneous/snippets/combine-compositions", + title: "How do I combine compositions?", + relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx", + compId: "articles-docs-miscellaneous-snippets-combine-compositions", + crumb: "FAQ", }, { - id: "upgrading", - title: "Upgrading Cloud Run", - relativePath: "docs/cloudrun/upgrading.mdx", - compId: "articles-docs-cloudrun-upgrading", - crumb: null, + id: "miscellaneous/snippets/fps-converter", + title: "FPS converter", + relativePath: "docs/miscellaneous/snippets/fps-converter.mdx", + compId: "articles-docs-miscellaneous-snippets-fps-converter", + crumb: "Snippets", }, { - id: "region-selection", - title: "Region selection", - relativePath: "docs/cloudrun/region-selection.mdx", - compId: "articles-docs-cloudrun-region-selection", - crumb: "Cloud Run", + id: "hls", + title: "HLS support (HTTP Live Streaming)", + relativePath: "docs/miscellaneous/snippets/hls.mdx", + compId: "articles-docs-miscellaneous-snippets-hls", + crumb: "Video", }, { - id: "getservices", - title: "getServices()", - relativePath: "docs/cloudrun/getservices.mdx", - compId: "articles-docs-cloudrun-getservices", - crumb: "Cloud Run API", + id: "miscellaneous/snippets/offthread-video-while-rendering", + title: "<OffthreadVideo /> while rendering", + relativePath: + "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx", + compId: + "articles-docs-miscellaneous-snippets-offthread-video-while-rendering", + crumb: "Snippets", }, { - id: "getserviceinfo", - title: "getServiceInfo()", - relativePath: "docs/cloudrun/getServiceinfo.mdx", - compId: "articles-docs-cloudrun-getServiceinfo", - crumb: "Cloud Run API", + id: "miscellaneous/snippets/player-in-iframe", + title: "Embedding a <Player> into an <iframe>", + relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx", + compId: "articles-docs-miscellaneous-snippets-player-in-iframe", + crumb: "Snippets", }, { - id: "checklist", - title: "Production Checklist", - relativePath: "docs/cloudrun/checklist.mdx", - compId: "articles-docs-cloudrun-checklist", - crumb: "Cloud Run", + id: "miscellaneous/snippets/use-delay-render", + title: "useDelayRender()", + relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx", + compId: "articles-docs-miscellaneous-snippets-use-delay-render", + crumb: "Snippets", }, { - id: "cloudrun/api", - title: "@remotion/cloudrun", - relativePath: "docs/cloudrun/api.mdx", - compId: "articles-docs-cloudrun-api", - crumb: "Render videos without servers on GCP", + id: "typescript-aliases", + title: "TypeScript aliases", + relativePath: "docs/miscellaneous/ts-aliases.mdx", + compId: "articles-docs-miscellaneous-ts-aliases", + crumb: "How to", }, { - id: "setup", - title: "Setup", - relativePath: "docs/cloudrun/setup.mdx", - compId: "articles-docs-cloudrun-setup", - crumb: "Cloud Run", + id: "miscellaneous/vercel-functions", + title: "Can I render videos using Vercel Serverless functions?", + relativePath: "docs/miscellaneous/vercel-functions.mdx", + compId: "articles-docs-miscellaneous-vercel-functions", + crumb: "FAQ", }, { - id: "rendermediaoncloudrun", - title: "renderMediaOnCloudrun()", - relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx", - compId: "articles-docs-cloudrun-rendermediaoncloudrun", - crumb: "Cloud Run API", + id: "miscellaneous/video-formats", + title: "Which video formats does Remotion support?", + relativePath: "docs/miscellaneous/video-formats.mdx", + compId: "articles-docs-miscellaneous-video-formats", + crumb: "FAQ", }, { - id: "speculateservicename", - title: "speculateServiceName()", - relativePath: "docs/cloudrun/speculateservicename.mdx", - compId: "articles-docs-cloudrun-speculateservicename", - crumb: "Cloud Run API", + id: "motion-blur/camera-motion-blur", + title: "<CameraMotionBlur>", + relativePath: "docs/motion-blur/camera-motion-blur.mdx", + compId: "articles-docs-motion-blur-camera-motion-blur", + crumb: "Realistic camera effect", }, { - id: "cloudrun/limits", - title: "Cloud Run Limits", - relativePath: "docs/cloudrun/limits.mdx", - compId: "articles-docs-cloudrun-limits", - crumb: "Cloud Run", + id: "motion-blur/index", + title: "@remotion/motion-blur", + relativePath: "docs/motion-blur/index.mdx", + compId: "articles-docs-motion-blur-index", + crumb: null, }, { - id: "deploysite", - title: "deploySite()", - relativePath: "docs/cloudrun/deploysite.mdx", - compId: "articles-docs-cloudrun-deploysite", - crumb: "Cloud Run API", + id: "motion-blur/motion-blur", + title: "<MotionBlur>", + relativePath: "docs/motion-blur/motion-blur.mdx", + compId: "articles-docs-motion-blur-motion-blur", + crumb: null, }, { - id: "uninstall", - title: "Uninstall Cloud Run", - relativePath: "docs/cloudrun/uninstall.mdx", - compId: "articles-docs-cloudrun-uninstall", + id: "motion-blur/trail", + title: "<Trail>", + relativePath: "docs/motion-blur/trail.mdx", + compId: "articles-docs-motion-blur-trail", + crumb: "Motion blur", + }, + { + id: "noise/index", + title: "@remotion/noise", + relativePath: "docs/noise/index.mdx", + compId: "articles-docs-noise-index", crumb: null, }, { - id: "deletesite", - title: "deleteSite()", - relativePath: "docs/cloudrun/deletesite.mdx", - compId: "articles-docs-cloudrun-deletesite", - crumb: "Cloud Run API", + id: "noise/noise-2d", + title: "noise2D()", + relativePath: "docs/noise/noise-2d.mdx", + compId: "articles-docs-noise-noise-2d", + crumb: "Make some", }, { - id: "reusability", - title: "Making components reusable", - relativePath: "docs/sequences.mdx", - compId: "articles-docs-sequences", - crumb: "The power of React", + id: "noise/noise-3d", + title: "noise3D()", + relativePath: "docs/noise/noise-3d.mdx", + compId: "articles-docs-noise-noise-3d", + crumb: "Make some", }, { - id: "three-canvas", - title: "<ThreeCanvas>", - relativePath: "docs/three-canvas.mdx", - compId: "articles-docs-three-canvas", - crumb: "@remotion/three", + id: "noise/noise-4d", + title: "noise4D()", + relativePath: "docs/noise/noise-4d.mdx", + compId: "articles-docs-noise-noise-4d", + crumb: "Make some", }, { - id: "version-mismatch", - title: "Version mismatch", - relativePath: "docs/version-mismatch.mdx", - compId: "articles-docs-version-mismatch", - crumb: "How to fix a", + id: "noise-visualization", + title: "Noise visualization", + relativePath: "docs/noise-visualization.mdx", + compId: "articles-docs-noise-visualization", + crumb: "Techniques", }, { - id: "iframe", - title: "<IFrame>", - relativePath: "docs/iframe.mdx", - compId: "articles-docs-iframe", - crumb: "API", + id: "non-seekable-media", + title: "Non-seekable media", + relativePath: "docs/non-seekable-media.mdx", + compId: "articles-docs-non-seekable-media", + crumb: "Troubleshooting", }, { - id: "cloudrun", - title: "@remotion/cloudrun", - relativePath: "docs/cloudrun.mdx", - compId: "articles-docs-cloudrun", - crumb: null, + id: '"null"', + title: "<Experimental.Null>", + relativePath: "docs/null.mdx", + compId: "articles-docs-null", + crumb: "Experimental API", }, { - id: "transitioning", - title: "Transitions", - relativePath: "docs/transitions.mdx", - compId: "articles-docs-transitions", - crumb: "Techniques", + id: "offthreadvideo", + title: "<OffthreadVideo>", + relativePath: "docs/offthreadvideo.mdx", + compId: "articles-docs-offthreadvideo", + crumb: "API", }, { id: "gl-options", @@ -1949,172 +1941,172 @@ export const articles = [ crumb: "Flags", }, { - id: "interpolate-colors", - title: "interpolateColors()", - relativePath: "docs/interpolate-colors.mdx", - compId: "articles-docs-interpolate-colors", - crumb: "API", + id: "overlay", + title: "Creating overlays", + relativePath: "docs/overlay.mdx", + compId: "articles-docs-overlay", + crumb: null, }, { - id: "using-randomness", - title: "Using randomness", - relativePath: "docs/using-randomness.mdx", - compId: "articles-docs-using-randomness", - crumb: "Roll the dice", + id: "webpack", + title: "Custom Webpack config", + relativePath: "docs/overwriting-webpack-config.mdx", + compId: "articles-docs-overwriting-webpack-config", + crumb: "How To", }, { - id: "5-0-migration", - title: "v5.0 Migration", - relativePath: "docs/5-0-migration.mdx", - compId: "articles-docs-5-0-migration", - crumb: "Version Upgrade", + id: "parameterized-rendering", + title: "Parameterized videos", + relativePath: "docs/parameterized-rendering.mdx", + compId: "articles-docs-parameterized-rendering", + crumb: "How To", }, { - id: "get-input-props", - title: "getInputProps()", - relativePath: "docs/get-input-props.mdx", - compId: "articles-docs-get-input-props", - crumb: "API", + id: "passing-props", + title: "Passing props to a composition", + relativePath: "docs/passing-props.mdx", + compId: "articles-docs-passing-props", + crumb: "How To", }, { - id: "measuring", - title: "Measuring DOM nodes", - relativePath: "docs/measuring.mdx", - compId: "articles-docs-measuring", - crumb: "How To", + id: "paths/evolve-path", + title: "evolvePath()", + relativePath: "docs/paths/evolve-path.mdx", + compId: "articles-docs-paths-evolve-path", + crumb: "@remotion/paths", }, { - id: "2-0-migration", - title: "v2.0 Migration", - relativePath: "docs/2-0-migration.mdx", - compId: "articles-docs-2-0-migration", - crumb: "Version Upgrade", + id: "paths/extend-viewbox", + title: "extendViewBox()", + relativePath: "docs/paths/extend-viewbox.mdx", + compId: "articles-docs-paths-extend-viewbox", + crumb: "@remotion/paths", }, { - id: "getting-started", - title: "Creating a new project", - relativePath: "docs/getting-started.mdx", - compId: "articles-docs-getting-started", - crumb: "Let's begin!", + id: "paths/get-bounding-box", + title: "getBoundingBox()", + relativePath: "docs/paths/get-bounding-box.mdx", + compId: "articles-docs-paths-get-bounding-box", + crumb: "@remotion/paths", }, { - id: "google-fonts", - title: "@remotion/google-fonts", - relativePath: "docs/google-fonts/index.mdx", - compId: "articles-docs-google-fonts-index", - crumb: null, + id: "paths/get-instruction-index-at-length", + title: "getInstructionIndexAtLength()", + relativePath: "docs/paths/get-instruction-index-at-length.mdx", + compId: "articles-docs-paths-get-instruction-index-at-length", + crumb: "@remotion/paths", }, { - id: "google-fonts/get-available-fonts", - title: "getAvailableFonts()", - relativePath: "docs/google-fonts/get-available-fonts.mdx", - compId: "articles-docs-google-fonts-get-available-fonts", - crumb: "@remotion/google-fonts", + id: "paths/get-length", + title: "getLength()", + relativePath: "docs/paths/get-length.mdx", + compId: "articles-docs-paths-get-length", + crumb: "@remotion/paths", }, { - id: "google-fonts/get-info", - title: "getInfo()", - relativePath: "docs/google-fonts/get-info.mdx", - compId: "articles-docs-google-fonts-get-info", - crumb: "@remotion/google-fonts", + id: "paths/get-parts", + title: "getParts()", + relativePath: "docs/paths/get-parts.mdx", + compId: "articles-docs-paths-get-parts", + crumb: "@remotion/paths", }, { - id: "google-fonts/load-font", - title: "loadFont()", - relativePath: "docs/google-fonts/load-font.mdx", - compId: "articles-docs-google-fonts-load-font", - crumb: "@remotion/google-fonts", + id: "paths/get-point-at-length", + title: "getPointAtLength()", + relativePath: "docs/paths/get-point-at-length.mdx", + compId: "articles-docs-paths-get-point-at-length", + crumb: "@remotion/paths", }, { - id: "media-utils/index", - title: "@remotion/media-utils", - relativePath: "docs/media-utils/index.mdx", - compId: "articles-docs-media-utils-index", - crumb: null, + id: "paths/get-subpaths", + title: "getSubpaths()", + relativePath: "docs/paths/get-subpaths.mdx", + compId: "articles-docs-paths-get-subpaths", + crumb: "@remotion/paths", }, { - id: "noise/noise-4d", - title: "noise4D()", - relativePath: "docs/noise/noise-4d.mdx", - compId: "articles-docs-noise-noise-4d", - crumb: "Make some", + id: "paths/get-tangent-at-length", + title: "getTangentAtLength()", + relativePath: "docs/paths/get-tangent-at-length.mdx", + compId: "articles-docs-paths-get-tangent-at-length", + crumb: "@remotion/paths", }, { - id: "noise/noise-3d", - title: "noise3D()", - relativePath: "docs/noise/noise-3d.mdx", - compId: "articles-docs-noise-noise-3d", - crumb: "Make some", + id: "paths/index", + title: "@remotion/paths", + relativePath: "docs/paths/index.mdx", + compId: "articles-docs-paths-index", + crumb: "SVG", }, { - id: "noise/index", - title: "@remotion/noise", - relativePath: "docs/noise/index.mdx", - compId: "articles-docs-noise-index", - crumb: null, + id: "paths/interpolate-path", + title: "interpolatePath()", + relativePath: "docs/paths/interpolate-path.mdx", + compId: "articles-docs-paths-interpolate-path", + crumb: "@remotion/paths", }, { - id: "noise/noise-2d", - title: "noise2D()", - relativePath: "docs/noise/noise-2d.mdx", - compId: "articles-docs-noise-noise-2d", - crumb: "Make some", + id: "paths/normalize-path", + title: "normalizePath()", + relativePath: "docs/paths/normalize-path.mdx", + compId: "articles-docs-paths-normalize-path", + crumb: "@remotion/paths", }, { - id: "security", - title: "Security Best Practices", - relativePath: "docs/security.mdx", - compId: "articles-docs-security", - crumb: "FAQ", + id: "paths/parse-path", + title: "parsePath()", + relativePath: "docs/paths/parse-path.mdx", + compId: "articles-docs-paths-parse-path", + crumb: "@remotion/paths", }, { - id: "delay-render", - title: "delayRender() and continueRender()", - relativePath: "docs/delay-render.mdx", - compId: "articles-docs-delay-render", - crumb: "How to", + id: "paths/reduce-instructions", + title: "reduceInstructions()", + relativePath: "docs/paths/reduce-instructions.mdx", + compId: "articles-docs-paths-reduce-instructions", + crumb: "@remotion/paths", }, { - id: "loop", - title: "<Loop>", - relativePath: "docs/loop.mdx", - compId: "articles-docs-loop", - crumb: "API", + id: "paths/reset-path", + title: "resetPath()", + relativePath: "docs/paths/reset-path.mdx", + compId: "articles-docs-paths-reset-path", + crumb: "@remotion/paths", }, { - id: "render", - title: "Render your video", - relativePath: "docs/render.mdx", - compId: "articles-docs-render", - crumb: "How To", + id: "paths/reverse-path", + title: "reversePath()", + relativePath: "docs/paths/reverse-path.mdx", + compId: "articles-docs-paths-reverse-path", + crumb: "@remotion/paths", }, { - id: "ssr-legacy", - title: "Server-Side Rendering (v1 and v2)", - relativePath: "docs/ssr-legacy.mdx", - compId: "articles-docs-ssr-legacy", - crumb: "Legacy docs", + id: "paths/scale-path", + title: "scalePath()", + relativePath: "docs/paths/scale-path.mdx", + compId: "articles-docs-paths-scale-path", + crumb: "@remotion/paths", }, { - id: "continue-render", - title: "continueRender()", - relativePath: "docs/continue-render.mdx", - compId: "articles-docs-continue-render", - crumb: "API", + id: "paths/serialize-instructions", + title: "serializeInstructions()", + relativePath: "docs/paths/serialize-instructions.mdx", + compId: "articles-docs-paths-serialize-instructions", + crumb: "@remotion/paths", }, { - id: "presigned-urls", - title: "Upload with a presigned URL", - relativePath: "docs/presigned-urls.mdx", - compId: "articles-docs-presigned-urls", - crumb: "Building video apps", + id: "paths/translate-path", + title: "translatePath()", + relativePath: "docs/paths/translate-path.mdx", + compId: "articles-docs-paths-translate-path", + crumb: "@remotion/paths", }, { - id: "tailwind-legacy", - title: "TailwindCSS v2 (Legacy)", - relativePath: "docs/tailwind-2.mdx", - compId: "articles-docs-tailwind-2", - crumb: "Legacy docs", + id: "paths/warp-path", + title: "warpPath()", + relativePath: "docs/paths/warp-path.mdx", + compId: "articles-docs-paths-warp-path", + crumb: "@remotion/paths", }, { id: "performance", @@ -2124,1436 +2116,1451 @@ export const articles = [ crumb: "Need for Speed", }, { - id: "getregions", - title: "getRegions()", - relativePath: "docs/lambda/getregions.mdx", - compId: "articles-docs-lambda-getregions", - crumb: "Lambda API", - }, - { - id: "lambda/feb-2023-incident", - title: "Upgrade your Lambda functions to prevent breakage", - relativePath: "docs/lambda/feb-2023-incident.mdx", - compId: "articles-docs-lambda-feb-2023-incident", - crumb: "DevOps advisory", + id: "player/api", + title: "<Player>", + relativePath: "docs/player/api.mdx", + compId: "articles-docs-player-api", + crumb: "@remotion/player", }, { - id: "lambda/multiple-buckets", - title: "Multiple buckets in Remotion Lambda", - relativePath: "docs/lambda/multiple-buckets.mdx", - compId: "articles-docs-lambda-multiple-buckets", - crumb: "@remotion/lambda", + id: "autoplay", + title: "Combatting autoplay issues", + relativePath: "docs/player/autoplay.mdx", + compId: "articles-docs-player-autoplay", + crumb: "@remotion/player", }, { - id: "cli", - title: "@remotion/lambda - CLI", - relativePath: "docs/lambda/cli.mdx", - compId: "articles-docs-lambda-cli", - crumb: null, + id: "best-practices", + title: "Player - Best practices", + relativePath: "docs/player/best-practices.mdx", + compId: "articles-docs-player-best-practices", + crumb: "@remotion/player", }, { - id: "getuserpolicy", - title: "getUserPolicy()", - relativePath: "docs/lambda/getuserpolicy.mdx", - compId: "articles-docs-lambda-getuserpolicy", - crumb: "Lambda API", + id: "buffer-state", + title: "The Player buffer state", + relativePath: "docs/player/buffer-state.mdx", + compId: "articles-docs-player-buffer-state", + crumb: "Best practices", }, { - id: "concurrency", - title: "Concurrency", - relativePath: "docs/lambda/concurrency.mdx", - compId: "articles-docs-lambda-concurrency", - crumb: "Lambda", + id: "current-time", + title: "Displaying the current time", + relativePath: "docs/player/current-time.mdx", + compId: "articles-docs-player-current-time", + crumb: "@remotion/player", }, { - id: "autodelete", - title: "Auto-delete renders", - relativePath: "docs/lambda/autodelete.mdx", - compId: "articles-docs-lambda-autodelete", - crumb: "Lambda API", + id: "player/index", + title: "@remotion/player", + relativePath: "docs/player/index.mdx", + compId: "articles-docs-player-index", + crumb: null, }, { - id: "optimizing-speed", - title: "Optimizing for speed", - relativePath: "docs/lambda/speed.mdx", - compId: "articles-docs-lambda-speed", - crumb: "Lambda", + id: "installation", + title: "Installation", + relativePath: "docs/player/installation.mdx", + compId: "articles-docs-player-installation", + crumb: "@remotion/player", }, { - id: "rendervideoonlambda", - title: "renderVideoOnLambda()", - relativePath: "docs/lambda/rendervideoonlambda.mdx", - compId: "articles-docs-lambda-rendervideoonlambda", - crumb: "Lambda API", + id: "examples", + title: "Examples for @remotion/player", + relativePath: "docs/player/player-examples.mdx", + compId: "articles-docs-player-player-examples", + crumb: "@remotion/player", }, { - id: "getsites", - title: "getSites()", - relativePath: "docs/lambda/getsites.mdx", - compId: "articles-docs-lambda-getsites", - crumb: "Lambda API", + id: "integration", + title: "Code sharing", + relativePath: "docs/player/player-integration.mdx", + compId: "articles-docs-player-player-integration", + crumb: "@remotion/player", }, { - id: "speculatefunctionname", - title: "speculateFunctionName()", - relativePath: "docs/lambda/speculateFunctionName.mdx", - compId: "articles-docs-lambda-speculateFunctionName", - crumb: "Lambda API", + id: "player/preloading", + title: "Preloading assets", + relativePath: "docs/player/preloading.mdx", + compId: "articles-docs-player-preloading", + crumb: "@remotion/player", }, { - id: "lambda/permissions", - title: "Permissions", - relativePath: "docs/lambda/permissions.mdx", - compId: "articles-docs-lambda-permissions", - crumb: "Lambda", + id: "player/premounting", + title: "Premounting", + relativePath: "docs/player/premounting.mdx", + compId: "articles-docs-player-premounting", + crumb: "@remotion/player", }, { - id: "deployfunction", - title: "deployFunction()", - relativePath: "docs/lambda/deployfunction.mdx", - compId: "articles-docs-lambda-deployfunction", - crumb: "Lambda API", + id: "scaling", + title: "Sizing", + relativePath: "docs/player/scaling.mdx", + compId: "articles-docs-player-scaling", + crumb: "@remotion/player", }, { - id: "feb-2022-outage", - title: "February 2022 Outage", - relativePath: "docs/lambda/feb-2022-outage.mdx", - compId: "articles-docs-lambda-feb-2022-outage", - crumb: null, + id: "thumbnail", + title: "<Thumbnail>", + relativePath: "docs/player/thumbnail.mdx", + compId: "articles-docs-player-thumbnail", + crumb: "@remotion/player", }, { - id: "getrenderprogress", - title: "getRenderProgress()", - relativePath: "docs/lambda/getrenderprogress.mdx", - compId: "articles-docs-lambda-getrenderprogress", - crumb: "Lambda API", + id: "prefetch", + title: "prefetch()", + relativePath: "docs/prefetch.mdx", + compId: "articles-docs-prefetch", + crumb: "API", }, { - id: "authentication", - title: "Authentication", - relativePath: "docs/lambda/authentication.mdx", - compId: "articles-docs-lambda-authentication", - crumb: "Lambda", + id: "preload-audio", + title: "preloadAudio()", + relativePath: "docs/preload/preload-audio.mdx", + compId: "articles-docs-preload-preload-audio", + crumb: "@remotion/preload", }, { - id: "lambda/python", - title: "Triggering renders from Python", - relativePath: "docs/lambda/python.mdx", - compId: "articles-docs-lambda-python", - crumb: "@remotion/lambda", + id: "preload-font", + title: "preloadFont()", + relativePath: "docs/preload/preload-font.mdx", + compId: "articles-docs-preload-preload-font", + crumb: "@remotion/preload", }, { - id: "lambda/webhooks", - title: "Webhooks", - relativePath: "docs/lambda/webhooks.mdx", - compId: "articles-docs-lambda-webhooks", - crumb: "Lambda", + id: "preload-image", + title: "preloadImage()", + relativePath: "docs/preload/preload-image.mdx", + compId: "articles-docs-preload-preload-image", + crumb: "@remotion/preload", }, { - id: "getorcreatebucket", - title: "getOrCreateBucket()", - relativePath: "docs/lambda/getorcreatebucket.mdx", - compId: "articles-docs-lambda-getorcreatebucket", - crumb: "Lambda API", + id: "preload-video", + title: "preloadVideo()", + relativePath: "docs/preload/preload-video.mdx", + compId: "articles-docs-preload-preload-video", + crumb: "@remotion/player", }, { - id: "custom-destination", - title: "Customizing Lambda output destination", - relativePath: "docs/lambda/custom-destination.mdx", - compId: "articles-docs-lambda-custom-destination", - crumb: "Lambda", + id: "preload", + title: "@remotion/preload", + relativePath: "docs/preload/preload.mdx", + compId: "articles-docs-preload-preload", + crumb: null, }, { - id: "deletefunction", - title: "deleteFunction()", - relativePath: "docs/lambda/deletefunction.mdx", - compId: "articles-docs-lambda-deletefunction", - crumb: "Lambda API", + id: "prereleases", + title: "Testing prereleases", + relativePath: "docs/prereleases.mdx", + compId: "articles-docs-prereleases", + crumb: "Only the brave", }, { - id: "estimateprice", - title: "estimatePrice()", - relativePath: "docs/lambda/estimateprice.mdx", - compId: "articles-docs-lambda-estimateprice", - crumb: "Lambda API", + id: "presigned-urls", + title: "Upload with a presigned URL", + relativePath: "docs/presigned-urls.mdx", + compId: "articles-docs-presigned-urls", + crumb: "Building video apps", }, { - id: "getrolepolicy", - title: "getRolePolicy()", - relativePath: "docs/lambda/getrolepolicy.mdx", - compId: "articles-docs-lambda-getrolepolicy", - crumb: "Lambda API", + id: "preview", + title: "Preview your video", + relativePath: "docs/preview.mdx", + compId: "articles-docs-preview", + crumb: "Timeline basics", }, { - id: "lambda/light-client", - title: "Light client", - relativePath: "docs/lambda/light-client.mdx", - compId: "articles-docs-lambda-light-client", - crumb: "Lambda", + id: "quality", + title: "Quality Guide", + relativePath: "docs/quality.mdx", + compId: "articles-docs-quality", + crumb: "Make it look good", }, { - id: "debug", - title: "Debugging failed Lambda renders", - relativePath: "docs/lambda/troubleshooting/debug.mdx", - compId: "articles-docs-lambda-troubleshooting-debug", - crumb: "Lambda", + id: "random", + title: "random()", + relativePath: "docs/random.mdx", + compId: "articles-docs-random", + crumb: "API", }, { - id: "unrecognizedclientexception", - title: "UnrecognizedClientException", - relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx", - compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception", - crumb: "Lambda Troubleshooting", + id: "react-18", + title: "Upgrade to React 18", + relativePath: "docs/react-18.mdx", + compId: "articles-docs-react-18", + crumb: "The new and shiny", }, { - id: "bucket-disallows-acl", - title: "The bucket does not allow ACLs", - relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx", - compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl", - crumb: "Lambda Troubleshooting", + id: "react-native", + title: "React Native", + relativePath: "docs/react-native.mdx", + compId: "articles-docs-react-native", + crumb: null, }, { - id: "permissions", - title: "AWS Permissions Troubleshooting", - relativePath: "docs/lambda/troubleshooting/permissions.mdx", - compId: "articles-docs-lambda-troubleshooting-permissions", - crumb: "Lambda Troubleshooting", + id: "recorder/before-you-buy", + title: "Before you buy", + relativePath: "docs/recorder/before-you-buy.mdx", + compId: "articles-docs-recorder-before-you-buy", + crumb: "Recorder", }, { - id: "lambda/troubleshooting/security-token", - title: "'\"The security token included in the request is invalid\"'", - relativePath: "docs/lambda/troubleshooting/security-token.mdx", - compId: "articles-docs-lambda-troubleshooting-security-token", - crumb: "Lambda", + id: "recorder/buy", + title: "Buy", + relativePath: "docs/recorder/buy.mdx", + compId: "articles-docs-recorder-buy", + crumb: "Recorder", }, { - id: "rate-limit", - title: "AWS Rate Limit Troubleshooting", - relativePath: "docs/lambda/troubleshooting/rate-limit.mdx", - compId: "articles-docs-lambda-troubleshooting-rate-limit", - crumb: "Lambda Troubleshooting", + id: "recorder/captions", + title: "Generate captions", + relativePath: "docs/recorder/captions.mdx", + compId: "articles-docs-recorder-captions", + crumb: "Recorder", }, { - id: "policies", - title: "npx remotion lambda policies", - relativePath: "docs/lambda/cli/policies.mdx", - compId: "articles-docs-lambda-cli-policies", - crumb: "Lambda CLI Reference", + id: "recorder/create", + title: "Create a new video", + relativePath: "docs/recorder/create.mdx", + compId: "articles-docs-recorder-create", + crumb: "Recorder", }, { - id: "quotas", - title: "npx remotion lambda quotas", - relativePath: "docs/lambda/cli/quotas.mdx", - compId: "articles-docs-lambda-cli-quotas", - crumb: "Lambda CLI Reference", + id: "recorder/editing/b-roll", + title: "B-Roll", + relativePath: "docs/recorder/editing/b-roll.mdx", + compId: "articles-docs-recorder-editing-b-roll", + crumb: "Recorder", }, { - id: "functions", - title: "npx remotion lambda functions", - relativePath: "docs/lambda/cli/functions.mdx", - compId: "articles-docs-lambda-cli-functions", - crumb: "Lambda CLI Reference", + id: "recorder/editing/captions", + title: "Edit captions", + relativePath: "docs/recorder/editing/captions.mdx", + compId: "articles-docs-recorder-editing-captions", + crumb: "Recorder", + }, + { + id: "recorder/editing/chapters", + title: "Chapters", + relativePath: "docs/recorder/editing/chapters.mdx", + compId: "articles-docs-recorder-editing-chapters", + crumb: "Recorder", }, { - id: "sites", - title: "npx remotion lambda sites", - relativePath: "docs/lambda/cli/sites.mdx", - compId: "articles-docs-lambda-cli-sites", - crumb: "Lambda CLI Reference", + id: "recorder/editing/cutting-clips", + title: "Cutting clips", + relativePath: "docs/recorder/editing/cutting-clips.mdx", + compId: "articles-docs-recorder-editing-cutting-clips", + crumb: "Recorder", }, { - id: "render", - title: "npx remotion lambda render", - relativePath: "docs/lambda/cli/render.mdx", - compId: "articles-docs-lambda-cli-render", - crumb: "Lambda CLI Reference", + id: "recorder/editing/editing", + title: "Start editing", + relativePath: "docs/recorder/editing/editing.mdx", + compId: "articles-docs-recorder-editing-editing", + crumb: "Recorder", }, { - id: "regions", - title: "npx remotion lambda regions", - relativePath: "docs/lambda/cli/regions.mdx", - compId: "articles-docs-lambda-cli-regions", - crumb: "Lambda CLI Reference", + id: "recorder/editing/endcard", + title: "Endcard", + relativePath: "docs/recorder/editing/endcard.mdx", + compId: "articles-docs-recorder-editing-endcard", + crumb: "Recorder", }, { - id: "lambda/cli/compositions", - title: "npx remotion lambda compositions", - relativePath: "docs/lambda/cli/compositions.mdx", - compId: "articles-docs-lambda-cli-compositions", - crumb: "Lambda CLI Reference", + id: "recorder/editing/layout", + title: "Layout", + relativePath: "docs/recorder/editing/layout.mdx", + compId: "articles-docs-recorder-editing-layout", + crumb: "Recorder", }, { - id: "still", - title: "npx remotion lambda still", - relativePath: "docs/lambda/cli/still.mdx", - compId: "articles-docs-lambda-cli-still", - crumb: "Lambda CLI Reference", + id: "recorder/editing/music", + title: "Adding music", + relativePath: "docs/recorder/editing/music.mdx", + compId: "articles-docs-recorder-editing-music", + crumb: "Recorder", }, { - id: "deleterender", - title: "deleteRender()", - relativePath: "docs/lambda/deleterender.mdx", - compId: "articles-docs-lambda-deleterender", - crumb: "Lambda API", + id: "recorder/editing/scenes", + title: "Scenes", + relativePath: "docs/recorder/editing/scenes.mdx", + compId: "articles-docs-recorder-editing-scenes", + crumb: "Recorder", }, { - id: "runtime", - title: "Runtime", - relativePath: "docs/lambda/runtime.mdx", - compId: "articles-docs-lambda-runtime", - crumb: "Lambda", + id: "recorder/editing/silence-removal", + title: "Silence removal", + relativePath: "docs/recorder/editing/silence-removal.mdx", + compId: "articles-docs-recorder-editing-silence-removal", + crumb: "Recorder", }, { - id: "upgrading", - title: "Upgrading Lambda", - relativePath: "docs/lambda/upgrading.mdx", - compId: "articles-docs-lambda-upgrading", - crumb: null, + id: "recorder/editing/transitions", + title: "Transitions", + relativePath: "docs/recorder/editing/transitions.mdx", + compId: "articles-docs-recorder-editing-transitions", + crumb: "Recorder", }, { - id: "region-selection", - title: "Region selection", - relativePath: "docs/lambda/region-selection.mdx", - compId: "articles-docs-lambda-region-selection", - crumb: "Lambda", + id: "recorder/exporting", + title: "Exporting a video", + relativePath: "docs/recorder/exporting.mdx", + compId: "articles-docs-recorder-exporting", + crumb: "Recorder", }, { - id: "renderstillonlambda", - title: "renderStillOnLambda()", - relativePath: "docs/lambda/renderstillonlambda.mdx", - compId: "articles-docs-lambda-renderstillonlambda", - crumb: "Lambda API", + id: "recorder/external-recordings", + title: "External recordings", + relativePath: "docs/recorder/external-recordings.mdx", + compId: "articles-docs-recorder-external-recordings", + crumb: "Recorder", }, { - id: "lambda/custom-layers", - title: "Custom Layers", - relativePath: "docs/lambda/custom-layers.mdx", - compId: "articles-docs-lambda-custom-layers", - crumb: "Lambda", + id: "recorder/gear", + title: "Our Gear", + relativePath: "docs/recorder/gear.mdx", + compId: "articles-docs-recorder-gear", + crumb: "Recorder", }, { - id: "changelog", - title: "Prerelease Changelog", - relativePath: "docs/lambda/changelog.mdx", - compId: "articles-docs-lambda-changelog", - crumb: null, + id: "recorder/index", + title: "Remotion Recorder", + relativePath: "docs/recorder/index.mdx", + compId: "articles-docs-recorder-index", + crumb: "Templates", }, { - id: "optimizing-cost", - title: "Optimizing for cost", - relativePath: "docs/lambda/cost.mdx", - compId: "articles-docs-lambda-cost", - crumb: "Lambda", + id: "recorder/lambda-rendering", + title: "Render on Lambda", + relativePath: "docs/recorder/lambda-rendering.mdx", + compId: "articles-docs-recorder-lambda-rendering", + crumb: "Recorder", }, { - id: "lambda/serverless-framework-integration", - title: "Using the Serverless Framework with Remotion Lambda", - relativePath: "docs/lambda/serverless-framework-integration.mdx", - compId: "articles-docs-lambda-serverless-framework-integration", - crumb: "@remotion/lambda", + id: "recorder/our-recorder", + title: "Our Recorder", + relativePath: "docs/recorder/our-recorder.mdx", + compId: "articles-docs-recorder-our-recorder", + crumb: "Recorder", }, { - id: "disk-size", - title: "Disk size", - relativePath: "docs/lambda/disk-size.mdx", - compId: "articles-docs-lambda-disk-size", - crumb: "Lambda", + id: "recorder/record", + title: "Record a scene", + relativePath: "docs/recorder/record.mdx", + compId: "articles-docs-recorder-record", + crumb: "Recorder", }, { - id: "lambda/without-iam/ec2", - title: "Authenticating Lambda with EC2", - relativePath: "docs/lambda/without-iam/ec2.mdx", - compId: "articles-docs-lambda-without-iam-ec2", - crumb: "@remotion/lambda", + id: "recorder/roadmap", + title: "Roadmap", + relativePath: "docs/recorder/roadmap.mdx", + compId: "articles-docs-recorder-roadmap", + crumb: "Recorder", }, { - id: "lambda/without-iam/index", - title: "Using Lambda with IAM roles", - relativePath: "docs/lambda/without-iam/index.mdx", - compId: "articles-docs-lambda-without-iam-index", - crumb: "Lambda without IAM", + id: "recorder/setup", + title: "Setup", + relativePath: "docs/recorder/setup.mdx", + compId: "articles-docs-recorder-setup", + crumb: "Recorder", }, { - id: "lambda/without-iam/example", - title: "Example setup without IAM user", - relativePath: "docs/lambda/without-iam/example.mdx", - compId: "articles-docs-lambda-without-iam-example", - crumb: "IAM Roles Example", + id: "recorder/source-control", + title: "Source Control", + relativePath: "docs/recorder/source-control.mdx", + compId: "articles-docs-recorder-source-control", + crumb: "Recorder", }, { - id: "downloadvideo", - title: "downloadVideo()", - relativePath: "docs/lambda/downloadvideo.mdx", - compId: "articles-docs-lambda-downloadvideo", - crumb: "Lambda API", + id: "recorder/support", + title: "Get Help", + relativePath: "docs/recorder/support.mdx", + compId: "articles-docs-recorder-support", + crumb: "Recorder", }, { - id: "getawsclient", - title: "getAwsClient()", - relativePath: "docs/lambda/getawsclient.mdx", - compId: "articles-docs-lambda-getawsclient", - crumb: "Lambda API", + id: "recorder/troubleshooting/cannot-read-properties-of-undefined", + title: "Cannot read properties of undefined (reading 'decode')", + relativePath: + "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx", + compId: + "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined", + crumb: "Recorder", }, { - id: "lambda/php", - title: "Triggering renders from PHP", - relativePath: "docs/lambda/php.mdx", - compId: "articles-docs-lambda-php", - crumb: "@remotion/lambda", + id: "recorder/troubleshooting/failed-to-execute-get-video-metadata", + title: "Failed to execute getVideoMetadata()", + relativePath: + "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx", + compId: + "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata", + crumb: "Recorder", }, { - id: "rendermediaonlambda", - title: "renderMediaOnLambda()", - relativePath: "docs/lambda/rendermediaonlambda.mdx", - compId: "articles-docs-lambda-rendermediaonlambda", - crumb: "Lambda API", + id: "recorder/upgrading", + title: "Upgrading", + relativePath: "docs/recorder/upgrading.mdx", + compId: "articles-docs-recorder-upgrading", + crumb: "Recorder", }, { - id: "simulatepermissions", - title: "simulatePermissions()", - relativePath: "docs/lambda/simulatepermissions.mdx", - compId: "articles-docs-lambda-simulatepermissions", - crumb: "Lambda API", + id: "register-root", + title: "registerRoot()", + relativePath: "docs/register-root.mdx", + compId: "articles-docs-register-root", + crumb: "API", }, { - id: "checklist", - title: "Production Checklist", - relativePath: "docs/lambda/checklist.mdx", - compId: "articles-docs-lambda-checklist", - crumb: "Lambda", + id: "remotion", + title: "remotion", + relativePath: "docs/remotion.mdx", + compId: "articles-docs-remotion", + crumb: "Core API Reference", }, { - id: "how-lambda-works", - title: "How Remotion Lambda works", - relativePath: "docs/lambda/how-lambda-works.mdx", - compId: "articles-docs-lambda-how-lambda-works", - crumb: "Lambda", + id: "render-all", + title: "Render all compositions", + relativePath: "docs/render-all.mdx", + compId: "articles-docs-render-all", + crumb: "Techniques", }, { - id: "presignurl", - title: "presignUrl()", - relativePath: "docs/lambda/presignurl.mdx", - compId: "articles-docs-lambda-presignurl", - crumb: "Lambda API", + id: "render-as-gif", + title: "Rendering GIFs", + relativePath: "docs/render-as-gif.mdx", + compId: "articles-docs-render-as-gif", + crumb: "Techniques", }, { - id: "lambda/faq", - title: "FAQ", - relativePath: "docs/lambda/faq.mdx", - compId: "articles-docs-lambda-faq", - crumb: "Lambda", + id: "render", + title: "Render your video", + relativePath: "docs/render.mdx", + compId: "articles-docs-render", + crumb: "How To", }, { - id: "lambda/api", - title: "@remotion/lambda", - relativePath: "docs/lambda/api.mdx", - compId: "articles-docs-lambda-api", - crumb: "Render videos without servers on AWS", + id: "ensure-browser", + title: "ensureBrowser()", + relativePath: "docs/renderer/ensure-browser.mdx", + compId: "articles-docs-renderer-ensure-browser", + crumb: "@remotion/renderer", }, { - id: "validatewebhooksignature", - title: "validateWebhookSignature()", - relativePath: "docs/lambda/validatewebhooksignature.mdx", - compId: "articles-docs-lambda-validatewebhooksignature", - crumb: "Lambda API", + id: "ensure-ffmpeg", + title: "ensureFfmpeg()", + relativePath: "docs/renderer/ensure-ffmpeg.mdx", + compId: "articles-docs-renderer-ensure-ffmpeg", + crumb: "@remotion/renderer", }, { - id: "getfunctioninfo", - title: "getFunctionInfo()", - relativePath: "docs/lambda/getfunctioninfo.mdx", - compId: "articles-docs-lambda-getfunctioninfo", - crumb: "Lambda API", + id: "ensure-ffprobe", + title: "ensureFfprobe()", + relativePath: "docs/renderer/ensure-ffprobe.mdx", + compId: "articles-docs-renderer-ensure-ffprobe", + crumb: "@remotion/renderer", }, { - id: "setup", - title: "Setup", - relativePath: "docs/lambda/setup.mdx", - compId: "articles-docs-lambda-setup", - crumb: "Lambda", + id: "extract-audio", + title: "extractAudio()", + relativePath: "docs/renderer/extract-audio.mdx", + compId: "articles-docs-renderer-extract-audio", + crumb: "@remotion/renderer", }, { - id: "getfunctions", - title: "getFunctions()", - relativePath: "docs/lambda/getfunctions.mdx", - compId: "articles-docs-lambda-getfunctions", - crumb: "Lambda API", + id: "get-can-extract-frames-fast", + title: "getCanExtractFramesFast()", + relativePath: "docs/renderer/get-can-extract-frames-fast.mdx", + compId: "articles-docs-renderer-get-can-extract-frames-fast", + crumb: "@remotion/renderer", }, { - id: "getcompositionsonlambda", - title: "getCompositionsOnLambda()", - relativePath: "docs/lambda/getcompositionsonlambda.mdx", - compId: "articles-docs-lambda-getcompositionsonlambda", - crumb: "Lambda API", + id: "get-compositions", + title: "getCompositions()", + relativePath: "docs/renderer/get-compositions.mdx", + compId: "articles-docs-renderer-get-compositions", + crumb: "@remotion/renderer", }, { - id: "downloadmedia", - title: "downloadMedia()", - relativePath: "docs/lambda/downloadmedia.mdx", - compId: "articles-docs-lambda-downloadmedia", - crumb: "Lambda API", + id: "get-silent-parts", + title: "getSilentParts()", + relativePath: "docs/renderer/get-silent-parts.mdx", + compId: "articles-docs-renderer-get-silent-parts", + crumb: "@remotion/renderer", }, { - id: "lambda/s3-public-access", - title: "Cannot create a S3 bucket using Remotion", - relativePath: "docs/lambda/s3-public-access.mdx", - compId: "articles-docs-lambda-s3-public-access", - crumb: "DevOps advisory", + id: "get-video-metadata", + title: "getVideoMetadata()", + relativePath: "docs/renderer/get-video-metadata.mdx", + compId: "articles-docs-renderer-get-video-metadata", + crumb: "@remotion/renderer", }, { - id: "lambda/limits", - title: "Lambda Limits", - relativePath: "docs/lambda/limits.mdx", - compId: "articles-docs-lambda-limits", - crumb: "Lambda", + id: "make-cancel-signal", + title: "makeCancelSignal()", + relativePath: "docs/renderer/make-cancel-signal.mdx", + compId: "articles-docs-renderer-make-cancel-signal", + crumb: "@remotion/renderer", }, { - id: "lambda/sqs", - title: "Using Lambda with SQS", - relativePath: "docs/lambda/sqs.mdx", - compId: "articles-docs-lambda-sqs", - crumb: "@remotion/lambda", + id: "open-browser", + title: "openBrowser()", + relativePath: "docs/renderer/open-browser.mdx", + compId: "articles-docs-renderer-open-browser", + crumb: "@remotion/renderer", }, { - id: "lambda/go", - title: "Triggering renders from Go", - relativePath: "docs/lambda/go.mdx", - compId: "articles-docs-lambda-go", - crumb: "@remotion/lambda", + id: "render-frames", + title: "renderFrames()", + relativePath: "docs/renderer/render-frames.mdx", + compId: "articles-docs-renderer-render-frames", + crumb: "@remotion/renderer", }, { - id: "deploysite", - title: "deploySite()", - relativePath: "docs/lambda/deploysite.mdx", - compId: "articles-docs-lambda-deploysite", - crumb: "Lambda API", + id: "render-media", + title: "renderMedia()", + relativePath: "docs/renderer/render-media.mdx", + compId: "articles-docs-renderer-render-media", + crumb: "@remotion/renderer", }, { - id: "uninstall", - title: "Uninstall Lambda", - relativePath: "docs/lambda/uninstall.mdx", - compId: "articles-docs-lambda-uninstall", - crumb: null, + id: "render-still", + title: "renderStill()", + relativePath: "docs/renderer/render-still.mdx", + compId: "articles-docs-renderer-render-still", + crumb: "@remotion/renderer", }, { - id: "lambda/bucket-naming", - title: "Bucket names in Remotion Lambda", - relativePath: "docs/lambda/bucket-naming.mdx", - compId: "articles-docs-lambda-bucket-naming", - crumb: "@remotion/lambda", + id: "select-composition", + title: "selectComposition()", + relativePath: "docs/renderer/select-composition.mdx", + compId: "articles-docs-renderer-select-composition", + crumb: "@remotion/renderer", }, { - id: "naming-convention", - title: "Function naming convention", - relativePath: "docs/lambda/naming-convention.mdx", - compId: "articles-docs-lambda-naming-convention", - crumb: "Lambda", + id: "stitch-frames-to-video", + title: "stitchFramesToVideo()", + relativePath: "docs/renderer/stitch-frames-to-video.mdx", + compId: "articles-docs-renderer-stitch-frames-to-video", + crumb: "@remotion/renderer", }, { - id: "lambda/insights", - title: "Enable Lambda Insights", - relativePath: "docs/lambda/insights.mdx", - compId: "articles-docs-lambda-insights", - crumb: "Lambda", + id: "renderer", + title: "@remotion/renderer", + relativePath: "docs/renderer.mdx", + compId: "articles-docs-renderer", + crumb: null, }, { - id: "deletesite", - title: "deleteSite()", - relativePath: "docs/lambda/deletesite.mdx", - compId: "articles-docs-lambda-deletesite", - crumb: "Lambda API", + id: "resources", + title: "List of resources", + relativePath: "docs/resources.mdx", + compId: "articles-docs-resources", + crumb: "Ecosystem", }, { - id: "compare/motion-canvas", - title: "How does Remotion compare to Motion Canvas?", - relativePath: "docs/compare/motion-canvas.mdx", - compId: "articles-docs-compare-motion-canvas", - crumb: "FAQ", + id: "rive/index", + title: "@remotion/rive", + relativePath: "docs/rive/index.mdx", + compId: "articles-docs-rive-index", + crumb: "Integrations", }, { - id: "api", - title: "API overview", - relativePath: "docs/api.mdx", - compId: "articles-docs-api", - crumb: null, + id: "rive/remotionrivecanvas", + title: "<RemotionRiveCanvas>", + relativePath: "docs/rive/remotionrivecanvas.mdx", + compId: "articles-docs-rive-remotionrivecanvas", + crumb: "@remotion/rive", }, { - id: "staticfile", - title: "staticFile()", - relativePath: "docs/staticfile.mdx", - compId: "articles-docs-staticfile", - crumb: "API", + id: "scaling", + title: "Output scaling", + relativePath: "docs/scaling.mdx", + compId: "articles-docs-scaling", + crumb: "How To", }, { - id: "lottie-staticfile", - title: "Loading Lottie animations from staticFile()", - relativePath: "docs/lottie/lottie-staticfile.mdx", - compId: "articles-docs-lottie-lottie-staticfile", - crumb: "@remotion/lottie", + id: "schemas", + title: "Defining a schema for your props", + relativePath: "docs/schemas.mdx", + compId: "articles-docs-schemas", + crumb: "How To", }, { - id: "lottie-index", - title: "@remotion/lottie", - relativePath: "docs/lottie/index.mdx", - compId: "articles-docs-lottie-index", - crumb: null, + id: "security", + title: "Security Best Practices", + relativePath: "docs/security.mdx", + compId: "articles-docs-security", + crumb: "FAQ", }, { - id: "getlottiemetadata", - title: "getLottieMetadata()", - relativePath: "docs/lottie/getlottiemetadata.mdx", - compId: "articles-docs-lottie-getlottiemetadata", - crumb: "@remotion/lottie", + id: "sequence", + title: "<Sequence>", + relativePath: "docs/sequence.mdx", + compId: "articles-docs-sequence", + crumb: "API", + }, + { + id: "reusability", + title: "Making components reusable", + relativePath: "docs/sequences.mdx", + compId: "articles-docs-sequences", + crumb: "The power of React", }, { - id: "lottie-remote", - title: "Loading Lottie animations from a URL", - relativePath: "docs/lottie/lottie-remote.mdx", - compId: "articles-docs-lottie-lottie-remote", - crumb: "@remotion/lottie", + id: "series", + title: "<Series>", + relativePath: "docs/series.mdx", + compId: "articles-docs-series", + crumb: "API", }, { - id: "lottie-comp", - title: "<Lottie>", - relativePath: "docs/lottie/lottie-comp.mdx", - compId: "articles-docs-lottie-lottie-comp", - crumb: "@remotion/lottie", + id: "shapes/circle", + title: "<Circle />", + relativePath: "docs/shapes/circle.mdx", + compId: "articles-docs-shapes-circle", + crumb: "@remotion/shapes", }, { - id: "lottie-lottiefiles", - title: "Finding Lottie files to use", - relativePath: "docs/lottie/lottie-lottiefiles.mdx", - compId: "articles-docs-lottie-lottie-lottiefiles", - crumb: "Resources", + id: "shapes/ellipse", + title: "<Ellipse />", + relativePath: "docs/shapes/ellipse.mdx", + compId: "articles-docs-shapes-ellipse", + crumb: "@remotion/shapes", }, { - id: "overlay", - title: "Creating overlays", - relativePath: "docs/overlay.mdx", - compId: "articles-docs-overlay", - crumb: null, + id: "shapes/index", + title: "@remotion/shapes", + relativePath: "docs/shapes/index.mdx", + compId: "articles-docs-shapes-index", + crumb: "SVG component library", }, { - id: "transparent-videos", - title: "Transparent videos", - relativePath: "docs/transparent-videos.mdx", - compId: "articles-docs-transparent-videos", - crumb: "Techniques", + id: "shapes/make-circle", + title: "makeCircle()", + relativePath: "docs/shapes/make-circle.mdx", + compId: "articles-docs-shapes-make-circle", + crumb: "@remotion/shapes", }, { - id: "recorder/create", - title: "Create a new video", - relativePath: "docs/recorder/create.mdx", - compId: "articles-docs-recorder-create", - crumb: "Recorder", + id: "shapes/make-ellipse", + title: "makeEllipse()", + relativePath: "docs/shapes/make-ellipse.mdx", + compId: "articles-docs-shapes-make-ellipse", + crumb: "@remotion/shapes", }, { - id: "recorder/lambda-rendering", - title: "Render on Lambda", - relativePath: "docs/recorder/lambda-rendering.mdx", - compId: "articles-docs-recorder-lambda-rendering", - crumb: "Recorder", + id: "shapes/make-pie", + title: "makePie()", + relativePath: "docs/shapes/make-pie.mdx", + compId: "articles-docs-shapes-make-pie", + crumb: "@remotion/shapes", }, { - id: "recorder/exporting", - title: "Exporting a video", - relativePath: "docs/recorder/exporting.mdx", - compId: "articles-docs-recorder-exporting", - crumb: "Recorder", + id: "shapes/make-polygon", + title: "makePolygon()", + relativePath: "docs/shapes/make-polygon.mdx", + compId: "articles-docs-shapes-make-polygon", + crumb: "@remotion/shapes", }, { - id: "recorder/external-recordings", - title: "External recordings", - relativePath: "docs/recorder/external-recordings.mdx", - compId: "articles-docs-recorder-external-recordings", - crumb: "Recorder", + id: "shapes/make-rect", + title: "makeRect()", + relativePath: "docs/shapes/make-rect.mdx", + compId: "articles-docs-shapes-make-rect", + crumb: "@remotion/shapes", }, { - id: "recorder/captions", - title: "Generate captions", - relativePath: "docs/recorder/captions.mdx", - compId: "articles-docs-recorder-captions", - crumb: "Recorder", + id: "shapes/make-star", + title: "makeStar()", + relativePath: "docs/shapes/make-star.mdx", + compId: "articles-docs-shapes-make-star", + crumb: "@remotion/shapes", }, { - id: "recorder/index", - title: "Remotion Recorder", - relativePath: "docs/recorder/index.mdx", - compId: "articles-docs-recorder-index", - crumb: "Templates", + id: "shapes/make-triangle", + title: "makeTriangle()", + relativePath: "docs/shapes/make-triangle.mdx", + compId: "articles-docs-shapes-make-triangle", + crumb: "@remotion/shapes", }, { - id: "recorder/support", - title: "Get Help", - relativePath: "docs/recorder/support.mdx", - compId: "articles-docs-recorder-support", - crumb: "Recorder", + id: "shapes/pie", + title: "<Pie />", + relativePath: "docs/shapes/pie.mdx", + compId: "articles-docs-shapes-pie", + crumb: "@remotion/shapes", }, { - id: "recorder/source-control", - title: "Source Control", - relativePath: "docs/recorder/source-control.mdx", - compId: "articles-docs-recorder-source-control", - crumb: "Recorder", + id: "shapes/polygon", + title: "<Polygon />", + relativePath: "docs/shapes/polygon.mdx", + compId: "articles-docs-shapes-polygon", + crumb: "@remotion/shapes", }, { - id: "recorder/buy", - title: "Buy", - relativePath: "docs/recorder/buy.mdx", - compId: "articles-docs-recorder-buy", - crumb: "Recorder", + id: "shapes/rect", + title: "<Rect />", + relativePath: "docs/shapes/rect.mdx", + compId: "articles-docs-shapes-rect", + crumb: "@remotion/shapes", }, { - id: "recorder/troubleshooting/failed-to-execute-get-video-metadata", - title: "Failed to execute getVideoMetadata()", - relativePath: - "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx", - compId: - "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata", - crumb: "Recorder", + id: "shapes/star", + title: "<Star />", + relativePath: "docs/shapes/star.mdx", + compId: "articles-docs-shapes-star", + crumb: "@remotion/shapes", }, { - id: "recorder/troubleshooting/cannot-read-properties-of-undefined", - title: "Cannot read properties of undefined (reading 'decode')", - relativePath: - "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx", - compId: - "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined", - crumb: "Recorder", + id: "shapes/triangle", + title: "<Triangle />", + relativePath: "docs/shapes/triangle.mdx", + compId: "articles-docs-shapes-triangle", + crumb: "@remotion/shapes", }, { - id: "recorder/record", - title: "Record a scene", - relativePath: "docs/recorder/record.mdx", - compId: "articles-docs-recorder-record", - crumb: "Recorder", + id: "skia/enable-skia", + title: "enableSkia()", + relativePath: "docs/skia/enable-skia.mdx", + compId: "articles-docs-skia-enable-skia", + crumb: "@remotion/skia", }, { - id: "recorder/gear", - title: "Our Gear", - relativePath: "docs/recorder/gear.mdx", - compId: "articles-docs-recorder-gear", - crumb: "Recorder", + id: "skia/skia-canvas", + title: "<SkiaCanvas />", + relativePath: "docs/skia/skia-canvas.mdx", + compId: "articles-docs-skia-skia-canvas", + crumb: "@remotion/skia", }, { - id: "recorder/roadmap", - title: "Roadmap", - relativePath: "docs/recorder/roadmap.mdx", - compId: "articles-docs-recorder-roadmap", - crumb: "Recorder", + id: "skia", + title: "@remotion/skia", + relativePath: "docs/skia/skia.mdx", + compId: "articles-docs-skia-skia", + crumb: null, }, { - id: "recorder/upgrading", - title: "Upgrading", - relativePath: "docs/recorder/upgrading.mdx", - compId: "articles-docs-recorder-upgrading", - crumb: "Recorder", + id: "slow-method-to-extract-frame", + title: "Slow method to extract frame", + relativePath: "docs/slow-method-to-extract-frame.mdx", + compId: "articles-docs-slow-method-to-extract-frame", + crumb: "Troubleshooting", }, { - id: "recorder/editing/scenes", - title: "Scenes", - relativePath: "docs/recorder/editing/scenes.mdx", - compId: "articles-docs-recorder-editing-scenes", - crumb: "Recorder", + id: "spline", + title: "Import from Spline", + relativePath: "docs/spline.mdx", + compId: "articles-docs-spline", + crumb: "Integrations", }, { - id: "recorder/editing/b-roll", - title: "B-Roll", - relativePath: "docs/recorder/editing/b-roll.mdx", - compId: "articles-docs-recorder-editing-b-roll", - crumb: "Recorder", + id: "spring", + title: "spring()", + relativePath: "docs/spring.mdx", + compId: "articles-docs-spring", + crumb: "API", }, { - id: "recorder/editing/music", - title: "Adding music", - relativePath: "docs/recorder/editing/music.mdx", - compId: "articles-docs-recorder-editing-music", - crumb: "Recorder", + id: "ssr-legacy", + title: "Server-Side Rendering (v1 and v2)", + relativePath: "docs/ssr-legacy.mdx", + compId: "articles-docs-ssr-legacy", + crumb: "Legacy docs", }, { - id: "recorder/editing/chapters", - title: "Chapters", - relativePath: "docs/recorder/editing/chapters.mdx", - compId: "articles-docs-recorder-editing-chapters", - crumb: "Recorder", + id: "ssr-node", + title: "Rendering using SSR APIs", + relativePath: "docs/ssr-node.mdx", + compId: "articles-docs-ssr-node", + crumb: "Server-side rendering", }, { - id: "recorder/editing/captions", - title: "Edit captions", - relativePath: "docs/recorder/editing/captions.mdx", - compId: "articles-docs-recorder-editing-captions", - crumb: "Recorder", + id: "ssr", + title: "Server-Side Rendering", + relativePath: "docs/ssr.mdx", + compId: "articles-docs-ssr", + crumb: "The power of", }, { - id: "recorder/editing/cutting-clips", - title: "Cutting clips", - relativePath: "docs/recorder/editing/cutting-clips.mdx", - compId: "articles-docs-recorder-editing-cutting-clips", - crumb: "Recorder", + id: "standalone", + title: "Useful functions outside of Remotion", + relativePath: "docs/standalone.mdx", + compId: "articles-docs-standalone", + crumb: null, }, { - id: "recorder/editing/editing", - title: "Start editing", - relativePath: "docs/recorder/editing/editing.mdx", - compId: "articles-docs-recorder-editing-editing", - crumb: "Recorder", + id: "staticfile-relative-paths", + title: "staticFile() does not support relative paths", + relativePath: "docs/static-file-relative-paths.mdx", + compId: "articles-docs-static-file-relative-paths", + crumb: "Troubleshooting", }, { - id: "recorder/editing/endcard", - title: "Endcard", - relativePath: "docs/recorder/editing/endcard.mdx", - compId: "articles-docs-recorder-editing-endcard", - crumb: "Recorder", + id: "staticfile-remote-urls", + title: "staticFile() does not support remote URLs", + relativePath: "docs/static-file-remote-urls.mdx", + compId: "articles-docs-static-file-remote-urls", + crumb: "Troubleshooting", }, { - id: "recorder/editing/transitions", - title: "Transitions", - relativePath: "docs/recorder/editing/transitions.mdx", - compId: "articles-docs-recorder-editing-transitions", - crumb: "Recorder", + id: "staticfile", + title: "staticFile()", + relativePath: "docs/staticfile.mdx", + compId: "articles-docs-staticfile", + crumb: "API", }, { - id: "recorder/editing/silence-removal", - title: "Silence removal", - relativePath: "docs/recorder/editing/silence-removal.mdx", - compId: "articles-docs-recorder-editing-silence-removal", - crumb: "Recorder", + id: "still", + title: "<Still>", + relativePath: "docs/still.mdx", + compId: "articles-docs-still", + crumb: "API", }, { - id: "recorder/editing/layout", - title: "Layout", - relativePath: "docs/recorder/editing/layout.mdx", - compId: "articles-docs-recorder-editing-layout", - crumb: "Recorder", + id: "stills", + title: "Still images", + relativePath: "docs/stills.mdx", + compId: "articles-docs-stills", + crumb: "How to generate", }, { - id: "recorder/our-recorder", - title: "Our Recorder", - relativePath: "docs/recorder/our-recorder.mdx", - compId: "articles-docs-recorder-our-recorder", - crumb: "Recorder", + id: "studio/api", + title: "@remotion/studio", + relativePath: "docs/studio/api.mdx", + compId: "articles-docs-studio-api", + crumb: null, }, { - id: "recorder/before-you-buy", - title: "Before you buy", - relativePath: "docs/recorder/before-you-buy.mdx", - compId: "articles-docs-recorder-before-you-buy", - crumb: "Recorder", + id: "studio/delete-static-file", + title: "deleteStaticFile()", + relativePath: "docs/studio/delete-static-file.mdx", + compId: "articles-docs-studio-delete-static-file", + crumb: "@remotion/studio", }, { - id: "recorder/setup", - title: "Setup", - relativePath: "docs/recorder/setup.mdx", - compId: "articles-docs-recorder-setup", - crumb: "Recorder", + id: "studio/deploy-server", + title: "Deploy the Remotion Studio on a VPS", + relativePath: "docs/studio/deploy-server.mdx", + compId: "articles-docs-studio-deploy-server", + crumb: "Remotion Studio", }, { - id: "fonts-api", - title: "@remotion/fonts", - relativePath: "docs/fonts-api/index.mdx", - compId: "articles-docs-fonts-api-index", - crumb: null, + id: "studio/deploy-static", + title: "Deploy Remotion Studio as static site", + relativePath: "docs/studio/deploy-static.mdx", + compId: "articles-docs-studio-deploy-static", + crumb: "Remotion Studio", }, { - id: "fonts-api/load-font", - title: "loadFont()", - relativePath: "docs/fonts-api/load-font.mdx", - compId: "articles-docs-fonts-api-load-font", - crumb: "@remotion/fonts", + id: "studio/focus-default-props-path", + title: "focusDefaultPropsPath()", + relativePath: "docs/studio/focus-default-props-path.mdx", + compId: "articles-docs-studio-focus-default-props-path", + crumb: "@remotion/studio", }, { - id: "authoring-packages", - title: "Authoring a Remotion library", - relativePath: "docs/creating-a-library.mdx", - compId: "articles-docs-creating-a-library", - crumb: "How to", + id: "studio/get-static-files", + title: "getStaticFiles()", + relativePath: "docs/studio/get-static-files.mdx", + compId: "articles-docs-studio-get-static-files", + crumb: "@remotion/studio", }, { - id: "use-buffer-state", - title: "useBufferState()", - relativePath: "docs/use-buffer-state.mdx", - compId: "articles-docs-use-buffer-state", - crumb: "API", + id: "studio/quick-switcher", + title: "Quick Switcher", + relativePath: "docs/studio/quick-switcher.mdx", + compId: "articles-docs-studio-quick-switcher", + crumb: "Remotion Studio", }, { - id: "transforms", - title: "Transforms", - relativePath: "docs/transforms.mdx", - compId: "articles-docs-transforms", - crumb: "The fundamentals", + id: "studio/reevaluate-composition", + title: "reevaluateComposition()", + relativePath: "docs/studio/reevaluate-composition.mdx", + compId: "articles-docs-studio-reevaluate-composition", + crumb: "@remotion/studio", }, { - id: "use-current-frame", - title: "useCurrentFrame()", - relativePath: "docs/use-current-frame.mdx", - compId: "articles-docs-use-current-frame", - crumb: "API", + id: "studio/restart-studio", + title: "restartStudio()", + relativePath: "docs/studio/restart-studio.mdx", + compId: "articles-docs-studio-restart-studio", + crumb: "@remotion/studio", }, { - id: "enable-scss/overview", - title: "@remotion/enable-scss", - relativePath: "docs/enable-scss/overview.mdx", - compId: "articles-docs-enable-scss-overview", - crumb: null, + id: "studio/save-default-props", + title: "saveDefaultProps()", + relativePath: "docs/studio/save-default-props.mdx", + compId: "articles-docs-studio-save-default-props", + crumb: "@remotion/studio", }, { - id: "enable-scss/enable-scss", - title: "enableScss()", - relativePath: "docs/enable-scss/enable-scss.mdx", - compId: "articles-docs-enable-scss-enable-scss", - crumb: "@remotion/enable-scss", + id: "studio/shortcuts", + title: "Keyboard navigation", + relativePath: "docs/studio/shortcuts.mdx", + compId: "articles-docs-studio-shortcuts", + crumb: "Remotion Studio", }, { - id: "3-0-migration", - title: "v3.0 Migration", - relativePath: "docs/3-0-migration.mdx", - compId: "articles-docs-3-0-migration", - crumb: "Version Upgrade", + id: "studio/studio", + title: "Starting the Studio", + relativePath: "docs/studio/studio.mdx", + compId: "articles-docs-studio-studio", + crumb: "Timeline basics", }, { - id: "quality", - title: "Quality Guide", - relativePath: "docs/quality.mdx", - compId: "articles-docs-quality", - crumb: "Make it look good", + id: "studio/update-default-props", + title: "updateDefaultProps()", + relativePath: "docs/studio/update-default-props.mdx", + compId: "articles-docs-studio-update-default-props", + crumb: "@remotion/studio", }, { - id: "composition", - title: "<Composition>", - relativePath: "docs/composition.mdx", - compId: "articles-docs-composition", - crumb: "API", + id: "studio/watch-public-folder", + title: "watchPublicFolder()", + relativePath: "docs/studio/watch-public-folder.mdx", + compId: "articles-docs-studio-watch-public-folder", + crumb: "@remotion/studio", }, { - id: "audio", - title: "<Audio>", - relativePath: "docs/audio.mdx", - compId: "articles-docs-audio", - crumb: "How to", + id: "studio/watch-static-file", + title: "watchStaticFile()", + relativePath: "docs/studio/watch-static-file.mdx", + compId: "articles-docs-studio-watch-static-file", + crumb: "@remotion/studio", }, { - id: "visualize-audio", - title: "visualizeAudio()", - relativePath: "docs/visualize-audio.mdx", - compId: "articles-docs-visualize-audio", - crumb: "@remotion/media-utils", + id: "studio/write-static-file", + title: "writeStaticFile()", + relativePath: "docs/studio/write-static-file.mdx", + compId: "articles-docs-studio-write-static-file", + crumb: "@remotion/studio", }, { - id: "react-18", - title: "Upgrade to React 18", - relativePath: "docs/react-18.mdx", - compId: "articles-docs-react-18", - crumb: "The new and shiny", + id: "support", + title: "Support Policy", + relativePath: "docs/support.mdx", + compId: "articles-docs-support", + crumb: "Let us help you", }, { - id: "cloudrun-alpha", - title: "Cloud Run Alpha", - relativePath: "docs/cloudrun-alpha.mdx", - compId: "articles-docs-cloudrun-alpha", - crumb: "Version Upgrade", + id: "tailwind/enable-tailwind", + title: "enableTailwind()", + relativePath: "docs/tailwind/enable-tailwind.mdx", + compId: "articles-docs-tailwind-enable-tailwind", + crumb: "@remotion/tailwind", }, { - id: "easing", - title: "Easing", - relativePath: "docs/easing.mdx", - compId: "articles-docs-easing", - crumb: "API", + id: "tailwind", + title: "@remotion/tailwind", + relativePath: "docs/tailwind/overview.mdx", + compId: "articles-docs-tailwind-overview", + crumb: null, }, { - id: "resources", - title: "List of resources", - relativePath: "docs/resources.mdx", - compId: "articles-docs-resources", - crumb: "Ecosystem", + id: "tailwind-legacy", + title: "TailwindCSS v2 (Legacy)", + relativePath: "docs/tailwind-2.mdx", + compId: "articles-docs-tailwind-2", + crumb: "Legacy docs", }, { - id: "schemas", - title: "Defining a schema for your props", - relativePath: "docs/schemas.mdx", - compId: "articles-docs-schemas", - crumb: "How To", + id: "tailwind", + title: "TailwindCSS", + relativePath: "docs/tailwind.mdx", + compId: "articles-docs-tailwind", + crumb: "text-lg font-bold", }, { - id: "slow-method-to-extract-frame", - title: "Slow method to extract frame", - relativePath: "docs/slow-method-to-extract-frame.mdx", - compId: "articles-docs-slow-method-to-extract-frame", + id: "target-closed", + title: "'Target closed' error message", + relativePath: "docs/target-closed.mdx", + compId: "articles-docs-target-closed", crumb: "Troubleshooting", }, { - id: "lambda", - title: "@remotion/lambda", - relativePath: "docs/lambda.mdx", - compId: "articles-docs-lambda", - crumb: null, + id: "terminology/bundle", + title: "Remotion Bundle", + relativePath: "docs/terminology/bundle.mdx", + compId: "articles-docs-terminology-bundle", + crumb: "Terminology", }, { - id: "transitions/timings/springtiming", - title: "springTiming()", - relativePath: "docs/transitions/timings/springtiming.mdx", - compId: "articles-docs-transitions-timings-springtiming", - crumb: "@remotion/transitions - Timings", + id: "terminology/cloud-run-url", + title: "Cloud Run URL", + relativePath: "docs/terminology/cloud-run-url.mdx", + compId: "articles-docs-terminology-cloud-run-url", + crumb: "Terminology", }, { - id: "transitions/timings/index", - title: "Timings", - relativePath: "docs/transitions/timings/index.mdx", - compId: "articles-docs-transitions-timings-index", - crumb: "@remotion/transitions", + id: "terminology/composition", + title: "Composition", + relativePath: "docs/terminology/composition.mdx", + compId: "articles-docs-terminology-composition", + crumb: "Terminology", }, { - id: "transitions/timings/custom", - title: "Custom timings", - relativePath: "docs/transitions/timings/custom.mdx", - compId: "articles-docs-transitions-timings-custom", - crumb: "@remotion/transitions", + id: "terminology/concurrency", + title: "Concurrency", + relativePath: "docs/terminology/concurrency.mdx", + compId: "articles-docs-terminology-concurrency", + crumb: "Terminology", }, { - id: "transitions/timings/lineartiming", - title: "linearTiming()", - relativePath: "docs/transitions/timings/lineartiming.mdx", - compId: "articles-docs-transitions-timings-lineartiming", - crumb: "@remotion/transitions - Timings", + id: "terminology/entry-point", + title: "Entry point", + relativePath: "docs/terminology/entry-point.mdx", + compId: "articles-docs-terminology-entry-point", + crumb: "Terminology", }, { - id: "transitions/index", - title: "@remotion/transitions", - relativePath: "docs/transitions/index.mdx", - compId: "articles-docs-transitions-index", - crumb: null, + id: "terminology/input-props", + title: "Input Props", + relativePath: "docs/terminology/input-props.mdx", + compId: "articles-docs-terminology-input-props", + crumb: "Terminology", }, { - id: "transitions/presentations/clock-wipe", - title: "clockWipe()", - relativePath: "docs/transitions/presentations/clock-wipe.mdx", - compId: "articles-docs-transitions-presentations-clock-wipe", - crumb: "@remotion/transitions - Presentations", + id: "terminology/player", + title: "Remotion Player", + relativePath: "docs/terminology/player.mdx", + compId: "articles-docs-terminology-player", + crumb: "Terminology", }, { - id: "transitions/presentations/index", - title: "Presentations", - relativePath: "docs/transitions/presentations/index.mdx", - compId: "articles-docs-transitions-presentations-index", - crumb: "@remotion/transitions", + id: "terminology/public-dir", + title: "Public directory", + relativePath: "docs/terminology/public-dir.mdx", + compId: "articles-docs-terminology-public-dir", + crumb: "Terminology", }, { - id: "transitions/presentations/custom", - title: "Custom presentations", - relativePath: "docs/transitions/presentations/custom.mdx", - compId: "articles-docs-transitions-presentations-custom", - crumb: "@remotion/transitions", + id: "terminology/remotion-root", + title: "Remotion Root", + relativePath: "docs/terminology/remotion-root.mdx", + compId: "articles-docs-terminology-remotion-root", + crumb: "Terminology", }, { - id: "transitions/presentations/cube", - title: "cube()", - relativePath: "docs/transitions/presentations/cube.mdx", - compId: "articles-docs-transitions-presentations-cube", - crumb: "@remotion/transitions - Presentations", + id: "terminology/root-file", + title: "Root file", + relativePath: "docs/terminology/root-file.mdx", + compId: "articles-docs-terminology-root-file", + crumb: "Terminology", }, { - id: "transitions/presentations/wipe", - title: "wipe()", - relativePath: "docs/transitions/presentations/wipe.mdx", - compId: "articles-docs-transitions-presentations-wipe", - crumb: "@remotion/transitions - Presentations", + id: "terminology/sequence", + title: "Sequence", + relativePath: "docs/terminology/sequence.mdx", + compId: "articles-docs-terminology-sequence", + crumb: "Terminology", }, { - id: "transitions/presentations/fade", - title: "fade()", - relativePath: "docs/transitions/presentations/fade.mdx", - compId: "articles-docs-transitions-presentations-fade", - crumb: "@remotion/transitions - Presentations", + id: "terminology/serve-url", + title: "Serve URL", + relativePath: "docs/terminology/serve-url.mdx", + compId: "articles-docs-terminology-serve-url", + crumb: "Terminology", }, { - id: "transitions/presentations/slide", - title: "slide()", - relativePath: "docs/transitions/presentations/slide.mdx", - compId: "articles-docs-transitions-presentations-slide", - crumb: "@remotion/transitions - Presentations", + id: "terminology/service-name", + title: "Service Name", + relativePath: "docs/terminology/service-name.mdx", + compId: "articles-docs-terminology-service-name", + crumb: "Terminology", }, { - id: "transitions/presentations/flip", - title: "flip()", - relativePath: "docs/transitions/presentations/flip.mdx", - compId: "articles-docs-transitions-presentations-flip", - crumb: "@remotion/transitions - Presentations", + id: "terminology/studio", + title: "Remotion Studio", + relativePath: "docs/terminology/studio.mdx", + compId: "articles-docs-terminology-studio", + crumb: "Terminology", }, { - id: "transitions/transitionseries", - title: "<TransitionSeries>", - relativePath: "docs/transitions/transitionseries.mdx", - compId: "articles-docs-transitions-transitionseries", - crumb: "@remotion/transitions", + id: "terminology", + title: "Terminology", + relativePath: "docs/terminology.mdx", + compId: "articles-docs-terminology", + crumb: "The Remotion dictionary", }, { - id: "transitions/audio-transitions", - title: "Transitioning with audio", - relativePath: "docs/transitions/audio-transitions.mdx", - compId: "articles-docs-transitions-audio-transitions", - crumb: "@remotion/transitions", + id: "the-fundamentals", + title: "The fundamentals", + relativePath: "docs/the-fundamentals.mdx", + compId: "articles-docs-the-fundamentals", + crumb: "Getting started", }, { - id: "video-vs-offthreadvideo", - title: "<Video> vs. <OffthreadVideo>", - relativePath: "docs/video-vs-offthreadvideo.mdx", - compId: "articles-docs-video-vs-offthreadvideo", - crumb: "Comparison", + id: "third-party", + title: "Third party integrations", + relativePath: "docs/third-party.mdx", + compId: "articles-docs-third-party", + crumb: "Integrations", }, { - id: "ffmpeg", - title: "Installing FFmpeg", - relativePath: "docs/ffmpeg.mdx", - compId: "articles-docs-ffmpeg", - crumb: "(you don't have to)", + id: "three-canvas", + title: "<ThreeCanvas>", + relativePath: "docs/three-canvas.mdx", + compId: "articles-docs-three-canvas", + crumb: "@remotion/three", }, { - id: "video", - title: "<Video>", - relativePath: "docs/video.mdx", - compId: "articles-docs-video", - crumb: "API", + id: "three", + title: "@remotion/three", + relativePath: "docs/three.mdx", + compId: "articles-docs-three", + crumb: null, }, { - id: "measure-spring", - title: "measureSpring()", - relativePath: "docs/measure-spring.mdx", - compId: "articles-docs-measure-spring", - crumb: "API", + id: "timeout", + title: "Debugging timeouts", + relativePath: "docs/timeout.mdx", + compId: "articles-docs-timeout", + crumb: "Troubleshooting", }, { - id: "javascript", - title: "Plain JavaScript", - relativePath: "docs/jsx-support.mdx", - compId: "articles-docs-jsx-support", - crumb: "How To", + id: "transforms", + title: "Transforms", + relativePath: "docs/transforms.mdx", + compId: "articles-docs-transforms", + crumb: "The fundamentals", }, { - id: "stills", - title: "Still images", - relativePath: "docs/stills.mdx", - compId: "articles-docs-stills", - crumb: "How to generate", + id: "transitions/audio-transitions", + title: "Transitioning with audio", + relativePath: "docs/transitions/audio-transitions.mdx", + compId: "articles-docs-transitions-audio-transitions", + crumb: "@remotion/transitions", }, { - id: "get-help", - title: "Get help", - relativePath: "docs/ask-for-help.mdx", - compId: "articles-docs-ask-for-help", + id: "transitions/index", + title: "@remotion/transitions", + relativePath: "docs/transitions/index.mdx", + compId: "articles-docs-transitions-index", crumb: null, }, { - id: "still", - title: "<Still>", - relativePath: "docs/still.mdx", - compId: "articles-docs-still", - crumb: "API", + id: "transitions/presentations/clock-wipe", + title: "clockWipe()", + relativePath: "docs/transitions/presentations/clock-wipe.mdx", + compId: "articles-docs-transitions-presentations-clock-wipe", + crumb: "@remotion/transitions - Presentations", }, { - id: "preload-video", - title: "preloadVideo()", - relativePath: "docs/preload/preload-video.mdx", - compId: "articles-docs-preload-preload-video", - crumb: "@remotion/player", + id: "transitions/presentations/cube", + title: "cube()", + relativePath: "docs/transitions/presentations/cube.mdx", + compId: "articles-docs-transitions-presentations-cube", + crumb: "@remotion/transitions - Presentations", }, { - id: "preload-audio", - title: "preloadAudio()", - relativePath: "docs/preload/preload-audio.mdx", - compId: "articles-docs-preload-preload-audio", - crumb: "@remotion/preload", + id: "transitions/presentations/custom", + title: "Custom presentations", + relativePath: "docs/transitions/presentations/custom.mdx", + compId: "articles-docs-transitions-presentations-custom", + crumb: "@remotion/transitions", }, { - id: "preload-image", - title: "preloadImage()", - relativePath: "docs/preload/preload-image.mdx", - compId: "articles-docs-preload-preload-image", - crumb: "@remotion/preload", + id: "transitions/presentations/fade", + title: "fade()", + relativePath: "docs/transitions/presentations/fade.mdx", + compId: "articles-docs-transitions-presentations-fade", + crumb: "@remotion/transitions - Presentations", }, { - id: "preload-font", - title: "preloadFont()", - relativePath: "docs/preload/preload-font.mdx", - compId: "articles-docs-preload-preload-font", - crumb: "@remotion/preload", + id: "transitions/presentations/flip", + title: "flip()", + relativePath: "docs/transitions/presentations/flip.mdx", + compId: "articles-docs-transitions-presentations-flip", + crumb: "@remotion/transitions - Presentations", }, { - id: "preload", - title: "@remotion/preload", - relativePath: "docs/preload/preload.mdx", - compId: "articles-docs-preload-preload", - crumb: null, + id: "transitions/presentations/index", + title: "Presentations", + relativePath: "docs/transitions/presentations/index.mdx", + compId: "articles-docs-transitions-presentations-index", + crumb: "@remotion/transitions", }, { - id: "version", - title: "VERSION", - relativePath: "docs/version.mdx", - compId: "articles-docs-version", - crumb: "API", + id: "transitions/presentations/slide", + title: "slide()", + relativePath: "docs/transitions/presentations/slide.mdx", + compId: "articles-docs-transitions-presentations-slide", + crumb: "@remotion/transitions - Presentations", }, { - id: "prereleases", - title: "Testing prereleases", - relativePath: "docs/prereleases.mdx", - compId: "articles-docs-prereleases", - crumb: "Only the brave", + id: "transitions/presentations/wipe", + title: "wipe()", + relativePath: "docs/transitions/presentations/wipe.mdx", + compId: "articles-docs-transitions-presentations-wipe", + crumb: "@remotion/transitions - Presentations", }, { - id: "artifact", - title: "<Artifact>", - relativePath: "docs/artifact.mdx", - compId: "articles-docs-artifact", - crumb: "API", + id: "transitions/timings/custom", + title: "Custom timings", + relativePath: "docs/transitions/timings/custom.mdx", + compId: "articles-docs-transitions-timings-custom", + crumb: "@remotion/transitions", }, { - id: "license", - title: "License & Pricing", - relativePath: "docs/license.mdx", - compId: "articles-docs-license", - crumb: null, + id: "transitions/timings/index", + title: "Timings", + relativePath: "docs/transitions/timings/index.mdx", + compId: "articles-docs-transitions-timings-index", + crumb: "@remotion/transitions", }, { - id: "fonts", - title: "Using fonts", - relativePath: "docs/fonts.mdx", - compId: "articles-docs-fonts", - crumb: "Techniques", + id: "transitions/timings/lineartiming", + title: "linearTiming()", + relativePath: "docs/transitions/timings/lineartiming.mdx", + compId: "articles-docs-transitions-timings-lineartiming", + crumb: "@remotion/transitions - Timings", }, { - id: "enametoolong", - title: "ENAMETOOLONG", - relativePath: "docs/enametoolong.mdx", - compId: "articles-docs-enametoolong", - crumb: "Troubleshooting", + id: "transitions/timings/springtiming", + title: "springTiming()", + relativePath: "docs/transitions/timings/springtiming.mdx", + compId: "articles-docs-transitions-timings-springtiming", + crumb: "@remotion/transitions - Timings", }, { - id: "get-waveform-portion", - title: "getWaveformPortion()", - relativePath: "docs/get-waveform-portion.mdx", - compId: "articles-docs-get-waveform-portion", - crumb: "@remotion/media-utils", + id: "transitions/transitionseries", + title: "<TransitionSeries>", + relativePath: "docs/transitions/transitionseries.mdx", + compId: "articles-docs-transitions-transitionseries", + crumb: "@remotion/transitions", }, { - id: "webpack", - title: "Custom Webpack config", - relativePath: "docs/overwriting-webpack-config.mdx", - compId: "articles-docs-overwriting-webpack-config", - crumb: "How To", + id: "transitioning", + title: "Transitions", + relativePath: "docs/transitions.mdx", + compId: "articles-docs-transitions", + crumb: "Techniques", }, { - id: '"null"', - title: "<Experimental.Null>", - relativePath: "docs/null.mdx", - compId: "articles-docs-null", - crumb: "Experimental API", + id: "transparent-videos", + title: "Transparent videos", + relativePath: "docs/transparent-videos.mdx", + compId: "articles-docs-transparent-videos", + crumb: "Techniques", }, { - id: "parameterized-rendering", - title: "Parameterized videos", - relativePath: "docs/parameterized-rendering.mdx", - compId: "articles-docs-parameterized-rendering", - crumb: "How To", + id: "troubleshooting/background-image", + title: "Flickering when using background-image or mask-image", + relativePath: "docs/troubleshooting/background-image.mdx", + compId: "articles-docs-troubleshooting-background-image", + crumb: "Common mistakes", }, { - id: "get-audio-data", - title: "getAudioData()", - relativePath: "docs/get-audio-data.mdx", - compId: "articles-docs-get-audio-data", - crumb: "@remotion/media-utils", + id: "broken-fast-refresh", + title: "Fast Refresh not working", + relativePath: "docs/troubleshooting/broken-fast-refresh.mdx", + compId: "articles-docs-troubleshooting-broken-fast-refresh", + crumb: "Troubleshooting", }, { - id: "clipper", - title: "<Experimental.Clipper>", - relativePath: "docs/clipper.mdx", - compId: "articles-docs-clipper", - crumb: "Experimental API", + id: "troubleshooting/browser-launch", + title: "Failed to launch the browser process", + relativePath: "docs/troubleshooting/browser-launch.mdx", + compId: "articles-docs-troubleshooting-browser-launch", + crumb: "Troubleshooting", }, { - id: "random", - title: "random()", - relativePath: "docs/random.mdx", - compId: "articles-docs-random", - crumb: "API", + id: "troubleshooting/bundling-bundle", + title: "Calling bundle() in bundled code", + relativePath: "docs/troubleshooting/bundling-bundle.mdx", + compId: "articles-docs-troubleshooting-bundling-bundle", + crumb: "Troubleshooting", }, { - id: "remotion", - title: "remotion", - relativePath: "docs/remotion.mdx", - compId: "articles-docs-remotion", - crumb: "Core API Reference", + id: "troubleshooting/could-not-be-parsed-as-a-value-list", + title: "The source provided could not be parsed as a value list", + relativePath: + "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx", + compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list", + crumb: "Browser quirks", }, { - id: "folder", - title: "<Folder>", - relativePath: "docs/folder.mdx", - compId: "articles-docs-folder", - crumb: "API", + id: "troubleshooting/debug-failed-render", + title: "Debugging render failures", + relativePath: "docs/troubleshooting/debug-failed-render.mdx", + compId: "articles-docs-troubleshooting-debug-failed-render", + crumb: "Troubleshooting", }, { - id: "video-manipulation", - title: "Video manipulation", - relativePath: "docs/video-manipulation.mdx", - compId: "articles-docs-video-manipulation", - crumb: "How To", + id: "defaultprops-too-big", + title: "defaultProps too big - could not serialize", + relativePath: "docs/troubleshooting/defaultprops-too-big.mdx", + compId: "articles-docs-troubleshooting-defaultprops-too-big", + crumb: "Troubleshooting", }, { - id: "use-offthread-video-texture", - title: "useOffthreadVideoTexture()", - relativePath: "docs/use-offthread-video-texture.mdx", - compId: "articles-docs-use-offthread-video-texture", - crumb: "@remotion/three", + id: "troubleshooting/delay-render-proxy", + title: "Loading <Img> with src http://localhost:3000/proxy", + relativePath: "docs/troubleshooting/delay-render-proxy.mdx", + compId: "articles-docs-troubleshooting-delay-render-proxy", + crumb: "Troubleshooting", }, { - id: "animation-math", - title: "Animation math", - relativePath: "docs/animation-math.mdx", - compId: "articles-docs-animation-math", - crumb: "Techniques", + id: "loading-root-component", + title: "Root component Timeout", + relativePath: "docs/troubleshooting/loading-root-component.mdx", + compId: "articles-docs-troubleshooting-loading-root-component", + crumb: "Troubleshooting", }, { - id: "render-all", - title: "Render all compositions", - relativePath: "docs/render-all.mdx", - compId: "articles-docs-render-all", - crumb: "Techniques", + id: "troubleshooting/nextjs-image", + title: "Flickering when using Next.js <Image> tag", + relativePath: "docs/troubleshooting/nextjs-image.mdx", + compId: "articles-docs-troubleshooting-nextjs-image", + crumb: "Common mistakes", }, { - id: "layout-utils/index", - title: "@remotion/layout-utils", - relativePath: "docs/layout-utils/index.mdx", - compId: "articles-docs-layout-utils-index", - crumb: null, + id: "rosetta", + title: "Apple Silicon under Rosetta", + relativePath: "docs/troubleshooting/rosetta.mdx", + compId: "articles-docs-troubleshooting-rosetta", + crumb: "Troubleshooting", + }, + { + id: "troubleshooting/sigkill", + title: "Process quit with signal SIGKILL", + relativePath: "docs/troubleshooting/sigkill.mdx", + compId: "articles-docs-troubleshooting-sigkill", + crumb: "Troubleshooting", }, { - id: "layout-utils/best-practices", - title: "Best practices for @remotion/layout-utils", - relativePath: "docs/layout-utils/best-practices.mdx", - compId: "articles-docs-layout-utils-best-practices", - crumb: "@remotion/layout-utils", + id: "troubleshooting/timed-out-page-function", + title: "Timed out evaluating page function", + relativePath: "docs/troubleshooting/timed-out-page-function.mdx", + compId: "articles-docs-troubleshooting-timed-out-page-function", + crumb: "Troubleshooting", }, { - id: "layout-utils/measure-text", - title: "measureText()", - relativePath: "docs/layout-utils/measure-text.mdx", - compId: "articles-docs-layout-utils-measure-text", - crumb: "@remotion/layout-utils", + id: "player-flicker", + title: "Avoiding flickering in <Player>", + relativePath: "docs/troubleshooting/video-flicker.mdx", + compId: "articles-docs-troubleshooting-video-flicker", + crumb: "Frame-perfection", }, { - id: "layout-utils/fill-text-box", - title: "fillTextBox()", - relativePath: "docs/layout-utils/fill-text-box.mdx", - compId: "articles-docs-layout-utils-fill-text-box", - crumb: "@remotion/layout-utils", + id: "upgrading", + title: "Upgrading Remotion", + relativePath: "docs/upgrading.mdx", + compId: "articles-docs-upgrading", + crumb: "upgrade remotion", }, { - id: "layout-utils/fit-text", - title: "fitText()", - relativePath: "docs/layout-utils/fit-text.mdx", - compId: "articles-docs-layout-utils-fit-text", - crumb: "@remotion/layout-utils", + id: "use-audio-data", + title: "useAudioData()", + relativePath: "docs/use-audio-data.mdx", + compId: "articles-docs-use-audio-data", + crumb: "@remotion/media-utils", }, { - id: "buffer-state", - title: "Display a buffer state", - relativePath: "docs/buffer-state.mdx", - compId: "articles-docs-buffer-state", - crumb: "Building video apps", + id: "use-buffer-state", + title: "useBufferState()", + relativePath: "docs/use-buffer-state.mdx", + compId: "articles-docs-use-buffer-state", + crumb: "API", }, { - id: "staticfile-remote-urls", - title: "staticFile() does not support remote URLs", - relativePath: "docs/static-file-remote-urls.mdx", - compId: "articles-docs-static-file-remote-urls", - crumb: "Troubleshooting", + id: "use-current-frame", + title: "useCurrentFrame()", + relativePath: "docs/use-current-frame.mdx", + compId: "articles-docs-use-current-frame", + crumb: "API", }, { - id: "player/index", - title: "@remotion/player", - relativePath: "docs/player/index.mdx", - compId: "articles-docs-player-index", - crumb: null, + id: "use-current-scale", + title: "useCurrentScale()", + relativePath: "docs/use-current-scale.mdx", + compId: "articles-docs-use-current-scale", + crumb: "API", }, { - id: "integration", - title: "Code sharing", - relativePath: "docs/player/player-integration.mdx", - compId: "articles-docs-player-player-integration", - crumb: "@remotion/player", + id: "use-img-and-iframe", + title: "<Img>, <Video> and <Audio>", + relativePath: "docs/use-img-and-iframe.mdx", + compId: "articles-docs-use-img-and-iframe", + crumb: "Best practices", }, { - id: "player/preloading", - title: "Preloading assets", - relativePath: "docs/player/preloading.mdx", - compId: "articles-docs-player-preloading", - crumb: "@remotion/player", + id: "use-offthread-video-texture", + title: "useOffthreadVideoTexture()", + relativePath: "docs/use-offthread-video-texture.mdx", + compId: "articles-docs-use-offthread-video-texture", + crumb: "@remotion/three", }, { - id: "best-practices", - title: "Player - Best practices", - relativePath: "docs/player/best-practices.mdx", - compId: "articles-docs-player-best-practices", - crumb: "@remotion/player", + id: "use-video-config", + title: "useVideoConfig()", + relativePath: "docs/use-video-config.mdx", + compId: "articles-docs-use-video-config", + crumb: "API", }, { - id: "autoplay", - title: "Combatting autoplay issues", - relativePath: "docs/player/autoplay.mdx", - compId: "articles-docs-player-autoplay", - crumb: "@remotion/player", + id: "use-video-texture", + title: "useVideoTexture()", + relativePath: "docs/use-video-texture.mdx", + compId: "articles-docs-use-video-texture", + crumb: "@remotion/three", }, { - id: "player/premounting", - title: "Premounting", - relativePath: "docs/player/premounting.mdx", - compId: "articles-docs-player-premounting", - crumb: "@remotion/player", + id: "using-audio", + title: "Using audio", + relativePath: "docs/using-audio.mdx", + compId: "articles-docs-using-audio", + crumb: "Techniques", }, { - id: "player/api", - title: "<Player>", - relativePath: "docs/player/api.mdx", - compId: "articles-docs-player-api", - crumb: "@remotion/player", + id: "using-randomness", + title: "Using randomness", + relativePath: "docs/using-randomness.mdx", + compId: "articles-docs-using-randomness", + crumb: "Roll the dice", }, { - id: "thumbnail", - title: "<Thumbnail>", - relativePath: "docs/player/thumbnail.mdx", - compId: "articles-docs-player-thumbnail", - crumb: "@remotion/player", + id: "version-mismatch", + title: "Version mismatch", + relativePath: "docs/version-mismatch.mdx", + compId: "articles-docs-version-mismatch", + crumb: "How to fix a", }, { - id: "current-time", - title: "Displaying the current time", - relativePath: "docs/player/current-time.mdx", - compId: "articles-docs-player-current-time", - crumb: "@remotion/player", + id: "version", + title: "VERSION", + relativePath: "docs/version.mdx", + compId: "articles-docs-version", + crumb: "API", }, { - id: "examples", - title: "Examples for @remotion/player", - relativePath: "docs/player/player-examples.mdx", - compId: "articles-docs-player-player-examples", - crumb: "@remotion/player", + id: "video-manipulation", + title: "Video manipulation", + relativePath: "docs/video-manipulation.mdx", + compId: "articles-docs-video-manipulation", + crumb: "How To", }, { - id: "installation", - title: "Installation", - relativePath: "docs/player/installation.mdx", - compId: "articles-docs-player-installation", - crumb: "@remotion/player", + id: "video-uploads", + title: "Handling user video uploads", + relativePath: "docs/video-uploads.mdx", + compId: "articles-docs-video-uploads", + crumb: "Building video apps", }, { - id: "buffer-state", - title: "The Player buffer state", - relativePath: "docs/player/buffer-state.mdx", - compId: "articles-docs-player-buffer-state", - crumb: "Best practices", + id: "video-vs-offthreadvideo", + title: "<Video> vs. <OffthreadVideo>", + relativePath: "docs/video-vs-offthreadvideo.mdx", + compId: "articles-docs-video-vs-offthreadvideo", + crumb: "Comparison", }, { - id: "scaling", - title: "Sizing", - relativePath: "docs/player/scaling.mdx", - compId: "articles-docs-player-scaling", - crumb: "@remotion/player", + id: "video", + title: "<Video>", + relativePath: "docs/video.mdx", + compId: "articles-docs-video", + crumb: "API", }, { id: "visual-editing", @@ -3563,45 +3570,45 @@ export const articles = [ crumb: "How To", }, { - id: "4-0-migration", - title: "v4.0 Migration", - relativePath: "docs/4-0-migration.mdx", - compId: "articles-docs-4-0-migration", - crumb: "Version Upgrade", + id: "visualize-audio", + title: "visualizeAudio()", + relativePath: "docs/visualize-audio.mdx", + compId: "articles-docs-visualize-audio", + crumb: "@remotion/media-utils", }, { - id: "get-gif-duration-in-seconds", - title: "getGifDurationInSeconds()", - relativePath: "docs/gif/get-gif-duration-in-seconds.mdx", - compId: "articles-docs-gif-get-gif-duration-in-seconds", - crumb: "@remotion/gif", + id: "watchstaticfile", + title: "watchStaticFile()", + relativePath: "docs/watch-static-file.mdx", + compId: "articles-docs-watch-static-file", + crumb: "API", }, { - id: "gif/preload-gif", - title: "preloadGif()", - relativePath: "docs/gif/preload-gif.mdx", - compId: "articles-docs-gif-preload-gif", - crumb: "@remotion/gif", + id: "wrong-composition-mount", + title: "Wrongly mounted <Composition>", + relativePath: "docs/wrong-composition-mount.mdx", + compId: "articles-docs-wrong-composition-mount", + crumb: "Troubleshooting", }, { - id: "gif/index", - title: "@remotion/gif", - relativePath: "docs/gif/index.mdx", - compId: "articles-docs-gif-index", - crumb: null, + id: "zod-types/index", + title: "@remotion/zod-types", + relativePath: "docs/zod-types/index.mdx", + compId: "articles-docs-zod-types-index", + crumb: "Schema", }, { - id: "gif/gif", - title: "<Gif>", - relativePath: "docs/gif/gif.mdx", - compId: "articles-docs-gif-gif", - crumb: "@remotion/gif", + id: "zod-types/z-color", + title: "zColor()", + relativePath: "docs/zod-types/z-color.mdx", + compId: "articles-docs-zod-types-z-color", + crumb: null, }, { - id: "scaling", - title: "Output scaling", - relativePath: "docs/scaling.mdx", - compId: "articles-docs-scaling", - crumb: "How To", + id: "zod-types/z-textarea", + title: "zTextarea()", + relativePath: "docs/zod-types/z-textarea.mdx", + compId: "articles-docs-zod-types-z-textarea", + crumb: null, }, ]; diff --git a/packages/docs/static/generated/articles-docs-artifacts.png b/packages/docs/static/generated/articles-docs-artifacts.png new file mode 100644 index 0000000000000000000000000000000000000000..2ddc01c2728aa73f104e04911a0d941bccde884c GIT binary patch literal 37466 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 G?Ee5I*Pmhl literal 0 HcmV?d00001 diff --git a/packages/lambda/src/api/render-still-on-lambda.ts b/packages/lambda/src/api/render-still-on-lambda.ts index dd4c2d8ce21..73326f45ebd 100644 --- a/packages/lambda/src/api/render-still-on-lambda.ts +++ b/packages/lambda/src/api/render-still-on-lambda.ts @@ -5,7 +5,7 @@ import type { } from '@remotion/renderer'; import type {BrowserSafeApis} from '@remotion/renderer/client'; import {NoReactAPIs} from '@remotion/renderer/pure'; -import type {ReceivedAsset} from '../functions/helpers/overall-render-progress'; +import type {ReceivedArtifact} from '../functions/helpers/overall-render-progress'; import type {RenderStillLambdaResponsePayload} from '../functions/still'; import type {AwsRegion} from '../pricing/aws-regions'; import {callLambdaWithStreaming} from '../shared/call-lambda'; @@ -68,7 +68,7 @@ export type RenderStillOnLambdaOutput = { bucketName: string; renderId: string; cloudWatchLogs: string; - receivedAssets: ReceivedAsset[]; + artifacts: ReceivedArtifact[]; }; const internalRenderStillOnLambda = async ( @@ -132,7 +132,7 @@ const internalRenderStillOnLambda = async ( renderId: res.renderId, rendererFunctionName: null, }), - receivedAssets: res.receivedAssets, + artifacts: res.receivedArtifacts, }; } catch (err) { if ((err as Error).stack?.includes('UnrecognizedClientException')) { diff --git a/packages/lambda/src/cli/commands/render/progress.ts b/packages/lambda/src/cli/commands/render/progress.ts index cfd637d60fb..2f29c13b491 100644 --- a/packages/lambda/src/cli/commands/render/progress.ts +++ b/packages/lambda/src/cli/commands/render/progress.ts @@ -2,7 +2,7 @@ import {CliInternals} from '@remotion/cli'; import {RenderInternals} from '@remotion/renderer'; import {NoReactInternals} from 'remotion/no-react'; import type {RenderProgress} from '../../../defaults'; -import type {ReceivedAsset} from '../../../functions/helpers/overall-render-progress'; +import type {ReceivedArtifact} from '../../../functions/helpers/overall-render-progress'; import {truthy} from '../../../shared/truthy'; type LambdaInvokeProgress = { @@ -201,7 +201,7 @@ const makeTopRow = (overall: RenderProgress) => { return CliInternals.chalk.gray(str); }; -export const makeArtifactProgress = (artifactProgress: ReceivedAsset[]) => { +export const makeArtifactProgress = (artifactProgress: ReceivedArtifact[]) => { if (artifactProgress.length === 0) { return null; } @@ -240,7 +240,7 @@ export const makeProgressString = ({ ...makeRenderProgress(overall), makeCombinationProgress(overall), downloadInfo ? makeDownloadProgress(downloadInfo) : null, - makeArtifactProgress(overall.artifactProgress), + makeArtifactProgress(overall.artifacts), ] .filter(NoReactInternals.truthy) .join('\n'); diff --git a/packages/lambda/src/cli/commands/still.ts b/packages/lambda/src/cli/commands/still.ts index 9c17a9ba290..61b2725a243 100644 --- a/packages/lambda/src/cli/commands/still.ts +++ b/packages/lambda/src/cli/commands/still.ts @@ -276,7 +276,7 @@ export const stillCommand = async ( indent: false, logLevel, }, - makeArtifactProgress(res.receivedAssets), + makeArtifactProgress(res.artifacts), ); Log.info( diff --git a/packages/lambda/src/functions/helpers/create-post-render-data.ts b/packages/lambda/src/functions/helpers/create-post-render-data.ts index f5a550063ed..c2725428ccb 100644 --- a/packages/lambda/src/functions/helpers/create-post-render-data.ts +++ b/packages/lambda/src/functions/helpers/create-post-render-data.ts @@ -103,6 +103,6 @@ export const createPostRenderData = ({ deleteAfter: renderMetadata.deleteAfter, estimatedBillingDurationInMilliseconds, timeToCombine: timeToCombine ?? null, - artifactProgress: overallProgress.receivedAssets, + artifactProgress: overallProgress.receivedArtifact, }; }; diff --git a/packages/lambda/src/functions/helpers/get-progress.ts b/packages/lambda/src/functions/helpers/get-progress.ts index 0a77693adc6..e22af635f2d 100644 --- a/packages/lambda/src/functions/helpers/get-progress.ts +++ b/packages/lambda/src/functions/helpers/get-progress.ts @@ -110,7 +110,7 @@ export const getProgress = async ({ compositionValidated: overallProgress.compositionValidated, functionLaunched: overallProgress.functionLaunched, serveUrlOpened: overallProgress.serveUrlOpened, - artifactProgress: overallProgress.receivedAssets, + artifacts: overallProgress.receivedArtifact, }; } @@ -258,6 +258,6 @@ export const getProgress = async ({ compositionValidated: overallProgress.compositionValidated, functionLaunched: overallProgress.functionLaunched, serveUrlOpened: overallProgress.serveUrlOpened, - artifactProgress: overallProgress.receivedAssets, + artifacts: overallProgress.receivedArtifact, }; }; diff --git a/packages/lambda/src/functions/helpers/overall-render-progress.ts b/packages/lambda/src/functions/helpers/overall-render-progress.ts index 710ad3c7b44..9d1f821331f 100644 --- a/packages/lambda/src/functions/helpers/overall-render-progress.ts +++ b/packages/lambda/src/functions/helpers/overall-render-progress.ts @@ -26,10 +26,10 @@ export type OverallRenderProgress = { functionLaunched: number; serveUrlOpened: number | null; compositionValidated: number | null; - receivedAssets: ReceivedAsset[]; + receivedArtifact: ReceivedArtifact[]; }; -export type ReceivedAsset = { +export type ReceivedArtifact = { filename: string; sizeInBytes: number; s3Url: string; @@ -63,8 +63,8 @@ export type OverallProgressHelper = { get: () => OverallRenderProgress; setServeUrlOpened: (timestamp: number) => void; setCompositionValidated: (timestamp: number) => void; - addReceivedAsset: (asset: ReceivedAsset) => void; - getReceivedAssets: () => ReceivedAsset[]; + addReceivedArtifact: (asset: ReceivedArtifact) => void; + getReceivedArtifacts: () => ReceivedArtifact[]; }; export const makeInitialOverallRenderProgress = ( @@ -88,7 +88,7 @@ export const makeInitialOverallRenderProgress = ( functionLaunched: Date.now(), serveUrlOpened: null, compositionValidated: null, - receivedAssets: [], + receivedArtifact: [], }; }; @@ -285,12 +285,12 @@ export const makeOverallRenderProgress = ({ renderProgress.retries.push(retry); upload(); }, - addReceivedAsset(asset) { - renderProgress.receivedAssets.push(asset); + addReceivedArtifact(asset) { + renderProgress.receivedArtifact.push(asset); upload(); }, - getReceivedAssets() { - return renderProgress.receivedAssets; + getReceivedArtifacts() { + return renderProgress.receivedArtifact; }, get: () => renderProgress, }; diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts index 9c15005c11c..4b4f2bde321 100644 --- a/packages/lambda/src/functions/launch.ts +++ b/packages/lambda/src/functions/launch.ts @@ -359,7 +359,7 @@ const innerLaunchHandler = async ({ const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => { if ( overallProgress - .getReceivedAssets() + .getReceivedArtifacts() .find((a) => a.filename === artifact.filename) ) { return {alreadyExisted: true}; @@ -367,7 +367,7 @@ const innerLaunchHandler = async ({ const region = getCurrentRegionInFunction(); const s3Key = artifactName(renderMetadata.renderId, artifact.filename); - overallProgress.addReceivedAsset({ + overallProgress.addReceivedArtifact({ filename: artifact.filename, sizeInBytes: artifact.content.length, s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts index 49219e0a702..7c2e9f5abbf 100644 --- a/packages/lambda/src/functions/still.ts +++ b/packages/lambda/src/functions/still.ts @@ -40,7 +40,7 @@ import {getCurrentRegionInFunction} from './helpers/get-current-region'; import {getOutputUrlFromMetadata} from './helpers/get-output-url-from-metadata'; import {lambdaWriteFile} from './helpers/io'; import {onDownloadsHelper} from './helpers/on-downloads-logger'; -import type {ReceivedAsset} from './helpers/overall-render-progress'; +import type {ReceivedArtifact} from './helpers/overall-render-progress'; import {makeInitialOverallRenderProgress} from './helpers/overall-render-progress'; import {validateComposition} from './helpers/validate-composition'; import type {OnStream} from './streaming/streaming'; @@ -196,7 +196,7 @@ const innerStillHandler = async ({ throw new Error('Should not download a browser in Lambda'); }; - const receivedAssets: ReceivedAsset[] = []; + const receivedArtifact: ReceivedArtifact[] = []; const {key, renderBucketName, customCredentials} = getExpectedOutName( renderMetadata, @@ -205,12 +205,12 @@ const innerStillHandler = async ({ ); const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => { - if (receivedAssets.find((a) => a.filename === artifact.filename)) { + if (receivedArtifact.find((a) => a.filename === artifact.filename)) { return {alreadyExisted: true}; } const s3Key = artifactName(renderMetadata.renderId, artifact.filename); - receivedAssets.push({ + receivedArtifact.push({ filename: artifact.filename, sizeInBytes: artifact.content.length, s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, @@ -334,7 +334,7 @@ const innerStillHandler = async ({ estimatedPrice: formatCostsInfo(estimatedPrice), renderId, outKey, - receivedAssets, + receivedArtifacts: receivedArtifact, }; onStream({ @@ -352,7 +352,7 @@ export type RenderStillLambdaResponsePayload = { sizeInBytes: number; estimatedPrice: CostsInfo; renderId: string; - receivedAssets: ReceivedAsset[]; + receivedArtifacts: ReceivedArtifact[]; }; export const stillHandler = async ( diff --git a/packages/lambda/src/shared/constants.ts b/packages/lambda/src/shared/constants.ts index 6418b7b2979..ec4ad1d54a4 100644 --- a/packages/lambda/src/shared/constants.ts +++ b/packages/lambda/src/shared/constants.ts @@ -14,7 +14,7 @@ import type { import type {BrowserSafeApis} from '@remotion/renderer/client'; import type {ChunkRetry} from '../functions/helpers/get-retry-stats'; import type {DeleteAfter} from '../functions/helpers/lifecycle'; -import type {ReceivedAsset} from '../functions/helpers/overall-render-progress'; +import type {ReceivedArtifact} from '../functions/helpers/overall-render-progress'; import type {EnhancedErrorInfo} from '../functions/helpers/write-lambda-error'; import type {AwsRegion} from '../pricing/aws-regions'; import type { @@ -415,7 +415,7 @@ export type PostRenderData = { estimatedBillingDurationInMilliseconds: number; deleteAfter: DeleteAfter | null; timeToCombine: number | null; - artifactProgress: ReceivedAsset[]; + artifactProgress: ReceivedArtifact[]; }; export type CostsInfo = { @@ -471,7 +471,7 @@ export type RenderProgress = { functionLaunched: number; serveUrlOpened: number | null; compositionValidated: number | null; - artifactProgress: ReceivedAsset[]; + artifacts: ReceivedArtifact[]; }; export type Privacy = 'public' | 'private' | 'no-acl'; diff --git a/packages/studio-shared/src/index.ts b/packages/studio-shared/src/index.ts index 7e66eac1070..3301c871636 100644 --- a/packages/studio-shared/src/index.ts +++ b/packages/studio-shared/src/index.ts @@ -54,7 +54,7 @@ export { CopyingState, DownloadProgress, JobProgressCallback, - ReceivedAsset, + ReceivedArtifact as ReceivedAsset, RenderJob, RenderJobWithCleanup, RenderingProgressInput, diff --git a/packages/studio-shared/src/render-job.ts b/packages/studio-shared/src/render-job.ts index 86eefd9e7e2..a849a9547c4 100644 --- a/packages/studio-shared/src/render-job.ts +++ b/packages/studio-shared/src/render-job.ts @@ -59,14 +59,14 @@ export type AggregateRenderProgress = { artifactState: ArtifactProgress; }; -export type ReceivedAsset = { +export type ReceivedArtifact = { filename: string; absoluteOutputDestination: string; sizeInBytes: number; }; export type ArtifactProgress = { - received: ReceivedAsset[]; + received: ReceivedArtifact[]; }; export type JobProgressCallback = ( From 32a5c6e84b971e9c1b91eabed6b17a5361bc7415 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 11:44:58 +0200 Subject: [PATCH 23/36] document onArtifact --- packages/docs/docs/renderer/render-frames.mdx | 4 ++++ packages/docs/docs/renderer/render-media.mdx | 4 ++++ packages/docs/docs/renderer/render-still.mdx | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/packages/docs/docs/renderer/render-frames.mdx b/packages/docs/docs/renderer/render-frames.mdx index e365c82fc3a..c00b4e1ae6f 100644 --- a/packages/docs/docs/renderer/render-frames.mdx +++ b/packages/docs/docs/renderer/render-frames.mdx @@ -129,6 +129,10 @@ Disables audio output. This option may only be set in combination with a video c <Options id="log" /> +### `onArtifact?`<AvailableFrom v="4.0.176" /> + +[Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [`<Artifact>`](/docs/artifact) component. + ### `puppeteerInstance?` _optional_ diff --git a/packages/docs/docs/renderer/render-media.mdx b/packages/docs/docs/renderer/render-media.mdx index 4e97b32fa10..8c9d73f2d6c 100644 --- a/packages/docs/docs/renderer/render-media.mdx +++ b/packages/docs/docs/renderer/render-media.mdx @@ -102,6 +102,10 @@ A `number` specifying how many render processes should be started in parallel, a <Options id="log" /> +### `onArtifact?`<AvailableFrom v="4.0.176" /> + +[Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [`<Artifact>`](/docs/artifact) component. + ### `audioCodec?` _"pcm-16" | "aac" | "mp3" | "opus", available from v3.3.41_ diff --git a/packages/docs/docs/renderer/render-still.mdx b/packages/docs/docs/renderer/render-still.mdx index 6ebd37f7a82..13cfbfb1e84 100644 --- a/packages/docs/docs/renderer/render-still.mdx +++ b/packages/docs/docs/renderer/render-still.mdx @@ -127,6 +127,10 @@ An object containing key-value pairs of environment variables which will be inje <Options id="log" /> +### `onArtifact?`<AvailableFrom v="4.0.176" /> + +[Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [`<Artifact>`](/docs/artifact) component. + ### `overwrite?` _optional - default `true`_ From cd3da52774859ba8dd267e482754bc50d4787db5 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 11:45:18 +0200 Subject: [PATCH 24/36] Update artifact.mdx --- packages/docs/docs/artifact.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs/docs/artifact.mdx b/packages/docs/docs/artifact.mdx index 4193d3e0e4a..b0cf31c288b 100644 --- a/packages/docs/docs/artifact.mdx +++ b/packages/docs/docs/artifact.mdx @@ -6,7 +6,7 @@ crumb: API # `<Artifact>`<AvailableFrom v="4.0.176"/> -By rendering an `<Artifact>` tag in your Remotion markup, [an extra file will get emitted during rendering](/docs/emitting-artifacts). +By rendering an `<Artifact>` tag in your Remotion markup, [an extra file will get emitted during rendering](/docs/artifacts). ```tsx twoslash title="MyComp.tsx" import { Artifact, useCurrentFrame } from "remotion"; From 1e46e919bbd88a7507ff613df37acd0ec52a401f Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 11:54:53 +0200 Subject: [PATCH 25/36] upgrade caniuse --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index de61ae7f9f5..452aad332c0 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "pnpm": { "overrides": { - "caniuse-lite": "1.0.30001577" + "caniuse-lite": "1.0.30001636" } }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7369b1cebd6..0f5c3899877 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 importers: @@ -11573,7 +11573,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -11589,7 +11589,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -11928,7 +11928,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 electron-to-chromium: 1.4.397 escalade: 3.1.1 node-releases: 1.1.77 @@ -11939,7 +11939,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 electron-to-chromium: 1.4.397 node-releases: 2.0.10 update-browserslist-db: 1.0.11(browserslist@4.21.5) @@ -11949,7 +11949,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 electron-to-chromium: 1.4.640 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) @@ -12104,13 +12104,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false - /caniuse-lite@1.0.30001577: - resolution: {integrity: sha512-rs2ZygrG1PNXMfmncM0B5H1hndY5ZCC9b5TkFaVNfZ+AUlyqcMyVIQtc3fsezi0NUCk5XZfDf9WS6WxMxnfdrg==} + /caniuse-lite@1.0.30001636: + resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} /canvaskit-wasm@0.39.1: resolution: {integrity: sha512-Gy3lCmhUdKq+8bvDrs9t8+qf7RvcjuQn+we7vTVVyqgOVO1UVfHpsnBxkTZw+R4ApEJ3D5fKySl9TU11hmjl/A==} @@ -14012,7 +14012,7 @@ packages: '@mdn/browser-compat-data': 5.5.19 ast-metadata-inferer: 0.8.0 browserslist: 4.22.2 - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 eslint: 8.56.0 find-up: 5.0.0 lodash.memoize: 4.1.2 @@ -17874,7 +17874,7 @@ packages: '@next/env': 14.1.4 '@swc/helpers': 0.5.2 busboy: 1.6.0 - caniuse-lite: 1.0.30001577 + caniuse-lite: 1.0.30001636 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 From 74a97cf2f754bb4504c8f8229c0a0686d90dda26 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 11:55:04 +0200 Subject: [PATCH 26/36] serialize right in browser --- packages/core/src/Artifact.tsx | 26 ++++++++++++++----- packages/core/src/CompositionManager.tsx | 1 + .../functions/helpers/serialize-artifact.ts | 8 +++--- packages/renderer/src/collect-assets.ts | 17 +++++++----- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx index 28f9fe04461..74c6dfc6c30 100644 --- a/packages/core/src/Artifact.tsx +++ b/packages/core/src/Artifact.tsx @@ -24,13 +24,25 @@ export const Artifact: React.FC<{ return; } - registerRenderAsset({ - type: 'artifact', - id, - content, - filename, - frame, - }); + if (typeof content === 'string') { + registerRenderAsset({ + type: 'artifact', + id, + content, + filename, + frame, + binary: false, + }); + } else { + registerRenderAsset({ + type: 'artifact', + id, + content: btoa(new TextDecoder('utf8').decode(content)), + filename, + frame, + binary: true, + }); + } return () => { return unregisterRenderAsset(id); diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx index 728851aa192..24ebe63ebb9 100644 --- a/packages/core/src/CompositionManager.tsx +++ b/packages/core/src/CompositionManager.tsx @@ -145,6 +145,7 @@ export type ArtifactAsset = { filename: string; content: string | Uint8Array; frame: number; + binary: boolean; }; export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset; diff --git a/packages/lambda/src/functions/helpers/serialize-artifact.ts b/packages/lambda/src/functions/helpers/serialize-artifact.ts index f65a856928c..50e26c89983 100644 --- a/packages/lambda/src/functions/helpers/serialize-artifact.ts +++ b/packages/lambda/src/functions/helpers/serialize-artifact.ts @@ -11,8 +11,9 @@ export const deserializeArtifact = ( serializedArtifact: SerializedArtifact, ): EmittedArtifact => { if (serializedArtifact.binary) { - const encoder = new TextEncoder(); - const content = encoder.encode(atob(serializedArtifact.stringContent)); + const content = new TextEncoder().encode( + atob(serializedArtifact.stringContent), + ); return { filename: serializedArtifact.filename, @@ -32,8 +33,7 @@ export const serializeArtifact = ( artifact: EmittedArtifact, ): SerializedArtifact => { if (artifact.content instanceof Uint8Array) { - const decoder = new TextDecoder('utf8'); - const b64encoded = btoa(decoder.decode(artifact.content)); + const b64encoded = btoa(new TextDecoder('utf8').decode(artifact.content)); return { filename: artifact.filename, stringContent: b64encoded, diff --git a/packages/renderer/src/collect-assets.ts b/packages/renderer/src/collect-assets.ts index 334d33df73e..279eba2f6ed 100644 --- a/packages/renderer/src/collect-assets.ts +++ b/packages/renderer/src/collect-assets.ts @@ -1,4 +1,4 @@ -import type {TRenderAsset} from 'remotion/no-react'; +import type {ArtifactAsset, TRenderAsset} from 'remotion/no-react'; import type {Page} from './browser/BrowserPage'; import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate'; @@ -26,15 +26,20 @@ export const collectAssets = async ({ return asset; } - const stringOrUintArray = - typeof asset.content === 'string' - ? asset.content - : new Uint8Array(Object.values(asset.content)); + if (typeof asset.content !== 'string') { + throw new Error( + `Expected string content for artifact ${asset.id}, but got ${asset.content}`, + ); + } + + const stringOrUintArray = asset.binary + ? new TextEncoder().encode(atob(asset.content as string)) + : asset.content; return { ...asset, content: stringOrUintArray, - }; + } as ArtifactAsset; }); return fixedArtifacts; From 5d734ca2b405329860e1ba6f44f34ffdbdc19d02 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 12:02:29 +0200 Subject: [PATCH 27/36] emit to out/[composition-id]/artifact --- packages/cli/src/on-artifact.ts | 37 ++++++++++++++++++------ packages/cli/src/progress-bar.ts | 6 ++-- packages/cli/src/render-flows/render.ts | 14 +++++---- packages/cli/src/render-flows/still.ts | 16 ++++++---- packages/studio-shared/src/render-job.ts | 2 ++ 5 files changed, 52 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts index 595d3d946fb..0ed587898e7 100644 --- a/packages/cli/src/on-artifact.ts +++ b/packages/cli/src/on-artifact.ts @@ -1,26 +1,45 @@ import type {OnArtifact} from '@remotion/renderer'; import type {ArtifactProgress} from '@remotion/studio-shared'; -import {writeFileSync} from 'fs'; +import {existsSync, mkdirSync, writeFileSync} from 'fs'; import path from 'path'; -export const handleOnArtifact = ( - artifactState: ArtifactProgress, - onProgress: (artifact: ArtifactProgress) => void, -) => { +export const handleOnArtifact = ({ + artifactState, + onProgress, + compositionId, +}: { + artifactState: ArtifactProgress; + onProgress: (artifact: ArtifactProgress) => void; + compositionId: string; +}) => { const initialProgress = {...artifactState}; const onArtifact: OnArtifact = (artifact) => { - const absoluteOutputDestination = path.join( - process.cwd(), + // It would be nice in the future to customize the artifact output destination + + const relativeOutputDestination = path.join( + 'out', + compositionId, artifact.filename.replace('/', path.sep), ); + const defaultOutName = path.join(process.cwd(), relativeOutputDestination); + + if (!existsSync(path.dirname(defaultOutName))) { + mkdirSync(path.dirname(defaultOutName), { + recursive: true, + }); + } + + const alreadyExisted = existsSync(defaultOutName); - writeFileSync(artifact.filename, artifact.content); + writeFileSync(defaultOutName, artifact.content); initialProgress.received.push({ - absoluteOutputDestination, + absoluteOutputDestination: defaultOutName, filename: artifact.filename, sizeInBytes: artifact.content.length, + alreadyExisted, + relativeOutputDestination, }); }; diff --git a/packages/cli/src/progress-bar.ts b/packages/cli/src/progress-bar.ts index 20cc2ca1b18..8a15af1fcc5 100644 --- a/packages/cli/src/progress-bar.ts +++ b/packages/cli/src/progress-bar.ts @@ -202,12 +202,12 @@ const makeArtifactProgress = (artifactState: ArtifactProgress) => { return received .map((artifact) => { return [ - chalk.blue('+'.padEnd(LABEL_WIDTH)), + chalk.blue((artifact.alreadyExisted ? '○' : '+').padEnd(LABEL_WIDTH)), chalk.blue( makeHyperlink({ url: 'file://' + artifact.absoluteOutputDestination, - fallback: artifact.filename, - text: artifact.filename, + fallback: artifact.absoluteOutputDestination, + text: artifact.relativeOutputDestination, }), ), chalk.gray(`${formatBytes(artifact.sizeInBytes)}`), diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index 89983aeb578..b657c9e2602 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -250,11 +250,6 @@ export const renderVideoFlow = async ({ } }; - const {onArtifact} = handleOnArtifact(artifactState, (progress) => { - artifactState = progress; - updateRenderProgress({newline: false, printToConsole: true}); - }); - const {urlOrBundle, cleanup: cleanupBundle} = await bundleOnCliOrTakeServeUrl( { fullPath: fullEntryPoint, @@ -332,6 +327,15 @@ export const renderVideoFlow = async ({ onBrowserDownload, }); + const {onArtifact} = handleOnArtifact({ + artifactState, + onProgress: (progress) => { + artifactState = progress; + updateRenderProgress({newline: false, printToConsole: true}); + }, + compositionId, + }); + const {value: codec, source: codecReason} = BrowserSafeApis.options.videoCodecOption.getValue( { diff --git a/packages/cli/src/render-flows/still.ts b/packages/cli/src/render-flows/still.ts index 8ffa426fa50..f538dc667f9 100644 --- a/packages/cli/src/render-flows/still.ts +++ b/packages/cli/src/render-flows/still.ts @@ -318,13 +318,17 @@ export const renderStillFlow = async ({ isUsingParallelEncoding: false, }); - const {onArtifact} = handleOnArtifact(aggregate.artifactState, (progress) => { - aggregate.artifactState = progress; + const {onArtifact} = handleOnArtifact({ + artifactState: aggregate.artifactState, + compositionId, + onProgress: (progress) => { + aggregate.artifactState = progress; - updateRenderProgress({ - newline: false, - printToConsole: true, - }); + updateRenderProgress({ + newline: false, + printToConsole: true, + }); + }, }); await RenderInternals.internalRenderStill({ diff --git a/packages/studio-shared/src/render-job.ts b/packages/studio-shared/src/render-job.ts index a849a9547c4..d22740d133a 100644 --- a/packages/studio-shared/src/render-job.ts +++ b/packages/studio-shared/src/render-job.ts @@ -62,7 +62,9 @@ export type AggregateRenderProgress = { export type ReceivedArtifact = { filename: string; absoluteOutputDestination: string; + relativeOutputDestination: string; sizeInBytes: number; + alreadyExisted: boolean; }; export type ArtifactProgress = { From ed893cdcc2d5ab3c0274d801560d7a1fa9f6d32d Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 13:46:00 +0200 Subject: [PATCH 28/36] better error handling on stills --- packages/example/runlambda.sh | 8 -- .../lambda/src/api/render-still-on-lambda.ts | 6 +- packages/lambda/src/functions/index.ts | 79 ++++++++++--------- packages/lambda/src/functions/still.ts | 61 ++++++++------ packages/renderer/src/seek-to-frame.ts | 2 +- 5 files changed, 85 insertions(+), 71 deletions(-) diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh index 0c9bd57010b..cc4b70f427b 100644 --- a/packages/example/runlambda.sh +++ b/packages/example/runlambda.sh @@ -1,10 +1,2 @@ -set -e -cd .. -cd lambda -npm run buildlambda -cd .. -cd example -bunx remotion lambda functions rmall -f -bunx remotion lambda functions deploy --memory=3000 --disk=10000 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry bunx remotion lambda still testbed-v6 subtitle --log=verbose --delete-after="1-day" out-there.png diff --git a/packages/lambda/src/api/render-still-on-lambda.ts b/packages/lambda/src/api/render-still-on-lambda.ts index 73326f45ebd..fe00d4cfd51 100644 --- a/packages/lambda/src/api/render-still-on-lambda.ts +++ b/packages/lambda/src/api/render-still-on-lambda.ts @@ -102,12 +102,16 @@ const internalRenderStillOnLambda = async ( }); } + if (message.type === 'error-occurred') { + reject(new Error(message.payload.error)); + } + if (message.type === 'still-rendered') { resolve(message.payload); } }, timeoutInTest: 120000, - retriesRemaining: 1, + retriesRemaining: input.maxRetries, }) .then(() => { reject(new Error('Expected response to be streamed')); diff --git a/packages/lambda/src/functions/index.ts b/packages/lambda/src/functions/index.ts index 0923b3e130e..3fccf215c14 100644 --- a/packages/lambda/src/functions/index.ts +++ b/packages/lambda/src/functions/index.ts @@ -69,47 +69,50 @@ const innerHandler = async ({ params.logLevel, ); - await new Promise((resolve, reject) => { - const onStream = (payload: StreamingPayload) => { - const message = makeStreamPayload({ - message: payload, - }); - return new Promise<void>((innerResolve, innerReject) => { - responseWriter - .write(message) - .then(() => { - innerResolve(); - }) - .catch((err) => { - reject(err); - innerReject(err); - }); - }); - }; + try { + await new Promise((resolve, reject) => { + const onStream = (payload: StreamingPayload) => { + const message = makeStreamPayload({ + message: payload, + }); + return new Promise<void>((innerResolve, innerReject) => { + responseWriter + .write(message) + .then(() => { + innerResolve(); + }) + .catch((err) => { + reject(err); + innerReject(err); + }); + }); + }; - if (params.streamed) { - onStream({ - type: 'render-id-determined', - payload: {renderId}, - }); - } + if (params.streamed) { + onStream({ + type: 'render-id-determined', + payload: {renderId}, + }); + } - stillHandler({ - expectedBucketOwner: currentUserId, - params, - renderId, - onStream, - timeoutInMilliseconds, - }) - .then((r) => { - resolve(r); + stillHandler({ + expectedBucketOwner: currentUserId, + params, + renderId, + onStream, + timeoutInMilliseconds, }) - .catch((err) => { - reject(err); - }); - }); - - await responseWriter.end(); + .then((r) => { + resolve(r); + }) + .catch((err) => { + reject(err); + }); + }); + await responseWriter.end(); + } catch (err) { + console.log({err}); + } return; } diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts index 7c2e9f5abbf..95f07a44ca5 100644 --- a/packages/lambda/src/functions/still.ts +++ b/packages/lambda/src/functions/still.ts @@ -6,13 +6,11 @@ import {NoReactInternals} from 'remotion/no-react'; import {VERSION} from 'remotion/version'; import {estimatePrice} from '../api/estimate-price'; import {internalGetOrCreateBucket} from '../api/get-or-create-bucket'; -import {callLambda} from '../shared/call-lambda'; import {cleanupSerializedInputProps} from '../shared/cleanup-serialized-input-props'; import {decompressInputProps} from '../shared/compress-props'; import type { CostsInfo, LambdaPayload, - LambdaPayloads, RenderMetadata, } from '../shared/constants'; import { @@ -43,6 +41,7 @@ import {onDownloadsHelper} from './helpers/on-downloads-logger'; import type {ReceivedArtifact} from './helpers/overall-render-progress'; import {makeInitialOverallRenderProgress} from './helpers/overall-render-progress'; import {validateComposition} from './helpers/validate-composition'; +import {getTmpDirStateIfENoSp} from './helpers/write-lambda-error'; import type {OnStream} from './streaming/streaming'; type Options = { @@ -357,9 +356,16 @@ export type RenderStillLambdaResponsePayload = { export const stillHandler = async ( options: Options, -): Promise<{ - type: 'success'; -}> => { +): Promise< + | { + type: 'success'; + } + | { + type: 'error'; + message: string; + stack: string; + } +> => { const {params} = options; if (params.type !== LambdaRoutines.still) { @@ -375,10 +381,6 @@ export const stillHandler = async ( const isBrowserError = isFlakyError(err as Error); const willRetry = isBrowserError || params.maxRetries > 0; - if (!willRetry) { - throw err; - } - RenderInternals.Log.error( { indent: false, @@ -389,21 +391,34 @@ export const stillHandler = async ( 'Will retry.', ); - const retryPayload: LambdaPayloads[LambdaRoutines.still] = { - ...params, - maxRetries: params.maxRetries - 1, - attempt: params.attempt + 1, - }; - - const res = await callLambda({ - functionName: process.env.AWS_LAMBDA_FUNCTION_NAME as string, - payload: retryPayload, - region: getCurrentRegionInFunction(), - type: LambdaRoutines.still, - timeoutInTest: 120000, - }); + if (params.streamed) { + await options.onStream({ + type: 'error-occurred', + payload: { + error: (err as Error).stack as string, + shouldRetry: willRetry, + errorInfo: { + name: (err as Error).name as string, + message: (err as Error).message as string, + stack: (err as Error).stack as string, + chunk: null, + frame: null, + type: 'renderer', + isFatal: false, + tmpDir: getTmpDirStateIfENoSp((err as Error).stack as string), + attempt: params.attempt, + totalAttempts: 1 + params.maxRetries, + willRetry: true, + }, + }, + }); + } - return res; + return { + type: 'error', + message: (err as Error).message, + stack: (err as Error).stack as string, + }; } finally { forgetBrowserEventLoop( options.params.type === LambdaRoutines.still diff --git a/packages/renderer/src/seek-to-frame.ts b/packages/renderer/src/seek-to-frame.ts index 1082ec7d0f3..f6d40ae8bca 100644 --- a/packages/renderer/src/seek-to-frame.ts +++ b/packages/renderer/src/seek-to-frame.ts @@ -113,7 +113,7 @@ export const waitForReady = ({ args: [], frame, page, - timeoutInMilliseconds, + timeoutInMilliseconds: 5000, }) .then((res) => { reject( From 50960dffb01ef19694aabca53529244475de4798 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 13:50:26 +0200 Subject: [PATCH 29/36] only write to lambda once actually saved --- .../functions/helpers/write-lambda-error.ts | 2 +- packages/lambda/src/functions/launch.ts | 27 ++++++++++++++----- packages/lambda/src/functions/still.ts | 3 +-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/lambda/src/functions/helpers/write-lambda-error.ts b/packages/lambda/src/functions/helpers/write-lambda-error.ts index 4f5d3a30ed5..db04989c89b 100644 --- a/packages/lambda/src/functions/helpers/write-lambda-error.ts +++ b/packages/lambda/src/functions/helpers/write-lambda-error.ts @@ -3,7 +3,7 @@ import {getFolderFiles} from './get-files-in-folder'; import {errorIsOutOfSpaceError} from './is-enosp-err'; export type LambdaErrorInfo = { - type: 'renderer' | 'browser' | 'stitcher' | 'webhook'; + type: 'renderer' | 'browser' | 'stitcher' | 'webhook' | 'artifact'; message: string; name: string; stack: string; diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts index 4b4f2bde321..62d73e407e9 100644 --- a/packages/lambda/src/functions/launch.ts +++ b/packages/lambda/src/functions/launch.ts @@ -367,12 +367,6 @@ const innerLaunchHandler = async ({ const region = getCurrentRegionInFunction(); const s3Key = artifactName(renderMetadata.renderId, artifact.filename); - overallProgress.addReceivedArtifact({ - filename: artifact.filename, - sizeInBytes: artifact.content.length, - s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, - s3Key, - }); const start = Date.now(); RenderInternals.Log.info( @@ -394,9 +388,28 @@ const innerLaunchHandler = async ({ {indent: false, logLevel: params.logLevel}, `Wrote artifact to S3 in ${Date.now() - start}ms`, ); + overallProgress.addReceivedArtifact({ + filename: artifact.filename, + sizeInBytes: artifact.content.length, + s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`, + s3Key, + }); }) .catch((err) => { - // TODO: Handle error + overallProgress.addErrorWithoutUpload({ + type: 'artifact', + message: (err as Error).message, + name: (err as Error).name as string, + stack: (err as Error).stack as string, + tmpDir: null, + frame: artifact.frame, + chunk: null, + isFatal: false, + attempt: 1, + willRetry: false, + totalAttempts: 1, + }); + overallProgress.upload(); RenderInternals.Log.error( {indent: false, logLevel: params.logLevel}, 'Failed to write artifact to S3', diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts index 95f07a44ca5..49c9d7aeb41 100644 --- a/packages/lambda/src/functions/still.ts +++ b/packages/lambda/src/functions/still.ts @@ -238,7 +238,6 @@ const innerStillHandler = async ({ ); }) .catch((err) => { - // TODO: Handle error RenderInternals.Log.error( {indent: false, logLevel: lambdaParams.logLevel}, 'Failed to write artifact to S3', @@ -402,7 +401,7 @@ export const stillHandler = async ( message: (err as Error).message as string, stack: (err as Error).stack as string, chunk: null, - frame: null, + frame: params.frame, type: 'renderer', isFatal: false, tmpDir: getTmpDirStateIfENoSp((err as Error).stack as string), From a817062ac4a85332fb2becf684780db20d4b48e5 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 13:53:14 +0200 Subject: [PATCH 30/36] add docs --- packages/docs/docs/lambda/getrenderprogress.mdx | 4 ++++ packages/docs/docs/lambda/renderstillonlambda.mdx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/docs/docs/lambda/getrenderprogress.mdx b/packages/docs/docs/lambda/getrenderprogress.mdx index 4772f48f67b..49561b65116 100644 --- a/packages/docs/docs/lambda/getrenderprogress.mdx +++ b/packages/docs/docs/lambda/getrenderprogress.mdx @@ -164,6 +164,10 @@ If the render is in progress, this is `null`. If the render is done, it is an ar - `timeInMilliseconds`: The time it took the render that chunk - `frameRange`: A tuple containing the first and last frame that was rendered in that chunk. +### `artifacts`<AvailableFrom v="4.0.176"/> + +Artifacts that were created so far during the render. [See here for an example of dealing with field.](/docs/artifacts#using-rendermediaonlambda) + ## See also - [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/lambda/src/api/get-render-progress.ts) diff --git a/packages/docs/docs/lambda/renderstillonlambda.mdx b/packages/docs/docs/lambda/renderstillonlambda.mdx index 2f0db3ec37a..ff0f3ae277c 100644 --- a/packages/docs/docs/lambda/renderstillonlambda.mdx +++ b/packages/docs/docs/lambda/renderstillonlambda.mdx @@ -292,6 +292,10 @@ _Available from v3.2.10_ A link to CloudWatch (if you haven't disabled it) that you can visit to see the logs for the render. +### `artifacts`<AvailableFrom v="4.0.176"/> + +Artifacts that were created so far during the render. [See here for an example of dealing with field.](/docs/artifacts#using-renderstillonlambda) + ## See also - [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/lambda/src/api/render-still-on-lambda.ts) From 495a18c47a9c9eff8a43ebe267fa8c6456e9cc0a Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 13:58:36 +0200 Subject: [PATCH 31/36] Good error handling on multiple assets --- packages/lambda/src/functions/helpers/stream-renderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts index ea99ff99f60..33aac979667 100644 --- a/packages/lambda/src/functions/helpers/stream-renderer.ts +++ b/packages/lambda/src/functions/helpers/stream-renderer.ts @@ -211,7 +211,7 @@ export const streamRendererFunctionWithRetry = async ({ if (result.type === 'error') { if (!result.shouldRetry) { - throw result.error; + throw new Error(result.error); } overallProgress.addRetry({ From b09d7f0ca6300e8e280cfa60171ce5c59c86b280 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 14:08:48 +0200 Subject: [PATCH 32/36] Discard changes to packages/example/runlambda.sh --- packages/example/runlambda.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh index cc4b70f427b..43c2eb94300 100644 --- a/packages/example/runlambda.sh +++ b/packages/example/runlambda.sh @@ -1,2 +1,10 @@ +set -e +cd .. +cd lambda +npm run buildlambda +cd .. +cd example +bunx remotion lambda functions rmall -f +bunx remotion lambda functions deploy --memory=3000 --disk=10000 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry -bunx remotion lambda still testbed-v6 subtitle --log=verbose --delete-after="1-day" out-there.png +bunx remotion lambda render testbed-v6 OffthreadRemoteVideo --log=verbose --delete-after="1-day" From d532bcb927f8abf0521a2ce44dba0f3dbc4d4d5a Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 14:11:32 +0200 Subject: [PATCH 33/36] update error messages --- packages/lambda/src/functions/helpers/stream-renderer.ts | 2 +- packages/renderer/src/render-frames.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts index 33aac979667..7f39b5ee604 100644 --- a/packages/lambda/src/functions/helpers/stream-renderer.ts +++ b/packages/lambda/src/functions/helpers/stream-renderer.ts @@ -112,7 +112,7 @@ const streamRenderer = ({ if (alreadyExisted) { return resolve({ type: 'error', - error: `Chunk ${payload.chunk} emitted an asset filename ${message.payload.artifact.filename} at frame ${message.payload.artifact.frame} but there is already another artifact with the same name.`, + error: `Chunk ${payload.chunk} emitted an asset filename ${message.payload.artifact.filename} at frame ${message.payload.artifact.frame} but there is already another artifact with the same name. https://remotion.dev/docs/artifacts`, shouldRetry: false, }); } diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index b338772df0d..384e6e0405a 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -519,7 +519,7 @@ const innerRenderFrames = async ({ if (artifact.filename === previousArtifact.filename) { reject( new Error( - `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`, + `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`, ), ); return; From 517dea3bd51557b89c5b61a0ee3d80552cc5c3bf Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 14:13:17 +0200 Subject: [PATCH 34/36] Update render-still.ts --- packages/renderer/src/render-still.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/renderer/src/render-still.ts b/packages/renderer/src/render-still.ts index 14796a1ce34..c4fac575cd0 100644 --- a/packages/renderer/src/render-still.ts +++ b/packages/renderer/src/render-still.ts @@ -343,7 +343,7 @@ const innerRenderStill = async ({ for (const previousArtifact of previousArtifactAssets) { if (artifact.filename === previousArtifact.filename) { throw new Error( - `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`, + `An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`, ); } } From c7ab57e76a7111aaaf685903fbf9592aeda01a99 Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 14:14:13 +0200 Subject: [PATCH 35/36] Update index.ts --- packages/studio-shared/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/studio-shared/src/index.ts b/packages/studio-shared/src/index.ts index 3301c871636..76d7d358be1 100644 --- a/packages/studio-shared/src/index.ts +++ b/packages/studio-shared/src/index.ts @@ -54,7 +54,6 @@ export { CopyingState, DownloadProgress, JobProgressCallback, - ReceivedArtifact as ReceivedAsset, RenderJob, RenderJobWithCleanup, RenderingProgressInput, From 12c63f71e930052f3569dd809286760b2cd6d4ff Mon Sep 17 00:00:00 2001 From: Jonny Burger <jonathanburger11@gmail.com> Date: Thu, 20 Jun 2024 14:26:07 +0200 Subject: [PATCH 36/36] Update Artifact.tsx --- packages/core/src/Artifact.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx index 74c6dfc6c30..673e19fb786 100644 --- a/packages/core/src/Artifact.tsx +++ b/packages/core/src/Artifact.tsx @@ -24,23 +24,23 @@ export const Artifact: React.FC<{ return; } - if (typeof content === 'string') { + if (content instanceof Uint8Array) { registerRenderAsset({ type: 'artifact', id, - content, + content: btoa(new TextDecoder('utf8').decode(content)), filename, frame, - binary: false, + binary: true, }); } else { registerRenderAsset({ type: 'artifact', id, - content: btoa(new TextDecoder('utf8').decode(content)), + content, filename, frame, - binary: true, + binary: false, }); }