diff --git a/packages/discord-poster/post.ts b/packages/discord-poster/post.ts index c5f6bec681a..1d8ba2346fe 100644 --- a/packages/discord-poster/post.ts +++ b/packages/discord-poster/post.ts @@ -1,41 +1,68 @@ +const DISCORD_MAX_LENGTH = 2000; + const latestRelease = await fetch( - "https://api.github.com/repos/remotion-dev/remotion/releases?per_page=1" + 'https://api.github.com/repos/remotion-dev/remotion/releases?per_page=1', ); const json = await latestRelease.json(); const markdown = [ - `${json[0].tag_name} has been released!`, - `<:merge:909914451447259177> ${json[0].html_url}`, - ...json[0].body.split("\n").map((s) => { - if (s.startsWith("## ")) { - return s.replace("## ", "**<:love:989990489824559104> ") + "**"; - } - return s; - }), + `${json[0].tag_name} has been released!`, + `<:merge:909914451447259177> ${json[0].html_url}`, + ...json[0].body.split('\n').map((s: string) => { + if (s.startsWith('## ')) { + return s.replace('## ', '**<:love:989990489824559104> ') + '**'; + } + return s; + }), ] - .filter(Boolean) - .join("\n"); - -const res = await fetch( - `https://discord.com/api/channels/994527481598070815/messages`, - { - method: "post", - body: JSON.stringify({ - content: markdown, - allowed_mentions: {}, - flags: 1 << 2, - }), - headers: { - "Content-Type": "application/json", - Authorization: `Bot ${process.env.DISCORD_TOKEN}`, - }, - } -); + .filter(Boolean) + .join('\n'); + +const splitIntoChunks = (text: string): string[] => { + const chunks: string[] = []; + let remaining = text; + + while (remaining.length > 0) { + if (remaining.length <= DISCORD_MAX_LENGTH) { + chunks.push(remaining); + break; + } + + const slice = remaining.slice(0, DISCORD_MAX_LENGTH); + const lastNewline = slice.lastIndexOf('\n'); + const splitAt = lastNewline > 0 ? lastNewline : DISCORD_MAX_LENGTH; + + chunks.push(remaining.slice(0, splitAt)); + remaining = remaining.slice(splitAt).replace(/^\n/, ''); + } + + return chunks; +}; + +const chunks = splitIntoChunks(markdown); + +for (const chunk of chunks) { + const res = await fetch( + `https://discord.com/api/channels/994527481598070815/messages`, + { + method: 'post', + body: JSON.stringify({ + content: chunk, + allowed_mentions: {}, + flags: 1 << 2, + }), + headers: { + 'Content-Type': 'application/json', + Authorization: `Bot ${process.env.DISCORD_TOKEN}`, + }, + }, + ); -if (res.status !== 200) { - console.log(await res.text()); - process.exit(1); + if (res.status !== 200) { + console.log(await res.text()); + process.exit(1); + } } export {}; diff --git a/packages/docs/docs/renderer/TableOfContents.tsx b/packages/docs/docs/renderer/TableOfContents.tsx index 71551f8ac42..6708e3dcb92 100644 --- a/packages/docs/docs/renderer/TableOfContents.tsx +++ b/packages/docs/docs/renderer/TableOfContents.tsx @@ -44,7 +44,9 @@ export const TableOfContents: React.FC = () => {
Create token to later cancel a render
- getVideoMetadata() + + getVideoMetadata() +
Get metadata from a video file in Node.js
diff --git a/packages/docs/docs/renderer/render-frames.mdx b/packages/docs/docs/renderer/render-frames.mdx index dccf4323ddc..aa540513aba 100644 --- a/packages/docs/docs/renderer/render-frames.mdx +++ b/packages/docs/docs/renderer/render-frames.mdx @@ -15,10 +15,6 @@ If you want to render only a still image, use [renderStill()](/docs/renderer/ren In Remotion 3.0, we added the [`renderMedia()`](/docs/renderer/render-media) API which combines `renderFrames()` and `stitchFramesToVideo()` into one simplified step and performs the render faster. Prefer `renderMedia()` if you can. ::: -:::info -Configuration in `remotion.config.ts` and CLI flags do not apply to this function. You must pass all options explicitly. -::: - ## Arguments Takes an object with the following keys: diff --git a/packages/docs/docs/renderer/render-media.mdx b/packages/docs/docs/renderer/render-media.mdx index a9ed26d6726..30269d41bee 100644 --- a/packages/docs/docs/renderer/render-media.mdx +++ b/packages/docs/docs/renderer/render-media.mdx @@ -5,9 +5,9 @@ title: renderMedia() crumb: '@remotion/renderer' --- -_Part of the `@remotion/renderer` package._ +# renderMedia() - +_Part of the `@remotion/renderer` package._ Render a video or an audio programmatically. @@ -28,7 +28,6 @@ const composition = await selectComposition({ }); // ---cut--- - await renderMedia({ composition, serveUrl, @@ -50,31 +49,27 @@ _string_ Either a local path pointing to a Remotion Webpack bundle generated by [`bundle()`](/docs/bundle) or a URL where the bundle is hosted. -### `port?` - -Prefer a specific port that will be used to serve the Remotion project. If not specified, a random port will be used. - -### `outputLocation?` - -_string, since v3.0.26_ - -Where to save the output artifact to. Either an absolute path or a relative path that will be resolved relative to the current working directory. Must be a file path (not a folder). - -If not specified or set to `null`, the file will be returned in-memory as a buffer. - ### `composition` _VideoConfig_ -An object describing a composition using `id`, `width`, `height`, `fps` and `durationInFrames`, `defaultProps` and `props`. +An object describing a composition using `id`, `width`, `height`, `fps` and `durationInFrames`, `defaultProps` and `props`. Call [`selectComposition()`](/docs/renderer/select-composition) or [`getCompositions()`](/docs/renderer/get-compositions) to get an array of possible configs. ### `codec` -_"h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif"_ +_string_ Choose a suitable codec for your output media. Refer to the [Encoding guide](/docs/encoding) to find the best codec for your use case. +### `outputLocation?` + +_string_ + +Where to save the output artifact to. Either an absolute path or a relative path that will be resolved relative to the current working directory. Must be a file path (not a folder). + +If not specified or set to `null`, the file will be returned in-memory as a buffer. + ### `inputProps?` _object_ @@ -86,14 +81,22 @@ You may transform input props using [`calculateMetadata()`](/docs/calculate-meta Make sure to also pass the same `inputProps` to [`selectComposition()`](/docs/renderer/select-composition) for this to work correctly. +### `port?` + +_number_ + +Prefer a specific port that will be used to serve the Remotion project. If not specified, a random port will be used. + ### `frameRange?` -_number | [number, number] | [number, null]_ +_number | [number, number] | [number, null]_ Specify a single frame (passing a `number`) or a range of frames (passing a tuple `[number, number]`) to be rendered. By passing `null` (default) all frames of a composition get rendered. Pass `[number, null]` to render from a frame to the end of the composition. ### `concurrency?` +_number | string | null_ + A `number` specifying how many render processes should be started in parallel, a `string` specifying the percentage of the CPU threads to use (e.g. `50%`), or `null` to let Remotion decide based on the CPU of the host machine. Default is half of the CPU threads available. ### `metadata?` @@ -108,11 +111,13 @@ _object_ ### `onArtifact?` +_function_ + [Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [``](/docs/artifact) component. ### `audioCodec?` -_"pcm-16" | "aac" | "mp3" | "opus", available from v3.3.41_ +_string_ Choose the encoding of your audio. @@ -133,7 +138,7 @@ Refer to the [Encoding guide](/docs/encoding/#audio-codec) to see defaults and s ### `crf?` -_number | null_ +_number | null_ The constant rate factor, controlling the quality. See: [Controlling quality using the CRF setting.](/docs/encoding/#controlling-quality-using-the-crf-setting) @@ -151,7 +156,7 @@ If you enable [hardware acceleration](/docs/hardware-acceleration), you cannot s ### `imageFormat?` -_"jpeg" (default) | "png" | "none", since v3.2.27_ +_string_ In which image format the frames should be rendered. @@ -173,7 +178,7 @@ Renders only every nth frame. For example only every second frame, every third f ### `pixelFormat?` -_string_ +_string_ [A custom pixel format to use.](/docs/transparent-videos/) Usually used for special use cases like transparent videos. @@ -231,12 +236,12 @@ If set to false, the output file will not be written if a file already exists. ### `onStart?` -_function_ +_function_ Callback function that gets called once the renderer has prepared to start rendering and has calculated the amount of frames that are going to be rendered: ```tsx twoslash -import {OnStartData} from '@remotion/renderer'; +import type {OnStartData} from '@remotion/renderer'; const onStart = ({ frameCount, @@ -255,12 +260,12 @@ const onStart = ({ ### `onProgress?` -_function_ +_function_ React to render progress. The following callback function is similar to how Remotion displays render progress on it's CLI: ```tsx twoslash title="Simple example - Log overall progress" -import {RenderMediaOnProgress} from '@remotion/renderer'; +import type {RenderMediaOnProgress} from '@remotion/renderer'; const onProgress: RenderMediaOnProgress = ({progress}) => { console.log(`Rendering is ${progress * 100}% complete`); @@ -268,7 +273,7 @@ const onProgress: RenderMediaOnProgress = ({progress}) => { ``` ```tsx twoslash title="Advanced example - Fine-grained progress values" -import {RenderMediaOnProgress} from '@remotion/renderer'; +import type {RenderMediaOnProgress} from '@remotion/renderer'; const onProgress: RenderMediaOnProgress = ({renderedFrames, encodedFrames, encodedDoneIn, renderedDoneIn, stitchStage}) => { if (stitchStage === 'encoding') { @@ -300,12 +305,12 @@ The `progress` attribute is available from v3.2.17. ### `onDownload?` -_function_ +_function_ If an audio (or a video with sound) is included in your project, Remotion needs to download it in order to use it's audio in the output file. You can use this callback to react to a download happening and progressing. ```tsx twoslash -import {RenderMediaOnDownload} from '@remotion/renderer'; +import type {RenderMediaOnDownload} from '@remotion/renderer'; const onDownload: RenderMediaOnDownload = (src) => { console.log(`Downloading ${src}...`); @@ -323,8 +328,6 @@ const onDownload: RenderMediaOnDownload = (src) => { ### `proResProfile?` -_string_ - Sets a ProRes profile. Only applies to videos rendered with `prores` codec. See [Encoding guide](/docs/encoding/#controlling-quality-using-prores-profile) for possible options. ### `x264Preset?` @@ -335,12 +338,12 @@ _string_ ### `onBrowserLog?` -_function_ +_function_ Catch a console message being printed. Example: ```tsx twoslash -import {BrowserLog} from '@remotion/renderer'; +import type {BrowserLog} from '@remotion/renderer'; const onBrowserLog = (log: BrowserLog) => { // `type` is the console.* method: `log`, `warn`, `error`, etc. @@ -406,7 +409,7 @@ Lets you set a custom user agent that the headless Chrome browser assumes. ### `ffmpegOverride?` -_function_ +_function_ Modifies the FFMPEG command that Remotion uses under the hood. It works reducer-style, meaning that you pass a function that takes a command as an argument and returns a new command. diff --git a/packages/docs/docs/renderer/render-still.mdx b/packages/docs/docs/renderer/render-still.mdx index f1027aed41f..523e9b90f17 100644 --- a/packages/docs/docs/renderer/render-still.mdx +++ b/packages/docs/docs/renderer/render-still.mdx @@ -5,17 +5,18 @@ title: renderStill() crumb: '@remotion/renderer' --- -_Part of the `@remotion/renderer` package._ +# renderStill() - +_Part of the `@remotion/renderer` package._ Renders a single frame to an image and writes it to the specified output location. -If you want to render a video, use [renderMedia()](/docs/renderer/render-media) instead. +If you want to render a video, use [`renderMedia()`](/docs/renderer/render-media) instead. ## Example usage -You first need to bundle the project and fetch the compositions. Read [the code snippet on the site for server-side rendering](/docs/ssr) for an example how to come up with the `bundleLocation` and `composition` variables. +You first need to bundle the project and fetch the compositions. +Read [the code snippet on the site for server-side rendering](/docs/ssr) for an example how to come up with the `serveUrl` and `composition` variables. ```ts twoslash import {bundle} from '@remotion/bundler'; @@ -24,11 +25,11 @@ import {getCompositions, renderStill} from '@remotion/renderer'; // The composition you want to render const compositionId = 'HelloWorld'; -const bundleLocation = await bundle({ +const serveUrl = await bundle({ entryPoint: require.resolve('./src/index.ts'), }); -const comps = await getCompositions(bundleLocation, { +const comps = await getCompositions(serveUrl, { inputProps: { custom: 'data', }, @@ -40,10 +41,9 @@ if (!composition) { } // ---cut--- - await renderStill({ composition, - serveUrl: bundleLocation, + serveUrl, output: '/tmp/still.png', inputProps: { custom: 'data', @@ -59,28 +59,36 @@ Takes an object with the following properties: _VideoConfig_ -An object describing a composition using `id`, `width`, `height`, `fps` and `durationInFrames`, `defaultProps` and `props`. +An object describing a composition using `id`, `width`, `height`, `fps` and `durationInFrames`, `defaultProps` and `props`. Call [`selectComposition()`](/docs/renderer/select-composition) or [`getCompositions()`](/docs/renderer/get-compositions) to get an array of possible configs. ### `serveUrl` -Either a local path pointing to a Remotion Webpack bundle generated by [`bundle()`](/docs/bundle) or a URL where the bundle is hosted. +_string_ -### `port?` - -Prefer a specific port that will be used to serve the Remotion project. If not specified, a random port will be used. +Either a local path pointing to a Remotion Webpack bundle generated by [`bundle()`](/docs/bundle) or a URL where the bundle is hosted. ### `output` +_string_ + An absolute path to where the frame should be rendered to. ### `inputProps?` -[Input Props to pass to the selected composition of your video.](/docs/passing-props#passing-input-props-in-the-cli). -Must be a JSON object. -From the root component the props can be read using [`getInputProps()`](/docs/get-input-props). +_object_ + +[Input Props to pass to the selected composition of your video.](/docs/passing-props#passing-input-props-in-the-cli). +Must be a JSON object. +From the root component the props can be read using [`getInputProps()`](/docs/get-input-props). You may transform input props using [`calculateMetadata()`](/docs/calculate-metadata). +### `port?` + +_number_ + +Prefer a specific port that will be used to serve the Remotion project. If not specified, a random port will be used. + ### `frame?` Which frame should be rendered based on its number. Default: `0`. Frames are zero-indexed. @@ -89,6 +97,8 @@ From v3.2.27, negative values are allowed, with `-1` being the last frame. ### `imageFormat?` +_string_ + Which output format the image should have, either `png`, `jpeg`, `webp` or `pdf`. Default: `"png"`. ### `scale?` @@ -117,6 +127,8 @@ An object containing key-value pairs of environment variables which will be inje ### `onArtifact?` +_function_ + [Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [``](/docs/artifact) component. ### `overwrite?` @@ -129,6 +141,8 @@ A string defining the absolute path on disk of the browser executable that shoul ### `onBrowserLog?` +_function_ + Gets called when your project calls `console.log` or another method from console. See the documentation for [`renderFrames`](/docs/renderer/render-frames#onbrowserlog) for more information. ### `timeoutInMilliseconds?` diff --git a/packages/docs/docs/renderer/types.mdx b/packages/docs/docs/renderer/types.mdx new file mode 100644 index 00000000000..4615693efdd --- /dev/null +++ b/packages/docs/docs/renderer/types.mdx @@ -0,0 +1,250 @@ +--- +image: /generated/articles-docs-renderer-types.png +id: types +sidebar_label: Types +title: TypeScript Types Reference +slug: /renderer/types +crumb: '@remotion/renderer' +--- + +The following types are part of the API of `@remotion/renderer`: + +## `Codec` + +```tsx twoslash +import type {Codec} from '@remotion/renderer'; +// ^? +``` + +Refer to the [Encoding guide](/docs/encoding) for more information. + +## `AudioCodec` + +```tsx twoslash +import type {AudioCodec} from '@remotion/renderer'; +// ^? +``` + +Refer to the [Encoding guide](/docs/encoding/#audio-codec) to see defaults and supported combinations. + +## `VideoImageFormat` + +```tsx twoslash +import type {VideoImageFormat} from '@remotion/renderer'; +// ^? +``` + +## `StillImageFormat` + +```tsx twoslash +import type {StillImageFormat} from '@remotion/renderer'; +// ^? +``` + +## `PixelFormat` + +```tsx twoslash +import type {PixelFormat} from '@remotion/renderer'; +// ^? +``` + +## `FrameRange` + +```tsx twoslash +import type {FrameRange} from '@remotion/renderer'; +// ^? +``` + +- A single number renders only that frame +- A tuple `[start, end]` renders frames from `start` to `end` (inclusive) +- A tuple `[start, null]` renders frames from `start` to the end of the composition + +## `Concurrency` + +```tsx twoslash +import type {Concurrency} from '@remotion/renderer'; +// ^? +``` + +## `LogLevel` + +```tsx twoslash +import type {LogLevel} from '@remotion/renderer'; +// ^? +``` + +## `OpenGlRenderer` + +```tsx twoslash +import type {OpenGlRenderer} from '@remotion/renderer'; +// ^? +``` + +## `ChromeMode` + +```tsx twoslash +import type {ChromeMode} from '@remotion/renderer'; +// ^? +``` + +## `ColorSpace` + +```tsx twoslash +import type {ColorSpace} from '@remotion/renderer'; +// ^? +``` + +## `X264Preset` + +```tsx twoslash +import type {X264Preset} from '@remotion/renderer'; +// ^? +``` + +## `Crf` + +```tsx twoslash +import type {Crf} from '@remotion/renderer'; +// ^? +``` + +## `Bitrate` + +```tsx twoslash +import type {Bitrate} from '@remotion/renderer'; +// ^? +``` + +## `ChromiumOptions` + +```tsx twoslash +import type {ChromiumOptions} from '@remotion/renderer'; +// ^? +``` + +## `OnStartData` + +```tsx twoslash +import type {OnStartData} from '@remotion/renderer'; +// ^? +``` + +- `frameCount`: The number of frames that will be rendered +- `parallelEncoding`: Whether parallel encoding is enabled +- `resolvedConcurrency`: The concurrency that will be used + +## `RenderMediaOnProgress` + +```tsx twoslash +import type {RenderMediaOnProgress} from '@remotion/renderer'; +// ^? +``` + +## `StitchingState` + +```tsx twoslash +import type {StitchingState} from '@remotion/renderer'; +// ^? +``` + +- `encoding`: Rendering frames and encoding into video +- `muxing`: Encoding audio and combining it with video (only when parallel encoding is used) + +## `SlowFrame` + +```tsx twoslash +import type {SlowFrame} from '@remotion/renderer'; +// ^? +``` + +## `RenderMediaOnDownload` + +```tsx twoslash +import type {RenderMediaOnDownload} from '@remotion/renderer'; +// ^? +``` + +## `BrowserLog` + +```tsx twoslash +import type {BrowserLog} from '@remotion/renderer'; +// ^? +``` + +- `type`: The `console.*` method (`log`, `warn`, `error`, etc.) +- `text`: The logged message +- `stackTrace`: The stack trace of the log + +## `FfmpegOverrideFn` + +```tsx twoslash +import type {FfmpegOverrideFn} from '@remotion/renderer'; +// ^? +``` + +## `OnArtifact` + +```tsx twoslash +import type {OnArtifact} from '@remotion/renderer'; +// ^? +``` + +## `EmittedArtifact` + +```tsx twoslash +import type {EmittedArtifact} from '@remotion/renderer'; +// ^? +``` + +- `filename`: The name of the artifact file +- `content`: The content of the artifact as a `string` or `Uint8Array` +- `frame`: The frame number at which the artifact was emitted + +## `OnBrowserDownload` + +```tsx twoslash +import type {OnBrowserDownload} from '@remotion/renderer'; +// ^? +``` + +## `DownloadBrowserProgressFn` + +```tsx twoslash +import type {DownloadBrowserProgressFn} from '@remotion/renderer'; +// ^? +``` + +## `NumberOfGifLoops` + +```tsx twoslash +import type {NumberOfGifLoops} from '@remotion/renderer'; +// ^? +``` + +## `RenderMediaOptions` + +```tsx twoslash +import type {RenderMediaOptions} from '@remotion/renderer'; +// ^? +``` + +## `RenderStillOptions` + +```tsx twoslash +import type {RenderStillOptions} from '@remotion/renderer'; +// ^? +``` + +## `RenderFramesOptions` + +```tsx twoslash +import type {RenderFramesOptions} from '@remotion/renderer'; +// ^? +``` + +## `SelectCompositionOptions` + +```tsx twoslash +import type {SelectCompositionOptions} from '@remotion/renderer'; +// ^? +``` diff --git a/packages/docs/docusaurus-theme-shiki-twoslash/theme/CodeBlock/styles.css b/packages/docs/docusaurus-theme-shiki-twoslash/theme/CodeBlock/styles.css index a05bd93a495..199c93a8a78 100644 --- a/packages/docs/docusaurus-theme-shiki-twoslash/theme/CodeBlock/styles.css +++ b/packages/docs/docusaurus-theme-shiki-twoslash/theme/CodeBlock/styles.css @@ -79,6 +79,7 @@ pre.shiki .meta-line { pre.shiki > code { padding: var(--ifm-pre-padding) 0; display: block; + overflow: auto; } /* Also support old .code-container wrapper if rendererClassic produces it */ diff --git a/packages/docs/sidebars.ts b/packages/docs/sidebars.ts index 3b924af0a16..c3c363c4404 100644 --- a/packages/docs/sidebars.ts +++ b/packages/docs/sidebars.ts @@ -555,6 +555,7 @@ const sidebars: SidebarsConfig = { 'renderer/get-silent-parts', 'renderer/combine-chunks', 'renderer/extract-audio', + 'renderer/types', ], }, { diff --git a/packages/docs/src/components/TsType.tsx b/packages/docs/src/components/TsType.tsx index 24f247caf00..6a0bb11561c 100644 --- a/packages/docs/src/components/TsType.tsx +++ b/packages/docs/src/components/TsType.tsx @@ -3,27 +3,51 @@ import React from 'react'; export const TsType: React.FC<{ readonly type: string; readonly source: string; -}> = ({type, source}) => { + readonly href?: string; +}> = ({type, source, href}) => { if (!type || !source) { throw new Error('type and source are required'); } + const icon = ( + + + + ); + + const content = ( + <> + {icon} + {type} + + ); + + if (href) { + return ( + + {content} + + ); + } + return ( - - - - {type} + {content} ); }; diff --git a/packages/docs/src/css/custom.css b/packages/docs/src/css/custom.css index f42f2f91ce7..1b0e0d391f5 100644 --- a/packages/docs/src/css/custom.css +++ b/packages/docs/src/css/custom.css @@ -148,6 +148,15 @@ code { text-decoration: underline; } +.ts-type-link { + text-decoration: none !important; + text-underline-position: under; +} + +.ts-type-link:hover { + text-decoration: underline !important; +} + .no-scroll-bar::-webkit-scrollbar { display: none; } diff --git a/packages/docs/src/data/articles.ts b/packages/docs/src/data/articles.ts index 4c017c8e851..920d0806133 100644 --- a/packages/docs/src/data/articles.ts +++ b/packages/docs/src/data/articles.ts @@ -5115,6 +5115,15 @@ export const articles = [ noAi: false, slug: 'renderer/stitch-frames-to-video', }, + { + id: 'types', + title: 'TypeScript Types Reference', + relativePath: 'docs/renderer/types.mdx', + compId: 'articles-docs-renderer-types', + crumb: '@remotion/renderer', + noAi: false, + slug: 'renderer/types', + }, { id: 'resources', title: 'List of resources', diff --git a/packages/docs/static/generated/articles-docs-renderer-types.png b/packages/docs/static/generated/articles-docs-renderer-types.png new file mode 100644 index 00000000000..658e4644208 Binary files /dev/null and b/packages/docs/static/generated/articles-docs-renderer-types.png differ diff --git a/packages/renderer/src/prespawn-ffmpeg.ts b/packages/renderer/src/prespawn-ffmpeg.ts index 9706d2db43d..5382f938742 100644 --- a/packages/renderer/src/prespawn-ffmpeg.ts +++ b/packages/renderer/src/prespawn-ffmpeg.ts @@ -32,6 +32,7 @@ type RunningStatus = | { type: 'quit-with-error'; exitCode: number; + signal: NodeJS.Signals | null; stderr: string; }; @@ -164,11 +165,12 @@ export const prespawnFfmpeg = (options: PreStitcherOptions) => { type: 'running', }; - task.on('exit', (code) => { - if (typeof code === 'number' && code > 0) { + task.on('exit', (code, signal) => { + if ((typeof code === 'number' && code > 0) || signal) { exitCode = { type: 'quit-with-error', - exitCode: code, + exitCode: code ?? 1, + signal: signal ?? null, stderr: ffmpegOutput, }; } else { diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts index c0f6b648197..ff84670a0c4 100644 --- a/packages/renderer/src/render-frames.ts +++ b/packages/renderer/src/render-frames.ts @@ -157,47 +157,53 @@ export type FrameAndAssets = { inlineAudioAssets: InlineAudioAsset[]; }; -export type RenderFramesOptions = { - onStart: (data: OnStartData) => void; - onFrameUpdate: ( - framesRendered: number, - frameIndex: number, - timeToRenderInMilliseconds: number, - ) => void; - outputDir: string | null; - inputProps: Record; - envVariables?: Record; - imageFormat?: VideoImageFormat; - /** - * @deprecated Renamed to "jpegQuality" - */ - quality?: never; - frameRange?: FrameRange | null; - everyNthFrame?: number; - /** - * @deprecated Use "logLevel": "verbose" instead - */ - dumpBrowserLogs?: boolean; - /** - * @deprecated Use "logLevel" instead - */ - verbose?: boolean; - puppeteerInstance?: HeadlessBrowser; - browserExecutable?: BrowserExecutable; - onBrowserLog?: (log: BrowserLog) => void; - onFrameBuffer?: (buffer: Buffer, frame: number) => void; - onDownload?: RenderMediaOnDownload; - timeoutInMilliseconds?: number; - chromiumOptions?: ChromiumOptions; - scale?: number; - port?: number | null; - cancelSignal?: CancelSignal; - composition: VideoConfig; - muted?: boolean; - concurrency?: number | string | null; - onArtifact?: OnArtifact | null; - serveUrl: string; -} & Partial>; +type Prettify = { + [K in keyof T]: T[K]; +} & {}; + +export type RenderFramesOptions = Prettify< + { + onStart: (data: OnStartData) => void; + onFrameUpdate: ( + framesRendered: number, + frameIndex: number, + timeToRenderInMilliseconds: number, + ) => void; + outputDir: string | null; + inputProps: Record; + envVariables?: Record; + imageFormat?: VideoImageFormat; + /** + * @deprecated Renamed to "jpegQuality" + */ + quality?: never; + frameRange?: FrameRange | null; + everyNthFrame?: number; + /** + * @deprecated Use "logLevel": "verbose" instead + */ + dumpBrowserLogs?: boolean; + /** + * @deprecated Use "logLevel" instead + */ + verbose?: boolean; + puppeteerInstance?: HeadlessBrowser; + browserExecutable?: BrowserExecutable; + onBrowserLog?: (log: BrowserLog) => void; + onFrameBuffer?: (buffer: Buffer, frame: number) => void; + onDownload?: RenderMediaOnDownload; + timeoutInMilliseconds?: number; + chromiumOptions?: ChromiumOptions; + scale?: number; + port?: number | null; + cancelSignal?: CancelSignal; + composition: VideoConfig; + muted?: boolean; + concurrency?: number | string | null; + onArtifact?: OnArtifact | null; + serveUrl: string; + } & Partial> +>; const innerRenderFrames = async ({ onFrameUpdate, diff --git a/packages/renderer/src/render-media.ts b/packages/renderer/src/render-media.ts index 960a54131cf..e43b8f2152f 100644 --- a/packages/renderer/src/render-media.ts +++ b/packages/renderer/src/render-media.ts @@ -696,13 +696,13 @@ const internalRenderMediaRaw = ({ const exitStatus = preStitcher?.getExitStatus(); if (exitStatus?.type === 'quit-successfully') { throw new Error( - `FFmpeg already quit while trying to pipe frame ${frame} to it. Stderr: ${exitStatus.stderr}}`, + `FFmpeg already quit while trying to pipe frame ${frame} to it. Stderr: ${exitStatus.stderr}`, ); } if (exitStatus?.type === 'quit-with-error') { throw new Error( - `FFmpeg quit with code ${exitStatus.exitCode} while piping frame ${frame}. Stderr: ${exitStatus.stderr}}`, + `FFmpeg quit with code ${exitStatus.exitCode}${exitStatus.signal ? ` (signal ${exitStatus.signal})` : ''} while piping frame ${frame}. Stderr: ${exitStatus.stderr}`, ); } diff --git a/packages/renderer/src/select-composition.ts b/packages/renderer/src/select-composition.ts index 1726708b59d..d6906fcad4f 100644 --- a/packages/renderer/src/select-composition.ts +++ b/packages/renderer/src/select-composition.ts @@ -39,20 +39,26 @@ type InternalSelectCompositionsConfig = { onServeUrlVisited: () => void; } & ToOptions; -export type SelectCompositionOptions = RequiredInputPropsInV5 & { - envVariables?: Record; - puppeteerInstance?: HeadlessBrowser; - onBrowserLog?: (log: BrowserLog) => void; - browserExecutable?: BrowserExecutable; - chromiumOptions?: ChromiumOptions; - port?: number | null; - /** - * @deprecated Use `logLevel` instead. - */ - verbose?: boolean; - serveUrl: string; - id: string; -} & Partial>; +type Prettify = { + [K in keyof T]: T[K]; +} & {}; + +export type SelectCompositionOptions = Prettify< + RequiredInputPropsInV5 & { + envVariables?: Record; + puppeteerInstance?: HeadlessBrowser; + onBrowserLog?: (log: BrowserLog) => void; + browserExecutable?: BrowserExecutable; + chromiumOptions?: ChromiumOptions; + port?: number | null; + /** + * @deprecated Use `logLevel` instead. + */ + verbose?: boolean; + serveUrl: string; + id: string; + } & Partial> +>; type CleanupFn = () => Promise;