@remotion/renderer 4.0.0-preload.17 → 4.0.0-spawn.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { TAsset } from 'remotion';
2
2
  export declare type RenderMediaOnDownload = (src: string) => ((progress: {
3
3
  percent: number;
4
4
  }) => void) | undefined | void;
5
- export declare const waitForAssetToBeDownloaded: (src: string) => Promise<string>;
5
+ export declare const waitForAssetToBeDownloaded: (src: string, to: string) => Promise<void>;
6
6
  export declare const markAllAssetsAsDownloaded: () => void;
7
7
  export declare const getSanitizedFilenameForAssetUrl: ({ src, downloadDir, }: {
8
8
  src: string;
@@ -13,25 +13,38 @@ const sanitize_filepath_1 = require("./sanitize-filepath");
13
13
  const isDownloadingMap = {};
14
14
  const hasBeenDownloadedMap = {};
15
15
  const listeners = {};
16
- const waitForAssetToBeDownloaded = (src) => {
17
- if (hasBeenDownloadedMap[src]) {
18
- return Promise.resolve(hasBeenDownloadedMap[src]);
16
+ const waitForAssetToBeDownloaded = (src, to) => {
17
+ var _a;
18
+ if ((_a = hasBeenDownloadedMap[src]) === null || _a === void 0 ? void 0 : _a[to]) {
19
+ return Promise.resolve();
19
20
  }
20
21
  if (!listeners[src]) {
21
- listeners[src] = [];
22
+ listeners[src] = {};
23
+ }
24
+ if (!listeners[src][to]) {
25
+ listeners[src][to] = [];
22
26
  }
23
27
  return new Promise((resolve) => {
24
- listeners[src].push((to) => resolve(to));
28
+ listeners[src][to].push(() => resolve());
25
29
  });
26
30
  };
27
31
  exports.waitForAssetToBeDownloaded = waitForAssetToBeDownloaded;
28
32
  const notifyAssetIsDownloaded = (src, to) => {
29
33
  if (!listeners[src]) {
30
- listeners[src] = [];
34
+ listeners[src] = {};
35
+ }
36
+ if (!listeners[src][to]) {
37
+ listeners[src][to] = [];
38
+ }
39
+ listeners[src][to].forEach((fn) => fn());
40
+ if (!isDownloadingMap[src]) {
41
+ isDownloadingMap[src] = {};
31
42
  }
32
- listeners[src].forEach((fn) => fn(to));
33
- isDownloadingMap[src] = false;
34
- hasBeenDownloadedMap[src] = to;
43
+ isDownloadingMap[src][to] = false;
44
+ if (!hasBeenDownloadedMap[src]) {
45
+ hasBeenDownloadedMap[src] = {};
46
+ }
47
+ hasBeenDownloadedMap[src][to] = true;
35
48
  };
36
49
  const validateMimeType = (mimeType, src) => {
37
50
  if (!mimeType.includes('/')) {
@@ -73,13 +86,17 @@ function validateBufferEncoding(potentialEncoding, dataUrl) {
73
86
  }
74
87
  }
75
88
  const downloadAsset = async (src, to, onDownload) => {
76
- if (hasBeenDownloadedMap[src]) {
89
+ var _a, _b;
90
+ if ((_a = hasBeenDownloadedMap[src]) === null || _a === void 0 ? void 0 : _a[to]) {
77
91
  return;
78
92
  }
79
- if (isDownloadingMap[src]) {
80
- return (0, exports.waitForAssetToBeDownloaded)(src);
93
+ if ((_b = isDownloadingMap[src]) === null || _b === void 0 ? void 0 : _b[to]) {
94
+ return (0, exports.waitForAssetToBeDownloaded)(src, to);
95
+ }
96
+ if (!isDownloadingMap[src]) {
97
+ isDownloadingMap[src] = {};
81
98
  }
82
- isDownloadingMap[src] = true;
99
+ isDownloadingMap[src][to] = true;
83
100
  const onProgress = onDownload(src);
84
101
  (0, ensure_output_directory_1.ensureOutputDirectory)(to);
85
102
  if (src.startsWith('data:')) {
@@ -1,6 +1,7 @@
1
1
  import { openBrowser } from './open-browser';
2
2
  declare type Await<T> = T extends PromiseLike<infer U> ? U : T;
3
- export declare const cycleBrowserTabs: (puppeteerInstance: Await<ReturnType<typeof openBrowser>>, concurrency: number) => {
3
+ declare type Browser = ReturnType<typeof openBrowser>;
4
+ export declare const cycleBrowserTabs: (puppeteerInstance: Browser | Await<Browser>, concurrency: number) => {
4
5
  stopCycling: () => void;
5
6
  };
6
7
  export {};
@@ -11,8 +11,8 @@ const cycleBrowserTabs = (puppeteerInstance, concurrency) => {
11
11
  let i = 0;
12
12
  const set = () => {
13
13
  interval = setTimeout(() => {
14
- puppeteerInstance
15
- .pages()
14
+ Promise.resolve(puppeteerInstance)
15
+ .then((instance) => instance.pages())
16
16
  .then((pages) => {
17
17
  var _a, _b;
18
18
  const currentPage = pages[i % pages.length];
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { FfmpegExecutable } from 'remotion';
3
4
  import { Readable } from 'stream';
4
5
  export declare function streamToString(stream: Readable): Promise<string>;
@@ -13,5 +13,5 @@ declare type GetCompositionsConfig = {
13
13
  ffmpegExecutable?: FfmpegExecutable;
14
14
  port?: number | null;
15
15
  };
16
- export declare const getCompositions: (serveUrlOrWebpackUrl: string, config?: GetCompositionsConfig | undefined) => Promise<TCompMetadata[]>;
16
+ export declare const getCompositions: (serveUrlOrWebpackUrl: string, config?: GetCompositionsConfig) => Promise<TCompMetadata[]>;
17
17
  export {};
@@ -28,6 +28,7 @@ const innerGetCompositions = async (serveUrl, page, config, proxyPort) => {
28
28
  initialFrame: 0,
29
29
  timeoutInMilliseconds: config === null || config === void 0 ? void 0 : config.timeoutInMilliseconds,
30
30
  proxyPort,
31
+ retriesRemaining: 2,
31
32
  });
32
33
  await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
33
34
  page,
package/dist/index.d.ts CHANGED
@@ -51,7 +51,6 @@ export declare const RenderInternals: {
51
51
  getLogs: () => string;
52
52
  }>;
53
53
  getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "mp4" | "mkv" | "mov" | "webm";
54
- makeAssetsDownloadTmpDir: () => string;
55
54
  tmpDir: (str: string) => string;
56
55
  deleteDirectory: (directory: string) => Promise<void>;
57
56
  isServeUrl: (potentialUrl: string) => boolean;
package/dist/index.js CHANGED
@@ -14,7 +14,6 @@ const get_extension_of_filename_1 = require("./get-extension-of-filename");
14
14
  const get_frame_to_render_1 = require("./get-frame-to-render");
15
15
  const get_local_browser_executable_1 = require("./get-local-browser-executable");
16
16
  const is_serve_url_1 = require("./is-serve-url");
17
- const make_assets_download_dir_1 = require("./make-assets-download-dir");
18
17
  const normalize_serve_url_1 = require("./normalize-serve-url");
19
18
  const open_browser_1 = require("./open-browser");
20
19
  const parse_browser_error_stack_1 = require("./parse-browser-error-stack");
@@ -54,7 +53,6 @@ exports.RenderInternals = {
54
53
  normalizeServeUrl: normalize_serve_url_1.normalizeServeUrl,
55
54
  spawnFfmpeg: stitch_frames_to_video_1.spawnFfmpeg,
56
55
  getFileExtensionFromCodec: get_extension_from_codec_1.getFileExtensionFromCodec,
57
- makeAssetsDownloadTmpDir: make_assets_download_dir_1.makeAssetsDownloadTmpDir,
58
56
  tmpDir: tmp_dir_1.tmpDir,
59
57
  deleteDirectory: delete_directory_1.deleteDirectory,
60
58
  isServeUrl: is_serve_url_1.isServeUrl,
@@ -30,8 +30,8 @@ const mergeAudioTrackUnlimited = async ({ ffmpegExecutable, outName, files, numb
30
30
  });
31
31
  return;
32
32
  }
33
- // FFMPEG has a limit of 64 tracks that can be merged at once
34
- if (files.length > 64) {
33
+ // In FFMPEG, the total number of left and right tracks that can be merged at one time is limited to 64
34
+ if (files.length >= 32) {
35
35
  const chunked = (0, chunk_1.chunk)(files, 10);
36
36
  const tempPath = (0, tmp_dir_1.tmpDir)('remotion-large-audio-mixing');
37
37
  const chunkNames = await Promise.all(chunked.map(async (chunkFiles, i) => {
@@ -35,14 +35,15 @@ const startOffthreadVideoServer = ({ ffmpegExecutable, downloadDir, onDownload,
35
35
  res.setHeader('access-control-allow-origin', '*');
36
36
  res.setHeader('content-type', 'image/jpg');
37
37
  const { src, time } = (0, exports.extractUrlAndSourceFromUrl)(req.url);
38
+ const to = (0, download_and_map_assets_to_file_1.getSanitizedFilenameForAssetUrl)({ downloadDir, src });
38
39
  (0, download_and_map_assets_to_file_1.startDownloadForSrc)({ src, downloadDir, onDownload }).catch((err) => {
39
40
  onError(new Error(`Error while downloading asset: ${err.stack}`));
40
41
  });
41
- (0, download_and_map_assets_to_file_1.waitForAssetToBeDownloaded)(src)
42
- .then((newSrc) => {
42
+ (0, download_and_map_assets_to_file_1.waitForAssetToBeDownloaded)(src, to)
43
+ .then(() => {
43
44
  return (0, extract_frame_from_video_1.extractFrameFromVideo)({
44
45
  time,
45
- src: newSrc,
46
+ src: to,
46
47
  ffmpegExecutable,
47
48
  });
48
49
  })
@@ -10,9 +10,9 @@ export declare type ChromiumOptions = {
10
10
  };
11
11
  export declare const killAllBrowsers: () => Promise<void>;
12
12
  export declare const openBrowser: (browser: Browser, options?: {
13
- shouldDumpIo?: boolean | undefined;
14
- browserExecutable?: string | null | undefined;
15
- chromiumOptions?: ChromiumOptions | undefined;
16
- forceDeviceScaleFactor?: number | undefined;
17
- } | undefined) => Promise<puppeteer.Browser>;
13
+ shouldDumpIo?: boolean;
14
+ browserExecutable?: string | null;
15
+ chromiumOptions?: ChromiumOptions;
16
+ forceDeviceScaleFactor?: number;
17
+ }) => Promise<puppeteer.Browser>;
18
18
  export {};
@@ -17,5 +17,6 @@ declare type PreSticherOptions = {
17
17
  export declare const prespawnFfmpeg: (options: PreSticherOptions) => Promise<{
18
18
  task: execa.ExecaChildProcess<string>;
19
19
  getLogs: () => string;
20
+ waitForSpawn: Promise<void>;
20
21
  }>;
21
22
  export {};
@@ -73,6 +73,9 @@ const prespawnFfmpeg = async (options) => {
73
73
  }
74
74
  const ffmpegString = ffmpegArgs.flat(2).filter(Boolean);
75
75
  const task = (0, execa_1.default)((_f = options.ffmpegExecutable) !== null && _f !== void 0 ? _f : 'ffmpeg', ffmpegString);
76
+ const waitForSpawn = new Promise((resolve) => {
77
+ task.on('spawn', () => resolve());
78
+ });
76
79
  let ffmpegOutput = '';
77
80
  (_g = task.stderr) === null || _g === void 0 ? void 0 : _g.on('data', (data) => {
78
81
  const str = data.toString();
@@ -84,6 +87,6 @@ const prespawnFfmpeg = async (options) => {
84
87
  }
85
88
  }
86
89
  });
87
- return { task, getLogs: () => ffmpegOutput };
90
+ return { task, getLogs: () => ffmpegOutput, waitForSpawn };
88
91
  };
89
92
  exports.prespawnFfmpeg = prespawnFfmpeg;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -36,5 +36,8 @@ declare type RenderFramesOptions = {
36
36
  ffmpegExecutable?: FfmpegExecutable;
37
37
  port?: number | null;
38
38
  } & ConfigOrComposition & ServeUrlOrWebpackBundle;
39
- export declare const renderFrames: (options: RenderFramesOptions) => Promise<RenderFramesOutput>;
39
+ declare type ReturnTypeWithCancel = Promise<RenderFramesOutput> & {
40
+ cancel: () => void;
41
+ };
42
+ export declare const renderFrames: (options: RenderFramesOptions) => ReturnTypeWithCancel;
40
43
  export {};
@@ -33,7 +33,12 @@ const getComposition = (others) => {
33
33
  }
34
34
  return undefined;
35
35
  };
36
- const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps, quality, imageFormat = image_format_1.DEFAULT_IMAGE_FORMAT, frameRange, puppeteerInstance, onError, envVariables, onBrowserLog, onFrameBuffer, onDownload, pagesArray, serveUrl, composition, timeoutInMilliseconds, scale, actualParallelism, downloadDir, proxyPort, }) => {
36
+ const getPool = async (pages) => {
37
+ const puppeteerPages = await Promise.all(pages);
38
+ const pool = new pool_1.Pool(puppeteerPages);
39
+ return pool;
40
+ };
41
+ const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, quality, imageFormat = image_format_1.DEFAULT_IMAGE_FORMAT, frameRange, puppeteerInstance, onError, envVariables, onBrowserLog, onFrameBuffer, onDownload, pagesArray, serveUrl, composition, timeoutInMilliseconds, scale, actualParallelism, downloadDir, proxyPort, }) => {
37
42
  if (!puppeteerInstance) {
38
43
  throw new Error('weird');
39
44
  }
@@ -77,6 +82,7 @@ const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps
77
82
  initialFrame,
78
83
  timeoutInMilliseconds,
79
84
  proxyPort,
85
+ retriesRemaining: 2,
80
86
  });
81
87
  await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
82
88
  pageFunction: (id) => {
@@ -92,23 +98,27 @@ const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps
92
98
  page.off('console', logCallback);
93
99
  return page;
94
100
  });
95
- const puppeteerPages = await Promise.all(pages);
96
- const pool = new pool_1.Pool(puppeteerPages);
97
101
  const [firstFrameIndex, lastFrameIndex] = realFrameRange;
98
102
  // Substract one because 100 frames will be 00-99
99
103
  // --> 2 digits
100
104
  const filePadLength = String(lastFrameIndex).length;
101
105
  let framesRendered = 0;
106
+ const poolPromise = getPool(pages);
102
107
  onStart({
103
108
  frameCount,
104
109
  });
105
110
  const assets = new Array(frameCount).fill(undefined);
106
- await Promise.all(new Array(frameCount)
111
+ let stopped = false;
112
+ const progress = Promise.all(new Array(frameCount)
107
113
  .fill(Boolean)
108
- .map((x, i) => i)
114
+ .map((_x, i) => i)
109
115
  .map(async (index) => {
110
116
  const frame = realFrameRange[0] + index;
117
+ const pool = await poolPromise;
111
118
  const freePage = await pool.acquire();
119
+ if (stopped) {
120
+ throw new Error('Render was stopped');
121
+ }
112
122
  const paddedIndex = String(frame).padStart(filePadLength, '0');
113
123
  const errorCallbackOnFrame = (err) => {
114
124
  onError(err);
@@ -175,18 +185,29 @@ const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps
175
185
  freePage.off('error', errorCallbackOnFrame);
176
186
  return compressedAssets;
177
187
  }));
178
- const returnValue = {
179
- assetsInfo: {
180
- assets,
181
- downloadDir,
182
- firstFrameIndex,
183
- imageSequenceName: `element-%0${filePadLength}d.${imageFormat}`,
188
+ const prom = Object.assign(new Promise((res, rej) => {
189
+ progress
190
+ .then(() => {
191
+ const returnValue = {
192
+ assetsInfo: {
193
+ assets,
194
+ downloadDir,
195
+ firstFrameIndex,
196
+ imageSequenceName: `element-%0${filePadLength}d.${imageFormat}`,
197
+ },
198
+ frameCount,
199
+ };
200
+ res(returnValue);
201
+ })
202
+ .catch((err) => rej(err));
203
+ }), {
204
+ cancel: () => {
205
+ stopped = true;
184
206
  },
185
- frameCount,
186
- };
187
- return returnValue;
207
+ });
208
+ return prom;
188
209
  };
189
- const renderFrames = async (options) => {
210
+ const renderFrames = (options) => {
190
211
  var _a, _b, _c, _d;
191
212
  const composition = getComposition(options);
192
213
  if (!composition) {
@@ -202,34 +223,38 @@ const renderFrames = async (options) => {
202
223
  const selectedServeUrl = (0, legacy_webpack_config_1.getServeUrlWithFallback)(options);
203
224
  remotion_1.Internals.validateQuality(options.quality);
204
225
  (0, validate_scale_1.validateScale)(options.scale);
205
- const browserInstance = (_a = options.puppeteerInstance) !== null && _a !== void 0 ? _a : (await (0, open_browser_1.openBrowser)(remotion_1.Internals.DEFAULT_BROWSER, {
226
+ const browserInstance = (_a = options.puppeteerInstance) !== null && _a !== void 0 ? _a : (0, open_browser_1.openBrowser)(remotion_1.Internals.DEFAULT_BROWSER, {
206
227
  shouldDumpIo: options.dumpBrowserLogs,
207
228
  browserExecutable: options.browserExecutable,
208
229
  chromiumOptions: options.chromiumOptions,
209
230
  forceDeviceScaleFactor: (_b = options.scale) !== null && _b !== void 0 ? _b : 1,
210
- }));
231
+ });
211
232
  const downloadDir = (0, make_assets_download_dir_1.makeAssetsDownloadTmpDir)();
212
233
  const onDownload = (_c = options.onDownload) !== null && _c !== void 0 ? _c : (() => () => undefined);
213
234
  const actualParallelism = (0, get_concurrency_1.getActualConcurrency)((_d = options.parallelism) !== null && _d !== void 0 ? _d : null);
214
235
  const { stopCycling } = (0, cycle_browser_tabs_1.cycleBrowserTabs)(browserInstance, actualParallelism);
215
236
  const openedPages = [];
216
- return new Promise((resolve, reject) => {
237
+ let cancel = () => undefined;
238
+ const prom = new Promise((resolve, reject) => {
217
239
  var _a, _b;
218
240
  let cleanup = null;
219
241
  const onError = (err) => reject(err);
220
- (0, prepare_server_1.prepareServer)({
221
- webpackConfigOrServeUrl: selectedServeUrl,
222
- downloadDir,
223
- onDownload,
224
- onError,
225
- ffmpegExecutable: (_a = options.ffmpegExecutable) !== null && _a !== void 0 ? _a : null,
226
- port: (_b = options.port) !== null && _b !== void 0 ? _b : null,
227
- })
228
- .then(({ serveUrl, closeServer, offthreadPort }) => {
242
+ Promise.all([
243
+ (0, prepare_server_1.prepareServer)({
244
+ webpackConfigOrServeUrl: selectedServeUrl,
245
+ downloadDir,
246
+ onDownload,
247
+ onError,
248
+ ffmpegExecutable: (_a = options.ffmpegExecutable) !== null && _a !== void 0 ? _a : null,
249
+ port: (_b = options.port) !== null && _b !== void 0 ? _b : null,
250
+ }),
251
+ browserInstance,
252
+ ])
253
+ .then(([{ serveUrl, closeServer, offthreadPort }, puppeteerInstance]) => {
229
254
  cleanup = closeServer;
230
- return innerRenderFrames({
255
+ const renderFramesProm = innerRenderFrames({
231
256
  ...options,
232
- puppeteerInstance: browserInstance,
257
+ puppeteerInstance,
233
258
  onError,
234
259
  pagesArray: openedPages,
235
260
  serveUrl,
@@ -239,6 +264,10 @@ const renderFrames = async (options) => {
239
264
  downloadDir,
240
265
  proxyPort: offthreadPort,
241
266
  });
267
+ cancel = () => {
268
+ renderFramesProm.cancel();
269
+ };
270
+ return renderFramesProm;
242
271
  })
243
272
  .then((res) => resolve(res))
244
273
  .catch((err) => reject(err))
@@ -252,7 +281,11 @@ const renderFrames = async (options) => {
252
281
  });
253
282
  }
254
283
  else {
255
- browserInstance.close().catch((err) => {
284
+ Promise.resolve(browserInstance)
285
+ .then((puppeteerInstance) => {
286
+ return puppeteerInstance.close();
287
+ })
288
+ .catch((err) => {
256
289
  console.log('Unable to close browser', err);
257
290
  });
258
291
  }
@@ -260,5 +293,11 @@ const renderFrames = async (options) => {
260
293
  cleanup === null || cleanup === void 0 ? void 0 : cleanup();
261
294
  });
262
295
  });
296
+ const returnType = Object.assign(prom, {
297
+ cancel: () => {
298
+ cancel();
299
+ },
300
+ });
301
+ return returnType;
263
302
  };
264
303
  exports.renderFrames = renderFrames;
@@ -40,4 +40,8 @@ export declare type RenderMediaOptions = {
40
40
  port?: number | null;
41
41
  browserExecutable?: BrowserExecutable;
42
42
  } & ServeUrlOrWebpackBundle;
43
- export declare const renderMedia: ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, ...options }: RenderMediaOptions) => Promise<void>;
43
+ declare type RenderMediaReturnType = Promise<void> & {
44
+ cancel: () => void;
45
+ };
46
+ export declare const renderMedia: (options: RenderMediaOptions) => RenderMediaReturnType;
47
+ export {};
@@ -23,7 +23,22 @@ const tmp_dir_1 = require("./tmp-dir");
23
23
  const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
24
24
  const validate_output_filename_1 = require("./validate-output-filename");
25
25
  const validate_scale_1 = require("./validate-scale");
26
- const renderMedia = async ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, ...options }) => {
26
+ const renderMedia = (options) => {
27
+ let resolve = () => undefined;
28
+ let reject = () => undefined;
29
+ const rejectIfCancel = new Promise((res, rej) => {
30
+ resolve = res;
31
+ reject = rej;
32
+ });
33
+ const prom = innerRenderMedia({ ...options, rejectIfCancel }).then(() => {
34
+ resolve();
35
+ });
36
+ return Object.assign(prom, {
37
+ cancel: () => reject(new Error('Render got cancelled')),
38
+ });
39
+ };
40
+ exports.renderMedia = renderMedia;
41
+ const innerRenderMedia = async ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, rejectIfCancel, ...options }) => {
27
42
  var _a;
28
43
  remotion_1.Internals.validateQuality(quality);
29
44
  if (typeof crf !== 'undefined' && crf !== null) {
@@ -39,6 +54,14 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
39
54
  let renderedFrames = 0;
40
55
  let renderedDoneIn = null;
41
56
  let encodedDoneIn = null;
57
+ let cancelRenderFrames = () => undefined;
58
+ rejectIfCancel
59
+ .then(() => undefined)
60
+ .catch((err) => {
61
+ preStitcher === null || preStitcher === void 0 ? void 0 : preStitcher.task.kill();
62
+ cancelRenderFrames();
63
+ throw err;
64
+ });
42
65
  const renderStart = Date.now();
43
66
  const tmpdir = (0, tmp_dir_1.tmpDir)('pre-encode');
44
67
  const parallelEncoding = (0, can_use_parallel_encoding_1.canUseParallelEncoding)(codec);
@@ -87,7 +110,7 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
87
110
  }
88
111
  const realFrameRange = (0, get_frame_to_render_1.getRealFrameRange)(composition.durationInFrames, frameRange !== null && frameRange !== void 0 ? frameRange : null);
89
112
  const { waitForRightTimeOfFrameToBeInserted, setFrameToStitch, waitForFinish, } = (0, ensure_frames_in_order_1.ensureFramesInOrder)(realFrameRange);
90
- const { assetsInfo } = await (0, render_frames_1.renderFrames)({
113
+ const renderFramesProc = (0, render_frames_1.renderFrames)({
91
114
  config: composition,
92
115
  onFrameUpdate: (frame) => {
93
116
  renderedFrames = frame;
@@ -125,6 +148,10 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
125
148
  browserExecutable,
126
149
  port,
127
150
  });
151
+ cancelRenderFrames = () => {
152
+ renderFramesProc.cancel();
153
+ };
154
+ const { assetsInfo } = await renderFramesProc;
128
155
  if (stitcherFfmpeg) {
129
156
  await waitForFinish();
130
157
  (_a = stitcherFfmpeg === null || stitcherFfmpeg === void 0 ? void 0 : stitcherFfmpeg.stdin) === null || _a === void 0 ? void 0 : _a.end();
@@ -168,6 +195,26 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
168
195
  encodedDoneIn = Date.now() - stitchStart;
169
196
  callUpdate();
170
197
  }
198
+ catch (err) {
199
+ /**
200
+ * When an error is thrown in renderFrames(...) (e.g., when delayRender() is used incorrectly), fs.unlinkSync(...) throws an error that the file is locked because ffmpeg is still running, and renderMedia returns it.
201
+ * Therefore we first kill the FFMPEG process before deleting the file
202
+ */
203
+ cancelRenderFrames();
204
+ if (stitcherFfmpeg !== undefined && stitcherFfmpeg.exitCode === null) {
205
+ const promise = new Promise((resolve) => {
206
+ setTimeout(() => {
207
+ resolve();
208
+ }, 2000);
209
+ stitcherFfmpeg.on('close', resolve);
210
+ });
211
+ // Can only kill the process once it has spawned, otherwise getting EPIPE error in Node.JS
212
+ await (preStitcher === null || preStitcher === void 0 ? void 0 : preStitcher.waitForSpawn);
213
+ stitcherFfmpeg.kill();
214
+ await promise;
215
+ }
216
+ throw err;
217
+ }
171
218
  finally {
172
219
  if (preEncodedFileLocation !== null &&
173
220
  fs_1.default.existsSync(preEncodedFileLocation)) {
@@ -175,4 +222,3 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
175
222
  }
176
223
  }
177
224
  };
178
- exports.renderMedia = renderMedia;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -105,6 +109,7 @@ const innerRenderStill = async ({ composition, quality, imageFormat = 'png', ser
105
109
  initialFrame: frame,
106
110
  timeoutInMilliseconds,
107
111
  proxyPort,
112
+ retriesRemaining: 2,
108
113
  });
109
114
  await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
110
115
  pageFunction: (id) => {
@@ -1,5 +1,5 @@
1
1
  import { Page } from 'puppeteer-core';
2
- export declare const setPropsAndEnv: ({ inputProps, envVariables, page, serveUrl, initialFrame, timeoutInMilliseconds, proxyPort, }: {
2
+ export declare const setPropsAndEnv: ({ inputProps, envVariables, page, serveUrl, initialFrame, timeoutInMilliseconds, proxyPort, retriesRemaining, }: {
3
3
  inputProps: unknown;
4
4
  envVariables: Record<string, string> | undefined;
5
5
  page: Page;
@@ -7,4 +7,5 @@ export declare const setPropsAndEnv: ({ inputProps, envVariables, page, serveUrl
7
7
  initialFrame: number;
8
8
  timeoutInMilliseconds: number | undefined;
9
9
  proxyPort: number;
10
+ retriesRemaining: number;
10
11
  }) => Promise<void>;
@@ -5,7 +5,7 @@ const remotion_1 = require("remotion");
5
5
  const normalize_serve_url_1 = require("./normalize-serve-url");
6
6
  const puppeteer_evaluate_1 = require("./puppeteer-evaluate");
7
7
  const validate_puppeteer_timeout_1 = require("./validate-puppeteer-timeout");
8
- const setPropsAndEnv = async ({ inputProps, envVariables, page, serveUrl, initialFrame, timeoutInMilliseconds, proxyPort, }) => {
8
+ const setPropsAndEnv = async ({ inputProps, envVariables, page, serveUrl, initialFrame, timeoutInMilliseconds, proxyPort, retriesRemaining, }) => {
9
9
  (0, validate_puppeteer_timeout_1.validatePuppeteerTimeout)(timeoutInMilliseconds);
10
10
  const actualTimeout = timeoutInMilliseconds !== null && timeoutInMilliseconds !== void 0 ? timeoutInMilliseconds : remotion_1.Internals.DEFAULT_PUPPETEER_TIMEOUT;
11
11
  page.setDefaultTimeout(actualTimeout);
@@ -32,6 +32,25 @@ const setPropsAndEnv = async ({ inputProps, envVariables, page, serveUrl, initia
32
32
  }, [proxyPort]);
33
33
  const pageRes = await page.goto(urlToVisit);
34
34
  const status = pageRes.status();
35
+ // S3 in rare occasions returns a 500 or 503 error code for GET operations.
36
+ // Usually it is fixed by retrying.
37
+ if (status >= 500 && status <= 504 && retriesRemaining > 0) {
38
+ await new Promise((resolve) => {
39
+ setTimeout(() => {
40
+ resolve();
41
+ }, 2000);
42
+ });
43
+ return (0, exports.setPropsAndEnv)({
44
+ envVariables,
45
+ initialFrame,
46
+ inputProps,
47
+ page,
48
+ proxyPort,
49
+ retriesRemaining: retriesRemaining - 1,
50
+ serveUrl,
51
+ timeoutInMilliseconds,
52
+ });
53
+ }
35
54
  if (status !== 200 &&
36
55
  status !== 301 &&
37
56
  status !== 302 &&
package/dist/tmp-dir.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/renderer",
3
- "version": "4.0.0-preload.17+14cd6033f",
3
+ "version": "4.0.0-spawn.14+243a2f006",
4
4
  "description": "Renderer for Remotion",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "execa": "5.1.1",
24
24
  "puppeteer-core": "13.5.1",
25
- "remotion": "4.0.0-preload.17+14cd6033f",
25
+ "remotion": "4.0.0-spawn.14+243a2f006",
26
26
  "serve-handler": "6.1.3",
27
27
  "source-map": "^0.8.0-beta.0"
28
28
  },
@@ -46,7 +46,7 @@
46
46
  "react": "18.0.0",
47
47
  "react-dom": "18.0.0",
48
48
  "ts-jest": "^27.0.5",
49
- "typescript": "^4.5.5"
49
+ "typescript": "^4.7.0"
50
50
  },
51
51
  "keywords": [
52
52
  "remotion",
@@ -59,5 +59,5 @@
59
59
  "publishConfig": {
60
60
  "access": "public"
61
61
  },
62
- "gitHead": "14cd6033f4ad0fa75af142d54a4cd1a23aff3f21"
62
+ "gitHead": "243a2f0067d75e193a6a45f491987be53208f3e0"
63
63
  }