@remotion/renderer 4.0.277 → 4.0.279

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.
@@ -1,21 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
2
  var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
3
  var __export = (target, all) => {
20
4
  for (var name in all)
21
5
  __defProp(target, name, {
@@ -25,53 +9,8 @@ var __export = (target, all) => {
25
9
  set: (newValue) => all[name] = () => newValue
26
10
  });
27
11
  };
28
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
12
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
13
 
31
- // ../../node_modules/.pnpm/react@19.0.0/node_modules/react/cjs/react-jsx-runtime.production.js
32
- var exports_react_jsx_runtime_production = {};
33
- __export(exports_react_jsx_runtime_production, {
34
- jsxs: () => $jsxs,
35
- jsx: () => $jsx,
36
- Fragment: () => $Fragment
37
- });
38
- function jsxProd(type, config, maybeKey) {
39
- var key = null;
40
- maybeKey !== undefined && (key = "" + maybeKey);
41
- config.key !== undefined && (key = "" + config.key);
42
- if ("key" in config) {
43
- maybeKey = {};
44
- for (var propName in config)
45
- propName !== "key" && (maybeKey[propName] = config[propName]);
46
- } else
47
- maybeKey = config;
48
- config = maybeKey.ref;
49
- return {
50
- $$typeof: REACT_ELEMENT_TYPE,
51
- type,
52
- key,
53
- ref: config !== undefined ? config : null,
54
- props: maybeKey
55
- };
56
- }
57
- var REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, $Fragment, $jsx, $jsxs;
58
- var init_react_jsx_runtime_production = __esm(() => {
59
- REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
60
- REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
61
- $Fragment = REACT_FRAGMENT_TYPE;
62
- $jsx = jsxProd;
63
- $jsxs = jsxProd;
64
- });
65
-
66
- // ../../node_modules/.pnpm/react@19.0.0/node_modules/react/jsx-runtime.js
67
- var require_jsx_runtime = __commonJS((exports, module) => {
68
- init_react_jsx_runtime_production();
69
- if (true) {
70
- module.exports = exports_react_jsx_runtime_production;
71
- } else {
72
- }
73
- });
74
-
75
14
  // src/index.ts
76
15
  import execa2 from "execa";
77
16
 
@@ -4120,526 +4059,376 @@ var validCodecs = [
4120
4059
  ];
4121
4060
  var DEFAULT_CODEC = "h264";
4122
4061
 
4123
- // src/combine-videos.ts
4124
- import { rmSync as rmSync4 } from "node:fs";
4125
- import { join as join4 } from "node:path";
4126
-
4127
- // src/combine-audio.ts
4128
- import { rmSync as rmSync2, writeFileSync } from "fs";
4129
- import { join as join2 } from "path";
4130
- import { VERSION as VERSION2 } from "remotion/version";
4131
-
4132
- // src/options/separate-audio.tsx
4133
- var DEFAULT = null;
4134
- var cliFlag = "separate-audio-to";
4135
- var separateAudioOption = {
4136
- cliFlag,
4137
- description: () => `If set, the audio will not be included in the main output but rendered as a separate file at the location you pass. It is recommended to use an absolute path. If a relative path is passed, it is relative to the Remotion Root.`,
4138
- docLink: "https://remotion.dev/docs/renderer/render-media",
4139
- getValue: ({ commandLine }) => {
4140
- if (commandLine[cliFlag]) {
4141
- return {
4142
- source: "cli",
4143
- value: commandLine[cliFlag]
4144
- };
4145
- }
4146
- return {
4147
- source: "default",
4148
- value: DEFAULT
4149
- };
4150
- },
4151
- name: "Separate audio to",
4152
- setConfig: () => {
4153
- throw new Error("Not implemented");
4154
- },
4155
- ssrName: "separateAudioTo",
4156
- type: "string"
4062
+ // src/convert-to-positive-frame-index.ts
4063
+ var convertToPositiveFrameIndex = ({
4064
+ frame,
4065
+ durationInFrames
4066
+ }) => {
4067
+ return frame < 0 ? durationInFrames - frame : frame;
4157
4068
  };
4158
4069
 
4159
- // src/options/audio-codec.tsx
4160
- var validAudioCodecs = ["pcm-16", "aac", "mp3", "opus"];
4161
- var supportedAudioCodecs = {
4162
- h264: ["aac", "pcm-16", "mp3"],
4163
- "h264-mkv": ["pcm-16", "mp3"],
4164
- "h264-ts": ["pcm-16", "aac"],
4165
- aac: ["aac", "pcm-16"],
4166
- avi: [],
4167
- gif: [],
4168
- h265: ["aac", "pcm-16"],
4169
- mp3: ["mp3", "pcm-16"],
4170
- prores: ["aac", "pcm-16"],
4171
- vp8: ["opus", "pcm-16"],
4172
- vp9: ["opus", "pcm-16"],
4173
- wav: ["pcm-16"]
4174
- };
4175
- var _satisfies = supportedAudioCodecs;
4176
- if (_satisfies) {
4177
- }
4178
- var mapAudioCodecToFfmpegAudioCodecName = (audioCodec) => {
4179
- if (audioCodec === "aac") {
4180
- return "libfdk_aac";
4181
- }
4182
- if (audioCodec === "mp3") {
4183
- return "libmp3lame";
4184
- }
4185
- if (audioCodec === "opus") {
4186
- return "libopus";
4187
- }
4188
- if (audioCodec === "pcm-16") {
4189
- return "pcm_s16le";
4070
+ // src/symbolicate-stacktrace.ts
4071
+ import { readFileSync } from "fs";
4072
+ import path6 from "path";
4073
+ import { SourceMapConsumer } from "source-map";
4074
+ function extractSourceMapUrl(fileContents) {
4075
+ const regex = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/gm;
4076
+ let match = null;
4077
+ for (;; ) {
4078
+ const next = regex.exec(fileContents);
4079
+ if (next === null || next === undefined) {
4080
+ break;
4081
+ }
4082
+ match = next;
4190
4083
  }
4191
- throw new Error("unknown audio codec: " + audioCodec);
4192
- };
4193
- var cliFlag2 = "audio-codec";
4194
- var ssrName = "audioCodec";
4195
- var defaultAudioCodecs = {
4196
- "h264-mkv": {
4197
- lossless: "pcm-16",
4198
- compressed: "pcm-16"
4199
- },
4200
- "h264-ts": {
4201
- lossless: "pcm-16",
4202
- compressed: "aac"
4203
- },
4204
- aac: {
4205
- lossless: "pcm-16",
4206
- compressed: "aac"
4207
- },
4208
- gif: {
4209
- lossless: null,
4210
- compressed: null
4211
- },
4212
- h264: {
4213
- lossless: "pcm-16",
4214
- compressed: "aac"
4215
- },
4216
- h265: {
4217
- lossless: "pcm-16",
4218
- compressed: "aac"
4219
- },
4220
- mp3: {
4221
- lossless: "pcm-16",
4222
- compressed: "mp3"
4223
- },
4224
- prores: {
4225
- lossless: "pcm-16",
4226
- compressed: "pcm-16"
4227
- },
4228
- vp8: {
4229
- lossless: "pcm-16",
4230
- compressed: "opus"
4231
- },
4232
- vp9: {
4233
- lossless: "pcm-16",
4234
- compressed: "opus"
4235
- },
4236
- wav: {
4237
- lossless: "pcm-16",
4238
- compressed: "pcm-16"
4084
+ if (!match?.[1]) {
4085
+ return null;
4239
4086
  }
4240
- };
4241
- var extensionMap = {
4242
- aac: "aac",
4243
- mp3: "mp3",
4244
- opus: "opus",
4245
- "pcm-16": "wav"
4246
- };
4247
- var getExtensionFromAudioCodec = (audioCodec) => {
4248
- if (extensionMap[audioCodec]) {
4249
- return extensionMap[audioCodec];
4087
+ return match[1].toString();
4088
+ }
4089
+ var getSourceMapFromRemoteUrl = async (url) => {
4090
+ if (!url.endsWith(".js.map")) {
4091
+ throw new Error(`The URL ${url} does not seem to be a valid source map URL.`);
4250
4092
  }
4251
- throw new Error(`Unsupported audio codec: ${audioCodec}`);
4093
+ const obj = await fetchUrl(url);
4094
+ return new SourceMapConsumer(obj);
4252
4095
  };
4253
- var resolveAudioCodec = ({
4254
- codec,
4255
- setting,
4256
- preferLossless,
4257
- separateAudioTo
4258
- }) => {
4259
- let derivedFromSeparateAudioToExtension = null;
4260
- if (separateAudioTo) {
4261
- const extension = separateAudioTo.split(".").pop();
4262
- for (const [key, value] of Object.entries(extensionMap)) {
4263
- if (value === extension) {
4264
- derivedFromSeparateAudioToExtension = key;
4265
- if (!supportedAudioCodecs[codec].includes(derivedFromSeparateAudioToExtension) && derivedFromSeparateAudioToExtension) {
4266
- throw new Error(`The codec is ${codec} but the audio codec derived from --${separateAudioOption.cliFlag} is ${derivedFromSeparateAudioToExtension}. The only supported codecs are: ${supportedAudioCodecs[codec].join(", ")}`);
4267
- }
4268
- }
4269
- }
4096
+ var getSourceMap = (filePath, fileContents, type) => {
4097
+ const sm = extractSourceMapUrl(fileContents);
4098
+ if (sm === null) {
4099
+ return Promise.resolve(null);
4270
4100
  }
4271
- if (preferLossless) {
4272
- const selected = getDefaultAudioCodec({ codec, preferLossless });
4273
- if (derivedFromSeparateAudioToExtension && selected !== derivedFromSeparateAudioToExtension) {
4274
- throw new Error(`The audio codec derived from --${separateAudioOption.cliFlag} is ${derivedFromSeparateAudioToExtension}, but does not match the audio codec derived from the "Prefer lossless" option (${selected}). Remove any conflicting options.`);
4101
+ if (sm.indexOf("data:") === 0) {
4102
+ const base64 = /^data:application\/json;([\w=:"-]+;)*base64,/;
4103
+ const match2 = sm.match(base64);
4104
+ if (!match2) {
4105
+ throw new Error("Sorry, non-base64 inline source-map encoding is not supported.");
4275
4106
  }
4276
- return selected;
4277
- }
4278
- if (setting === null) {
4279
- if (derivedFromSeparateAudioToExtension) {
4280
- return derivedFromSeparateAudioToExtension;
4107
+ const converted = window.atob(sm.substring(match2[0].length));
4108
+ try {
4109
+ const sourceMapConsumer = new SourceMapConsumer(JSON.parse(converted));
4110
+ return Promise.resolve(sourceMapConsumer);
4111
+ } catch {
4112
+ return Promise.resolve(null);
4281
4113
  }
4282
- return getDefaultAudioCodec({ codec, preferLossless });
4283
4114
  }
4284
- if (derivedFromSeparateAudioToExtension !== setting && derivedFromSeparateAudioToExtension) {
4285
- throw new Error(`The audio codec derived from --${separateAudioOption.cliFlag} is ${derivedFromSeparateAudioToExtension}, but does not match the audio codec derived from your ${audioCodecOption.name} setting (${setting}). Remove any conflicting options.`);
4115
+ if (type === "local") {
4116
+ const newFilePath = path6.join(path6.dirname(filePath), sm);
4117
+ return Promise.resolve(new SourceMapConsumer(readFileSync(newFilePath, "utf8")));
4286
4118
  }
4287
- return setting;
4119
+ const index = filePath.lastIndexOf("/");
4120
+ const url = filePath.substring(0, index + 1) + sm;
4121
+ return getSourceMapFromRemoteUrl(url);
4288
4122
  };
4289
- var getDefaultAudioCodec = ({
4290
- codec,
4291
- preferLossless
4292
- }) => {
4293
- return defaultAudioCodecs[codec][preferLossless ? "lossless" : "compressed"];
4123
+ var fetchUrl = async (url) => {
4124
+ const res = await readFile(url);
4125
+ return new Promise((resolve, reject) => {
4126
+ let downloaded = "";
4127
+ res.on("data", (d) => {
4128
+ downloaded += d;
4129
+ });
4130
+ res.on("end", () => {
4131
+ resolve(downloaded);
4132
+ });
4133
+ res.on("error", (err) => reject(err));
4134
+ });
4294
4135
  };
4295
- var _audioCodec = null;
4296
- var audioCodecOption = {
4297
- cliFlag: cliFlag2,
4298
- setConfig: (audioCodec) => {
4299
- if (audioCodec === null) {
4300
- _audioCodec = null;
4301
- return;
4302
- }
4303
- if (!validAudioCodecs.includes(audioCodec)) {
4304
- throw new Error(`Audio codec must be one of the following: ${validAudioCodecs.join(", ")}, but got ${audioCodec}`);
4305
- }
4306
- _audioCodec = audioCodec;
4307
- },
4308
- getValue: ({ commandLine }) => {
4309
- if (commandLine[cliFlag2]) {
4310
- const codec = commandLine[cliFlag2];
4311
- if (!validAudioCodecs.includes(commandLine[cliFlag2])) {
4312
- throw new Error(`Audio codec must be one of the following: ${validAudioCodecs.join(", ")}, but got ${codec}`);
4313
- }
4314
- return {
4315
- source: "cli",
4316
- value: commandLine[cliFlag2]
4317
- };
4318
- }
4319
- if (_audioCodec !== null) {
4320
- return {
4321
- source: "config",
4322
- value: _audioCodec
4323
- };
4324
- }
4325
- return {
4326
- source: "default",
4327
- value: null
4328
- };
4329
- },
4330
- description: () => `Set the format of the audio that is embedded in the video. Not all codec and audio codec combinations are supported and certain combinations require a certain file extension and container format. See the table in the docs to see possible combinations.`,
4331
- docLink: "https://www.remotion.dev/docs/encoding/#audio-codec",
4332
- name: "Audio Codec",
4333
- ssrName,
4334
- type: "aac"
4335
- };
4336
-
4337
- // src/parse-ffmpeg-progress.ts
4338
- var parseFfmpegProgress = (input, fps) => {
4339
- const match = input.match(/frame=(\s+)?([0-9]+)\s/);
4340
- if (match) {
4341
- return Number(match[2]);
4136
+ function getLinesAround(line, count, lines) {
4137
+ const result = [];
4138
+ for (let index = Math.max(0, line - 1 - count) + 1;index <= Math.min(lines.length - 1, line - 1 + count); ++index) {
4139
+ result.push({
4140
+ lineNumber: index + 1,
4141
+ content: lines[index],
4142
+ highlight: index + 1 === line
4143
+ });
4342
4144
  }
4343
- const match2 = input.match(/time=(\d+):(\d+):(\d+).(\d+)\s/);
4344
- if (match2) {
4345
- const [, hours, minutes, seconds, hundreds] = match2;
4346
- return Number(hundreds) / 100 * fps + Number(seconds) * fps + Number(minutes) * fps * 60 + Number(hours) * fps * 60 * 60;
4145
+ return result;
4146
+ }
4147
+ var getOriginalPosition = (source_map, line, column) => {
4148
+ const result = source_map.originalPositionFor({
4149
+ line,
4150
+ column
4151
+ });
4152
+ return { line: result.line, column: result.column, source: result.source };
4153
+ };
4154
+ var symbolicateStackTraceFromRemoteFrames = async (frames) => {
4155
+ const uniqueFileNames = [
4156
+ ...new Set(frames.map((f) => f.fileName).filter((f) => f.startsWith("http://") || f.startsWith("https://")).filter(truthy))
4157
+ ];
4158
+ const maps = await Promise.all(uniqueFileNames.map((fileName) => {
4159
+ return getSourceMapFromRemoteFile(fileName);
4160
+ }));
4161
+ const mapValues = {};
4162
+ for (let i = 0;i < uniqueFileNames.length; i++) {
4163
+ mapValues[uniqueFileNames[i]] = maps[i];
4347
4164
  }
4165
+ return symbolicateFromSources(frames, mapValues);
4166
+ };
4167
+ var symbolicateFromSources = (frames, mapValues) => {
4168
+ return frames.map((frame) => {
4169
+ const map = mapValues[frame.fileName];
4170
+ if (!map) {
4171
+ return null;
4172
+ }
4173
+ return symbolicateStackFrame(frame, map);
4174
+ }).filter(truthy).filter((f) => f.originalScriptCode !== null);
4175
+ };
4176
+ var symbolicateStackFrame = (frame, map) => {
4177
+ const pos = getOriginalPosition(map, frame.lineNumber, frame.columnNumber);
4178
+ const hasSource = pos.source ? map.sourceContentFor(pos.source, false) : null;
4179
+ const scriptCode = hasSource && pos.line ? getLinesAround(pos.line, 3, hasSource.split(`
4180
+ `)) : null;
4181
+ return {
4182
+ originalColumnNumber: pos.column,
4183
+ originalFileName: pos.source,
4184
+ originalFunctionName: frame.functionName,
4185
+ originalLineNumber: pos.line,
4186
+ originalScriptCode: scriptCode
4187
+ };
4188
+ };
4189
+ var getSourceMapFromRemoteFile = async (fileName) => {
4190
+ const fileContents = await fetchUrl(fileName);
4191
+ return getSourceMap(fileName, fileContents, "remote");
4192
+ };
4193
+ var getSourceMapFromLocalFile = (fileName) => {
4194
+ const fileContents = readFileSync(fileName, "utf8");
4195
+ return getSourceMap(fileName, fileContents, "local");
4348
4196
  };
4349
4197
 
4350
- // src/sample-rate.ts
4351
- var DEFAULT_SAMPLE_RATE = 48000;
4198
+ // src/error-handling/handle-javascript-exception.ts
4199
+ import { NoReactInternals as NoReactInternals5 } from "remotion/no-react";
4352
4200
 
4353
- // src/combine-audio.ts
4354
- var durationOf1Frame = 1024 / DEFAULT_SAMPLE_RATE * 1e6;
4355
- var getClosestAlignedTime = (targetTime) => {
4356
- const decimalFramesToTargetTime = targetTime * 1e6 / durationOf1Frame;
4357
- const nearestFrameIndexForTargetTime = Math.round(decimalFramesToTargetTime);
4358
- return nearestFrameIndexForTargetTime * durationOf1Frame / 1e6;
4359
- };
4360
- var encodeAudio = async ({
4361
- files,
4362
- resolvedAudioCodec,
4363
- audioBitrate,
4364
- filelistDir,
4365
- output,
4366
- indent,
4367
- logLevel,
4368
- addRemotionMetadata,
4369
- fps,
4370
- binariesDirectory,
4371
- cancelSignal,
4372
- onProgress
4201
+ // src/delay-render-embedded-stack.ts
4202
+ import { NoReactInternals as NoReactInternals4 } from "remotion/no-react";
4203
+
4204
+ // src/parse-browser-error-stack.ts
4205
+ var regexValidFrame_Chrome = /^\s*(at|in)\s.+(:\d+)/;
4206
+ var regexValidFrame_FireFox = /(^|@)\S+:\d+|.+line\s+\d+\s+>\s+(eval|Function).+/;
4207
+ var regexExtractLocation = /\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/;
4208
+ function extractLocation(token) {
4209
+ const execed = regexExtractLocation.exec(token);
4210
+ if (!execed) {
4211
+ throw new Error("Could not match in extractLocation");
4212
+ }
4213
+ return execed.slice(1).map((v) => {
4214
+ const p = Number(v);
4215
+ if (!isNaN(p)) {
4216
+ return p;
4217
+ }
4218
+ return v;
4219
+ });
4220
+ }
4221
+ var makeStackFrame = ({
4222
+ functionName,
4223
+ fileName,
4224
+ lineNumber,
4225
+ columnNumber
4373
4226
  }) => {
4374
- const fileList = files.map((p) => `file '${p}'`).join(`
4375
- `);
4376
- const fileListTxt = join2(filelistDir, "audio-files.txt");
4377
- writeFileSync(fileListTxt, fileList);
4378
- const startCombining = Date.now();
4379
- const command = [
4380
- "-hide_banner",
4381
- "-f",
4382
- "concat",
4383
- "-safe",
4384
- "0",
4385
- "-i",
4386
- fileListTxt,
4387
- "-c:a",
4388
- mapAudioCodecToFfmpegAudioCodecName(resolvedAudioCodec),
4389
- resolvedAudioCodec === "aac" ? "-cutoff" : null,
4390
- resolvedAudioCodec === "aac" ? "18000" : null,
4391
- "-b:a",
4392
- audioBitrate ? audioBitrate : "320k",
4393
- "-vn",
4394
- addRemotionMetadata ? `-metadata` : null,
4395
- addRemotionMetadata ? `comment=Made with Remotion ${VERSION2}` : null,
4396
- "-y",
4397
- output
4398
- ];
4399
- Log.verbose({ indent, logLevel }, `Combining audio with re-encoding, command: ${command.join(" ")}`);
4400
- try {
4401
- const task = callFf({
4402
- args: command,
4403
- bin: "ffmpeg",
4404
- indent,
4405
- logLevel,
4406
- binariesDirectory,
4407
- cancelSignal
4408
- });
4409
- task.stderr?.on("data", (data) => {
4410
- const utf8 = data.toString("utf8");
4411
- const parsed = parseFfmpegProgress(utf8, fps);
4412
- if (parsed === undefined) {
4413
- Log.verbose({ indent, logLevel }, utf8);
4414
- } else {
4415
- onProgress(parsed);
4416
- Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
4227
+ if (functionName && functionName.indexOf("Object.") === 0) {
4228
+ functionName = functionName.slice("Object.".length);
4229
+ }
4230
+ if (functionName === "friendlySyntaxErrorLabel" || functionName === "exports.__esModule" || functionName === "<anonymous>" || !functionName) {
4231
+ functionName = null;
4232
+ }
4233
+ return {
4234
+ columnNumber,
4235
+ fileName,
4236
+ functionName,
4237
+ lineNumber
4238
+ };
4239
+ };
4240
+ var parseStack = (stack) => {
4241
+ const frames = stack.filter((e) => regexValidFrame_Chrome.test(e) || regexValidFrame_FireFox.test(e)).map((e) => {
4242
+ if (regexValidFrame_FireFox.test(e)) {
4243
+ let isEval = false;
4244
+ if (/ > (eval|Function)/.test(e)) {
4245
+ e = e.replace(/ line (\d+)(?: > eval line \d+)* > (eval|Function):\d+:\d+/g, ":$1");
4246
+ isEval = true;
4417
4247
  }
4248
+ const _data = e.split(/[@]/g);
4249
+ const _last = _data.pop();
4250
+ if (!_last) {
4251
+ throw new Error("could not get last");
4252
+ }
4253
+ const [_fileName, _lineNumber, _columnNumber] = extractLocation(_last);
4254
+ return makeStackFrame({
4255
+ functionName: _data.join("@") || (isEval ? "eval" : null),
4256
+ fileName: _fileName,
4257
+ lineNumber: _lineNumber,
4258
+ columnNumber: _columnNumber
4259
+ });
4260
+ }
4261
+ if (e.indexOf("(eval ") !== -1) {
4262
+ e = e.replace(/(\(eval at [^()]*)|(\),.*$)/g, "");
4263
+ }
4264
+ if (e.indexOf("(at ") !== -1) {
4265
+ e = e.replace(/\(at /, "(");
4266
+ }
4267
+ const data = e.trim().split(/\s+/g).slice(1);
4268
+ const last = data.pop();
4269
+ if (!last) {
4270
+ throw new Error("could not get last");
4271
+ }
4272
+ const [fileName, lineNumber, columnNumber] = extractLocation(last);
4273
+ return makeStackFrame({
4274
+ functionName: data.join(" ") || null,
4275
+ fileName,
4276
+ lineNumber,
4277
+ columnNumber
4418
4278
  });
4419
- await task;
4420
- Log.verbose({ indent, logLevel }, `Encoded audio in ${Date.now() - startCombining}ms`);
4421
- return output;
4422
- } catch (e) {
4423
- rmSync2(fileListTxt, { recursive: true });
4424
- throw e;
4279
+ });
4280
+ return frames;
4281
+ };
4282
+
4283
+ // src/delay-render-embedded-stack.ts
4284
+ var parseDelayRenderEmbeddedStack = (message) => {
4285
+ const index = message.indexOf(NoReactInternals4.DELAY_RENDER_CALLSTACK_TOKEN);
4286
+ if (index === -1) {
4287
+ return null;
4425
4288
  }
4289
+ const msg = message.substring(index + NoReactInternals4.DELAY_RENDER_CALLSTACK_TOKEN.length).trim();
4290
+ const parsed = parseStack(msg.split(`
4291
+ `));
4292
+ return parsed;
4426
4293
  };
4427
- var combineAudioSeamlessly = async ({
4428
- files,
4429
- filelistDir,
4430
- indent,
4431
- logLevel,
4432
- output,
4433
- chunkDurationInSeconds,
4434
- addRemotionMetadata,
4435
- fps,
4436
- binariesDirectory,
4437
- cancelSignal,
4438
- onProgress
4439
- }) => {
4440
- const startConcatenating = Date.now();
4441
- const fileList = files.map((p, i) => {
4442
- const isLast = i === files.length - 1;
4443
- const targetStart = i * chunkDurationInSeconds;
4444
- const endStart = (i + 1) * chunkDurationInSeconds;
4445
- const startTime = getClosestAlignedTime(targetStart) * 1e6;
4446
- const endTime = getClosestAlignedTime(endStart) * 1e6;
4447
- const realDuration = endTime - startTime;
4448
- let inpoint = 0;
4449
- if (i > 0) {
4450
- inpoint = durationOf1Frame * 4;
4451
- }
4452
- const outpoint = (i === 0 ? durationOf1Frame * 2 : inpoint) + realDuration - (isLast ? 0 : durationOf1Frame);
4453
- return [`file '${p}'`, `inpoint ${inpoint}us`, `outpoint ${outpoint}us`].filter(truthy).join(`
4294
+
4295
+ // src/error-handling/symbolicateable-error.ts
4296
+ class SymbolicateableError extends Error {
4297
+ stackFrame;
4298
+ delayRenderCall;
4299
+ frame;
4300
+ chunk;
4301
+ constructor({
4302
+ message,
4303
+ stack,
4304
+ stackFrame,
4305
+ frame,
4306
+ name,
4307
+ chunk
4308
+ }) {
4309
+ super(message);
4310
+ this.stack = stack;
4311
+ this.stackFrame = stackFrame;
4312
+ this.frame = frame;
4313
+ this.chunk = chunk;
4314
+ this.name = name;
4315
+ this.delayRenderCall = stack ? parseDelayRenderEmbeddedStack(stack) : null;
4316
+ }
4317
+ }
4318
+
4319
+ // src/error-handling/handle-javascript-exception.ts
4320
+ class ErrorWithStackFrame extends Error {
4321
+ symbolicatedStackFrames;
4322
+ frame;
4323
+ chunk;
4324
+ name;
4325
+ delayRenderCall;
4326
+ constructor({
4327
+ message,
4328
+ symbolicatedStackFrames,
4329
+ frame,
4330
+ name,
4331
+ delayRenderCall,
4332
+ stack,
4333
+ chunk
4334
+ }) {
4335
+ super(message);
4336
+ this.symbolicatedStackFrames = symbolicatedStackFrames;
4337
+ this.frame = frame;
4338
+ this.chunk = chunk;
4339
+ this.name = name;
4340
+ this.delayRenderCall = delayRenderCall;
4341
+ this.stack = stack;
4342
+ }
4343
+ }
4344
+ var cleanUpErrorMessage = (exception) => {
4345
+ let errorMessage = exception.exceptionDetails.exception?.description;
4346
+ if (!errorMessage) {
4347
+ return null;
4348
+ }
4349
+ const errorType = exception.exceptionDetails.exception?.className;
4350
+ const prefix = `${errorType}: `;
4351
+ if (errorMessage.startsWith(prefix)) {
4352
+ errorMessage = errorMessage.substring(prefix.length);
4353
+ }
4354
+ const frames = exception.exceptionDetails.stackTrace?.callFrames.length ?? 0;
4355
+ const split = errorMessage.split(`
4454
4356
  `);
4455
- }).join(`
4357
+ return split.slice(0, Math.max(1, split.length - frames)).join(`
4456
4358
  `);
4457
- const fileListTxt = join2(filelistDir, "audio-files.txt");
4458
- writeFileSync(fileListTxt, fileList);
4459
- const command = [
4460
- "-hide_banner",
4461
- "-f",
4462
- "concat",
4463
- "-safe",
4464
- "0",
4465
- "-i",
4466
- fileListTxt,
4467
- "-c:a",
4468
- "copy",
4469
- "-vn",
4470
- addRemotionMetadata ? `-metadata` : null,
4471
- addRemotionMetadata ? `comment=Made with Remotion ${VERSION2}` : null,
4472
- "-y",
4473
- output
4474
- ];
4475
- Log.verbose({ indent, logLevel }, `Combining AAC audio seamlessly, command: ${command.join(" ")}`);
4476
- try {
4477
- const task = callFf({
4478
- args: command,
4479
- bin: "ffmpeg",
4480
- indent,
4481
- logLevel,
4482
- binariesDirectory,
4483
- cancelSignal
4484
- });
4485
- task.stderr?.on("data", (data) => {
4486
- const utf8 = data.toString("utf8");
4487
- const parsed = parseFfmpegProgress(utf8, fps);
4488
- if (parsed !== undefined) {
4489
- onProgress(parsed);
4490
- Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
4491
- }
4492
- });
4493
- await task;
4494
- Log.verbose({ indent, logLevel }, `Combined audio seamlessly in ${Date.now() - startConcatenating}ms`);
4495
- return output;
4496
- } catch (e) {
4497
- rmSync2(fileListTxt, { recursive: true });
4498
- Log.error({ indent, logLevel }, e);
4499
- throw e;
4359
+ };
4360
+ var removeDelayRenderStack = (message) => {
4361
+ const index = message.indexOf(NoReactInternals5.DELAY_RENDER_CALLSTACK_TOKEN);
4362
+ if (index === -1) {
4363
+ return message;
4500
4364
  }
4365
+ return message.substring(0, index);
4501
4366
  };
4502
- var createCombinedAudio = ({
4503
- seamless,
4504
- filelistDir,
4505
- files,
4506
- indent,
4507
- logLevel,
4508
- audioBitrate,
4509
- resolvedAudioCodec,
4510
- output,
4511
- chunkDurationInSeconds,
4512
- addRemotionMetadata,
4513
- binariesDirectory,
4514
- fps,
4515
- cancelSignal,
4516
- onProgress
4367
+ var callFrameToStackFrame = (callFrame) => {
4368
+ return {
4369
+ columnNumber: callFrame.columnNumber,
4370
+ fileName: callFrame.url,
4371
+ functionName: callFrame.functionName,
4372
+ lineNumber: callFrame.lineNumber
4373
+ };
4374
+ };
4375
+ var handleJavascriptException = ({
4376
+ page,
4377
+ onError,
4378
+ frame
4517
4379
  }) => {
4518
- if (seamless) {
4519
- return combineAudioSeamlessly({
4520
- filelistDir,
4521
- files,
4522
- indent,
4523
- logLevel,
4524
- output,
4525
- chunkDurationInSeconds,
4526
- addRemotionMetadata,
4527
- binariesDirectory,
4528
- fps,
4529
- cancelSignal,
4530
- onProgress
4380
+ const client = page._client();
4381
+ const handler = (exception) => {
4382
+ const rawErrorMessage = exception.exceptionDetails.exception?.description;
4383
+ const cleanErrorMessage = cleanUpErrorMessage(exception);
4384
+ if (!cleanErrorMessage) {
4385
+ console.error(exception);
4386
+ const err = new Error(rawErrorMessage);
4387
+ err.stack = rawErrorMessage;
4388
+ onError(err);
4389
+ return;
4390
+ }
4391
+ if (!exception.exceptionDetails.stackTrace) {
4392
+ const err = new Error(removeDelayRenderStack(cleanErrorMessage));
4393
+ err.stack = rawErrorMessage;
4394
+ onError(err);
4395
+ return;
4396
+ }
4397
+ const errorType = exception.exceptionDetails.exception?.className;
4398
+ const symbolicatedErr = new SymbolicateableError({
4399
+ message: removeDelayRenderStack(cleanErrorMessage),
4400
+ stackFrame: exception.exceptionDetails.stackTrace.callFrames.map((f) => callFrameToStackFrame(f)),
4401
+ frame,
4402
+ name: errorType,
4403
+ stack: exception.exceptionDetails.exception?.description,
4404
+ chunk: null
4531
4405
  });
4532
- }
4533
- return encodeAudio({
4534
- filelistDir,
4535
- files,
4536
- resolvedAudioCodec,
4537
- audioBitrate,
4538
- output,
4539
- indent,
4540
- logLevel,
4541
- addRemotionMetadata,
4542
- binariesDirectory,
4543
- fps,
4544
- cancelSignal,
4545
- onProgress
4546
- });
4406
+ onError(symbolicatedErr);
4407
+ };
4408
+ client.on("Runtime.exceptionThrown", handler);
4409
+ return () => {
4410
+ client.off("Runtime.exceptionThrown", handler);
4411
+ return Promise.resolve();
4412
+ };
4547
4413
  };
4548
4414
 
4549
- // src/combine-video-streams.ts
4550
- import { rmSync as rmSync3, writeFileSync as writeFileSync2 } from "fs";
4551
- import { join as join3 } from "path";
4552
- import { VERSION as VERSION3 } from "remotion/version";
4553
-
4554
- // src/convert-number-of-gif-loops-to-ffmpeg.ts
4555
- var convertNumberOfGifLoopsToFfmpegSyntax = (loops) => {
4556
- if (loops === null) {
4557
- return "0";
4558
- }
4559
- if (loops === 0) {
4560
- return "-1";
4561
- }
4562
- return String(loops);
4563
- };
4564
-
4565
- // src/combine-video-streams.ts
4566
- var combineVideoStreams = async ({
4567
- fps,
4568
- codec,
4569
- filelistDir,
4570
- numberOfGifLoops,
4571
- output,
4572
- indent,
4573
- logLevel,
4574
- onProgress,
4575
- files,
4576
- addRemotionMetadata,
4577
- binariesDirectory,
4578
- cancelSignal
4579
- }) => {
4580
- const fileList = files.map((p) => `file '${p}'`).join(`
4581
- `);
4582
- const fileListTxt = join3(filelistDir, "video-files.txt");
4583
- writeFileSync2(fileListTxt, fileList);
4584
- const encoder = codec === "gif" ? "gif" : "copy";
4585
- const command = [
4586
- "-hide_banner",
4587
- "-r",
4588
- String(fps),
4589
- "-f",
4590
- "concat",
4591
- "-safe",
4592
- "0",
4593
- "-i",
4594
- fileListTxt,
4595
- numberOfGifLoops === null ? null : "-loop",
4596
- numberOfGifLoops === null ? null : convertNumberOfGifLoopsToFfmpegSyntax(numberOfGifLoops),
4597
- codec === "gif" ? "-filter_complex" : null,
4598
- codec === "gif" ? "split[v],palettegen,[v]paletteuse" : null,
4599
- "-an",
4600
- "-c:v",
4601
- encoder,
4602
- codec === "h265" ? "-tag:v" : null,
4603
- codec === "h265" ? "hvc1" : null,
4604
- addRemotionMetadata ? `-metadata` : null,
4605
- addRemotionMetadata ? `comment=Made with Remotion ${VERSION3}` : null,
4606
- "-y",
4607
- output
4608
- ].filter(truthy);
4609
- const doesReencode = encoder !== "copy";
4610
- const startTime = Date.now();
4611
- Log.verbose({ indent, logLevel }, `Combining video ${doesReencode ? "with reencoding" : "without reencoding"}, command: ${command.join(" ")}`);
4612
- try {
4613
- const task = callFf({
4614
- args: command,
4615
- bin: "ffmpeg",
4616
- indent,
4617
- logLevel,
4618
- binariesDirectory,
4619
- cancelSignal
4620
- });
4621
- task.stderr?.on("data", (data) => {
4622
- const parsed = parseFfmpegProgress(data.toString("utf8"), fps);
4623
- if (parsed === undefined) {
4624
- Log.verbose({ indent, logLevel }, data.toString("utf8"));
4625
- } else {
4626
- Log.verbose({ indent, logLevel }, `Encoded ${parsed} video frames`);
4627
- onProgress(parsed);
4628
- }
4629
- });
4630
- await task;
4631
- Log.verbose({ indent, logLevel }, `Finished combining video in ${Date.now() - startTime}ms`);
4632
- return output;
4633
- } catch (e) {
4634
- rmSync3(fileListTxt, { recursive: true });
4635
- throw e;
4636
- }
4637
- };
4638
-
4639
- // src/combine-video-streams-seamlessly.ts
4640
- var combineVideoStreamsSeamlessly = ({ files }) => {
4641
- const fileList = `concat:${files.join("|")}`;
4642
- return fileList;
4415
+ // src/error-handling/symbolicate-error.ts
4416
+ var symbolicateError = async (symbolicateableError) => {
4417
+ const { delayRenderCall, stackFrame } = symbolicateableError;
4418
+ const [mainErrorFrames, delayRenderFrames] = await Promise.all([
4419
+ stackFrame ? symbolicateStackTraceFromRemoteFrames(stackFrame) : null,
4420
+ delayRenderCall ? symbolicateStackTraceFromRemoteFrames(delayRenderCall) : null
4421
+ ].filter(truthy));
4422
+ const symbolicatedErr = new ErrorWithStackFrame({
4423
+ message: symbolicateableError.message,
4424
+ symbolicatedStackFrames: mainErrorFrames,
4425
+ frame: symbolicateableError.frame,
4426
+ name: symbolicateableError.name,
4427
+ delayRenderCall: delayRenderFrames,
4428
+ stack: symbolicateableError.stack,
4429
+ chunk: symbolicateableError.chunk
4430
+ });
4431
+ return symbolicatedErr;
4643
4432
  };
4644
4433
 
4645
4434
  // src/file-extensions.ts
@@ -4726,1385 +4515,768 @@ var defaultFileExtensionMap = {
4726
4515
  }
4727
4516
  };
4728
4517
 
4729
- // src/get-extension-from-codec.ts
4730
- var getFileExtensionFromCodec = (codec, audioCodec) => {
4731
- if (!validCodecs.includes(codec)) {
4732
- throw new Error(`Codec must be one of the following: ${validCodecs.join(", ")}, but got ${codec}`);
4733
- }
4734
- const map = defaultFileExtensionMap[codec];
4735
- if (audioCodec === null) {
4736
- return map.default;
4518
+ // src/frame-range.ts
4519
+ var validateFrameRange = (frameRange) => {
4520
+ if (frameRange === null) {
4521
+ return;
4737
4522
  }
4738
- const typedAudioCodec = audioCodec;
4739
- if (!(typedAudioCodec in map.forAudioCodec)) {
4740
- throw new Error(`Audio codec ${typedAudioCodec} is not supported for codec ${codec}`);
4523
+ if (typeof frameRange === "number") {
4524
+ if (frameRange < 0) {
4525
+ throw new TypeError("Frame must be a non-negative number, got " + frameRange);
4526
+ }
4527
+ if (!Number.isFinite(frameRange)) {
4528
+ throw new TypeError("Frame must be a finite number, got " + frameRange);
4529
+ }
4530
+ if (!Number.isInteger(frameRange)) {
4531
+ throw new Error(`Frame must be an integer, but got a float (${frameRange})`);
4532
+ }
4533
+ return;
4741
4534
  }
4742
- return map.forAudioCodec[audioCodec].default;
4743
- };
4744
- var makeFileExtensionMap = () => {
4745
- const map = {};
4746
- Object.keys(defaultFileExtensionMap).forEach((_codec) => {
4747
- const codec = _codec;
4748
- const fileExtMap = defaultFileExtensionMap[codec];
4749
- const audioCodecs = Object.keys(fileExtMap.forAudioCodec);
4750
- const possibleExtensionsForAudioCodec = audioCodecs.map((audioCodec) => fileExtMap.forAudioCodec[audioCodec].possible);
4751
- const allPossibleExtensions = [
4752
- fileExtMap.default,
4753
- ...possibleExtensionsForAudioCodec.flat(1)
4754
- ];
4755
- for (const extension of allPossibleExtensions) {
4756
- if (!map[extension]) {
4757
- map[extension] = [];
4535
+ if (Array.isArray(frameRange)) {
4536
+ if (frameRange.length !== 2) {
4537
+ throw new TypeError("Frame range must be a tuple, got an array with length " + frameRange.length);
4538
+ }
4539
+ for (const value of frameRange) {
4540
+ if (typeof value !== "number") {
4541
+ throw new Error(`Each value of frame range must be a number, but got ${typeof value} (${JSON.stringify(value)})`);
4758
4542
  }
4759
- if (!map[extension].includes(codec)) {
4760
- map[extension].push(codec);
4543
+ if (!Number.isFinite(value)) {
4544
+ throw new TypeError("Each value of frame range must be finite, but got " + value);
4545
+ }
4546
+ if (!Number.isInteger(value)) {
4547
+ throw new Error(`Each value of frame range must be an integer, but got a float (${value})`);
4548
+ }
4549
+ if (value < 0) {
4550
+ throw new Error(`Each value of frame range must be non-negative, but got ${value}`);
4761
4551
  }
4762
4552
  }
4763
- });
4764
- return map;
4765
- };
4766
- var defaultCodecsForFileExtension = {
4767
- "3gp": "aac",
4768
- aac: "aac",
4769
- gif: "gif",
4770
- hevc: "h265",
4771
- m4a: "aac",
4772
- m4b: "aac",
4773
- mkv: "h264-mkv",
4774
- mov: "prores",
4775
- mp3: "mp3",
4776
- mp4: "h264",
4777
- mpeg: "aac",
4778
- mpg: "aac",
4779
- mxf: "prores",
4780
- wav: "wav",
4781
- webm: "vp8",
4782
- ts: "h264-ts"
4553
+ const [first, second] = frameRange;
4554
+ if (second < first) {
4555
+ throw new Error("The second value of frame range must be not smaller than the first one, but got " + frameRange.join("-"));
4556
+ }
4557
+ return;
4558
+ }
4559
+ throw new TypeError("Frame range must be a number or a tuple of numbers, but got object of type " + typeof frameRange);
4783
4560
  };
4784
4561
 
4785
- // src/make-metadata-args.ts
4786
- import { VERSION as VERSION4 } from "remotion/version";
4787
- var makeMetadataArgs = (metadata) => {
4788
- const defaultComment = `Made with Remotion ${VERSION4}`;
4789
- const newMetadata = {
4790
- comment: defaultComment
4562
+ // src/get-compositions.ts
4563
+ import { NoReactInternals as NoReactInternals8 } from "remotion/no-react";
4564
+
4565
+ // src/to-megabytes.ts
4566
+ function toMegabytes(bytes) {
4567
+ const mb = bytes / 1024 / 1024;
4568
+ return `${Math.round(mb * 10) / 10} Mb`;
4569
+ }
4570
+
4571
+ // src/browser/browser-download-progress-bar.ts
4572
+ var defaultBrowserDownloadProgress = ({
4573
+ indent,
4574
+ logLevel,
4575
+ api
4576
+ }) => ({ chromeMode }) => {
4577
+ if (chromeMode === "chrome-for-testing") {
4578
+ Log.info({ indent, logLevel }, "Downloading Chrome for Testing https://www.remotion.dev/chrome-for-testing");
4579
+ } else {
4580
+ Log.info({ indent, logLevel }, "Downloading Chrome Headless Shell https://www.remotion.dev/chrome-headless-shell");
4581
+ }
4582
+ Log.info({ indent, logLevel }, `Customize this behavior by adding a onBrowserDownload function to ${api}.`);
4583
+ let lastProgress = 0;
4584
+ return {
4585
+ onProgress: (progress) => {
4586
+ if (progress.downloadedBytes > lastProgress + 1e7 || progress.percent === 1) {
4587
+ lastProgress = progress.downloadedBytes;
4588
+ if (chromeMode === "chrome-for-testing") {
4589
+ Log.info({ indent, logLevel }, `Downloading Chrome for Testing - ${toMegabytes(progress.downloadedBytes)}/${toMegabytes(progress.totalSizeInBytes)}`);
4590
+ } else {
4591
+ Log.info({ indent, logLevel }, `Downloading Chrome Headless Shell - ${toMegabytes(progress.downloadedBytes)}/${toMegabytes(progress.totalSizeInBytes)}`);
4592
+ }
4593
+ }
4594
+ },
4595
+ version: null
4791
4596
  };
4792
- Object.keys(metadata).forEach((key) => {
4793
- const lowercaseKey = key.toLowerCase();
4794
- if (lowercaseKey === "comment") {
4795
- newMetadata[lowercaseKey] = `${defaultComment}; ${metadata[key]}`;
4796
- } else {
4797
- newMetadata[lowercaseKey] = metadata[key];
4798
- }
4799
- });
4800
- const metadataArgs = Object.entries(newMetadata).map(([key, value]) => ["-metadata", `${key}=${value}`]);
4801
- return metadataArgs.flat(1);
4802
4597
  };
4803
4598
 
4804
- // src/mux-video-and-audio.ts
4805
- var muxVideoAndAudio = async ({
4806
- videoOutput,
4807
- audioOutput,
4808
- output,
4599
+ // src/open-browser.ts
4600
+ import fs9 from "node:fs";
4601
+ import os3 from "node:os";
4602
+ import path9 from "node:path";
4603
+
4604
+ // src/browser/Launcher.ts
4605
+ var launchChrome = async ({
4606
+ args,
4607
+ executablePath,
4608
+ defaultViewport,
4809
4609
  indent,
4810
4610
  logLevel,
4811
- onProgress,
4812
- binariesDirectory,
4813
- fps,
4814
- cancelSignal,
4815
- addFaststart,
4816
- metadata
4611
+ userDataDir
4817
4612
  }) => {
4818
- const startTime = Date.now();
4819
- Log.verbose({ indent, logLevel }, "Muxing video and audio together");
4820
- const command = [
4821
- "-hide_banner",
4822
- videoOutput ? "-i" : null,
4823
- videoOutput,
4824
- audioOutput ? "-i" : null,
4825
- audioOutput,
4826
- videoOutput ? "-c:v" : null,
4827
- videoOutput ? "copy" : null,
4828
- audioOutput ? "-c:a" : null,
4829
- audioOutput ? "copy" : null,
4830
- addFaststart ? "-movflags" : null,
4831
- addFaststart ? "faststart" : null,
4832
- ...makeMetadataArgs(metadata ?? {}),
4833
- "-y",
4834
- output
4835
- ].filter(truthy);
4836
- Log.verbose({ indent, logLevel }, "Combining command: ", command);
4837
- const task = callFf({
4838
- bin: "ffmpeg",
4839
- args: command,
4840
- indent,
4613
+ const timeout = 60000;
4614
+ const browser = await HeadlessBrowser.create({
4615
+ defaultViewport,
4616
+ args,
4617
+ executablePath,
4618
+ timeout,
4619
+ userDataDir,
4841
4620
  logLevel,
4842
- binariesDirectory,
4843
- cancelSignal
4844
- });
4845
- task.stderr?.on("data", (data) => {
4846
- const utf8 = data.toString("utf8");
4847
- const parsed = parseFfmpegProgress(utf8, fps);
4848
- if (parsed === undefined) {
4849
- if (!utf8.includes("Estimating duration from bitrate, this may be inaccurate")) {
4850
- Log.verbose({ indent, logLevel }, utf8);
4851
- }
4852
- } else {
4853
- Log.verbose({ indent, logLevel }, `Combined ${parsed} frames`);
4854
- onProgress(parsed);
4855
- }
4621
+ indent
4856
4622
  });
4857
- await task;
4858
- Log.verbose({ indent, logLevel }, `Muxing done in ${Date.now() - startTime}ms`);
4859
- };
4860
-
4861
- // src/combine-videos.ts
4862
- var codecSupportsFastStart = {
4863
- "h264-mkv": false,
4864
- "h264-ts": false,
4865
- h264: true,
4866
- h265: true,
4867
- aac: false,
4868
- gif: false,
4869
- mp3: false,
4870
- prores: false,
4871
- vp8: false,
4872
- vp9: false,
4873
- wav: false
4874
- };
4875
- var combineChunks = async ({
4876
- files,
4877
- filelistDir,
4878
- output,
4879
- onProgress,
4880
- numberOfFrames,
4881
- codec,
4882
- fps,
4883
- numberOfGifLoops,
4884
- resolvedAudioCodec,
4885
- audioBitrate,
4886
- indent,
4887
- logLevel,
4888
- chunkDurationInSeconds,
4889
- binariesDirectory,
4890
- cancelSignal,
4891
- seamlessAudio,
4892
- seamlessVideo,
4893
- muted,
4894
- metadata
4895
- }) => {
4896
- const shouldCreateAudio = resolvedAudioCodec !== null && !muted;
4897
- const shouldCreateVideo = !isAudioCodec(codec);
4898
- const videoOutput = shouldCreateVideo ? join4(filelistDir, `video.${getFileExtensionFromCodec(codec, resolvedAudioCodec)}`) : null;
4899
- const audioOutput = shouldCreateAudio ? join4(filelistDir, `audio.${getExtensionFromAudioCodec(resolvedAudioCodec)}`) : null;
4900
- const audioFiles = files.filter((f) => f.endsWith("audio"));
4901
- const videoFiles = files.filter((f) => f.endsWith("video"));
4902
- let concatenatedAudio = 0;
4903
- let concatenatedVideo = 0;
4904
- let muxing = 0;
4905
- const updateProgress = () => {
4906
- const totalFrames = (shouldCreateAudio ? numberOfFrames : 0) + (shouldCreateVideo ? numberOfFrames : 0) + numberOfFrames;
4907
- const actualProgress = concatenatedAudio + concatenatedVideo + muxing;
4908
- onProgress(actualProgress / totalFrames * numberOfFrames);
4909
- };
4910
- Log.verbose({ indent, logLevel }, `Combining chunks, audio = ${shouldCreateAudio === false ? "no" : seamlessAudio ? "seamlessly" : "normally"}, video = ${shouldCreateVideo === false ? "no" : seamlessVideo ? "seamlessly" : "normally"}`);
4911
- await Promise.all([
4912
- shouldCreateAudio && audioOutput ? createCombinedAudio({
4913
- audioBitrate,
4914
- filelistDir,
4915
- files: audioFiles,
4916
- indent,
4917
- logLevel,
4918
- output: audioOutput,
4919
- resolvedAudioCodec,
4920
- seamless: seamlessAudio,
4921
- chunkDurationInSeconds,
4922
- addRemotionMetadata: !shouldCreateVideo,
4923
- binariesDirectory,
4924
- fps,
4925
- cancelSignal,
4926
- onProgress: (frames) => {
4927
- concatenatedAudio = frames;
4928
- updateProgress();
4929
- }
4930
- }) : null,
4931
- shouldCreateVideo && !seamlessVideo && videoOutput ? combineVideoStreams({
4932
- codec,
4933
- filelistDir,
4934
- fps,
4935
- indent,
4936
- logLevel,
4937
- numberOfGifLoops,
4938
- output: videoOutput,
4939
- files: videoFiles,
4940
- addRemotionMetadata: !shouldCreateAudio,
4941
- binariesDirectory,
4942
- cancelSignal,
4943
- onProgress: (frames) => {
4944
- concatenatedVideo = frames;
4945
- updateProgress();
4946
- }
4947
- }) : null
4948
- ].filter(truthy));
4949
4623
  try {
4950
- await muxVideoAndAudio({
4951
- audioOutput,
4952
- indent,
4953
- logLevel,
4954
- onProgress: (frames) => {
4955
- muxing = frames;
4956
- updateProgress();
4957
- },
4958
- output,
4959
- videoOutput: seamlessVideo ? combineVideoStreamsSeamlessly({ files: videoFiles }) : videoOutput,
4960
- binariesDirectory,
4961
- fps,
4962
- cancelSignal,
4963
- addFaststart: codecSupportsFastStart[codec],
4964
- metadata
4965
- });
4966
- onProgress(numberOfFrames);
4967
- rmSync4(filelistDir, { recursive: true });
4968
- } catch (err) {
4969
- rmSync4(filelistDir, { recursive: true });
4970
- throw err;
4624
+ await browser.waitForTarget((t) => {
4625
+ return t.type() === "page";
4626
+ }, { timeout });
4627
+ } catch (error) {
4628
+ await browser.close({ silent: false });
4629
+ throw error;
4971
4630
  }
4631
+ return browser;
4972
4632
  };
4973
4633
 
4974
- // src/convert-to-positive-frame-index.ts
4975
- var convertToPositiveFrameIndex = ({
4976
- frame,
4977
- durationInFrames
4978
- }) => {
4979
- return frame < 0 ? durationInFrames - frame : frame;
4980
- };
4634
+ // src/ensure-browser.ts
4635
+ import fs7 from "fs";
4981
4636
 
4982
- // src/symbolicate-stacktrace.ts
4983
- import { readFileSync } from "fs";
4984
- import path6 from "path";
4985
- import { SourceMapConsumer } from "source-map";
4986
- function extractSourceMapUrl(fileContents) {
4987
- const regex = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/gm;
4988
- let match = null;
4637
+ // src/browser/BrowserFetcher.ts
4638
+ import * as fs6 from "node:fs";
4639
+ import * as os2 from "node:os";
4640
+ import * as path8 from "node:path";
4641
+ import extractZip from "extract-zip";
4642
+ import { promisify } from "node:util";
4643
+
4644
+ // src/browser/get-download-destination.ts
4645
+ import fs5 from "node:fs";
4646
+ import path7 from "node:path";
4647
+ var getDownloadsCacheDir = () => {
4648
+ const cwd = process.cwd();
4649
+ let dir = cwd;
4989
4650
  for (;; ) {
4990
- const next = regex.exec(fileContents);
4991
- if (next === null || next === undefined) {
4651
+ try {
4652
+ if (fs5.statSync(path7.join(dir, "package.json")).isFile()) {
4653
+ break;
4654
+ }
4655
+ } catch (e) {
4656
+ }
4657
+ const parent = path7.dirname(dir);
4658
+ if (dir === parent) {
4659
+ dir = undefined;
4992
4660
  break;
4993
4661
  }
4994
- match = next;
4662
+ dir = parent;
4995
4663
  }
4996
- if (!match?.[1]) {
4997
- return null;
4664
+ if (!dir) {
4665
+ return path7.resolve(cwd, ".remotion");
4998
4666
  }
4999
- return match[1].toString();
5000
- }
5001
- var getSourceMapFromRemoteUrl = async (url) => {
5002
- if (!url.endsWith(".js.map")) {
5003
- throw new Error(`The URL ${url} does not seem to be a valid source map URL.`);
4667
+ if (process.versions.pnp === "1") {
4668
+ return path7.resolve(dir, ".pnp/.remotion");
5004
4669
  }
5005
- const obj = await fetchUrl(url);
5006
- return new SourceMapConsumer(obj);
5007
- };
5008
- var getSourceMap = (filePath, fileContents, type) => {
5009
- const sm = extractSourceMapUrl(fileContents);
5010
- if (sm === null) {
5011
- return Promise.resolve(null);
4670
+ if (process.versions.pnp === "3") {
4671
+ return path7.resolve(dir, ".yarn/.remotion");
5012
4672
  }
5013
- if (sm.indexOf("data:") === 0) {
5014
- const base64 = /^data:application\/json;([\w=:"-]+;)*base64,/;
5015
- const match2 = sm.match(base64);
5016
- if (!match2) {
5017
- throw new Error("Sorry, non-base64 inline source-map encoding is not supported.");
5018
- }
5019
- const converted = window.atob(sm.substring(match2[0].length));
5020
- try {
5021
- const sourceMapConsumer = new SourceMapConsumer(JSON.parse(converted));
5022
- return Promise.resolve(sourceMapConsumer);
5023
- } catch {
5024
- return Promise.resolve(null);
5025
- }
4673
+ return path7.resolve(dir, "node_modules/.remotion");
4674
+ };
4675
+
4676
+ // src/browser/BrowserFetcher.ts
4677
+ var TESTED_VERSION = "133.0.6943.0";
4678
+ var PLAYWRIGHT_VERSION = "1155";
4679
+ function getChromeDownloadUrl({
4680
+ platform: platform2,
4681
+ version,
4682
+ chromeMode
4683
+ }) {
4684
+ if (platform2 === "linux-arm64") {
4685
+ return `https://playwright.azureedge.net/builds/chromium/${version ?? PLAYWRIGHT_VERSION}/chromium-headless-shell-linux-arm64.zip`;
5026
4686
  }
5027
- if (type === "local") {
5028
- const newFilePath = path6.join(path6.dirname(filePath), sm);
5029
- return Promise.resolve(new SourceMapConsumer(readFileSync(newFilePath, "utf8")));
4687
+ if (chromeMode === "headless-shell") {
4688
+ return `https://storage.googleapis.com/chrome-for-testing-public/${version ?? TESTED_VERSION}/${platform2}/chrome-headless-shell-${platform2}.zip`;
5030
4689
  }
5031
- const index = filePath.lastIndexOf("/");
5032
- const url = filePath.substring(0, index + 1) + sm;
5033
- return getSourceMapFromRemoteUrl(url);
5034
- };
5035
- var fetchUrl = async (url) => {
5036
- const res = await readFile(url);
5037
- return new Promise((resolve, reject) => {
5038
- let downloaded = "";
5039
- res.on("data", (d) => {
5040
- downloaded += d;
5041
- });
5042
- res.on("end", () => {
5043
- resolve(downloaded);
4690
+ return `https://storage.googleapis.com/chrome-for-testing-public/${version ?? TESTED_VERSION}/${platform2}/chrome-${platform2}.zip`;
4691
+ }
4692
+ var mkdirAsync = fs6.promises.mkdir;
4693
+ var unlinkAsync = promisify(fs6.unlink.bind(fs6));
4694
+ function existsAsync(filePath) {
4695
+ return new Promise((resolve2) => {
4696
+ fs6.access(filePath, (err) => {
4697
+ return resolve2(!err);
5044
4698
  });
5045
- res.on("error", (err) => reject(err));
5046
4699
  });
5047
- };
5048
- function getLinesAround(line, count, lines) {
5049
- const result = [];
5050
- for (let index = Math.max(0, line - 1 - count) + 1;index <= Math.min(lines.length - 1, line - 1 + count); ++index) {
5051
- result.push({
5052
- lineNumber: index + 1,
5053
- content: lines[index],
5054
- highlight: index + 1 === line
5055
- });
5056
- }
5057
- return result;
5058
4700
  }
5059
- var getOriginalPosition = (source_map, line, column) => {
5060
- const result = source_map.originalPositionFor({
5061
- line,
5062
- column
5063
- });
5064
- return { line: result.line, column: result.column, source: result.source };
4701
+ var getPlatform = () => {
4702
+ const platform2 = os2.platform();
4703
+ switch (platform2) {
4704
+ case "darwin":
4705
+ return os2.arch() === "arm64" ? "mac-arm64" : "mac-x64";
4706
+ case "linux":
4707
+ return os2.arch() === "arm64" ? "linux-arm64" : "linux64";
4708
+ case "win32":
4709
+ return "win64";
4710
+ default:
4711
+ throw new Error("Unsupported platform: " + platform2);
4712
+ }
5065
4713
  };
5066
- var symbolicateStackTraceFromRemoteFrames = async (frames) => {
5067
- const uniqueFileNames = [
5068
- ...new Set(frames.map((f) => f.fileName).filter((f) => f.startsWith("http://") || f.startsWith("https://")).filter(truthy))
5069
- ];
5070
- const maps = await Promise.all(uniqueFileNames.map((fileName) => {
5071
- return getSourceMapFromRemoteFile(fileName);
5072
- }));
5073
- const mapValues = {};
5074
- for (let i = 0;i < uniqueFileNames.length; i++) {
5075
- mapValues[uniqueFileNames[i]] = maps[i];
4714
+ var getDownloadsFolder = (chromeMode) => {
4715
+ const destination = chromeMode === "headless-shell" ? "chrome-headless-shell" : "chrome-for-testing";
4716
+ return path8.join(getDownloadsCacheDir(), destination);
4717
+ };
4718
+ var downloadBrowser = async ({
4719
+ logLevel,
4720
+ indent,
4721
+ onProgress,
4722
+ version,
4723
+ chromeMode
4724
+ }) => {
4725
+ const platform2 = getPlatform();
4726
+ const downloadURL = getChromeDownloadUrl({ platform: platform2, version, chromeMode });
4727
+ const fileName = downloadURL.split("/").pop();
4728
+ if (!fileName) {
4729
+ throw new Error(`A malformed download URL was found: ${downloadURL}.`);
5076
4730
  }
5077
- return symbolicateFromSources(frames, mapValues);
4731
+ const downloadsFolder = getDownloadsFolder(chromeMode);
4732
+ const archivePath = path8.join(downloadsFolder, fileName);
4733
+ const outputPath = getFolderPath(downloadsFolder, platform2);
4734
+ if (await existsAsync(outputPath)) {
4735
+ return getRevisionInfo(chromeMode);
4736
+ }
4737
+ if (!await existsAsync(downloadsFolder)) {
4738
+ await mkdirAsync(downloadsFolder, {
4739
+ recursive: true
4740
+ });
4741
+ }
4742
+ if (os2.platform() !== "darwin" && os2.platform() !== "linux" && os2.arch() === "arm64") {
4743
+ throw new Error([
4744
+ "Chrome Headless Shell is not available for Windows for arm64 architecture."
4745
+ ].join(`
4746
+ `));
4747
+ }
4748
+ try {
4749
+ await downloadFile({
4750
+ url: downloadURL,
4751
+ to: () => archivePath,
4752
+ onProgress: (progress) => {
4753
+ if (progress.totalSize === null || progress.percent === null) {
4754
+ throw new Error("Expected totalSize and percent to be defined");
4755
+ }
4756
+ onProgress({
4757
+ downloadedBytes: progress.downloaded,
4758
+ totalSizeInBytes: progress.totalSize,
4759
+ percent: progress.percent
4760
+ });
4761
+ },
4762
+ indent,
4763
+ logLevel
4764
+ });
4765
+ await extractZip(archivePath, { dir: outputPath });
4766
+ const chromePath = path8.join(outputPath, "chrome-linux", "chrome");
4767
+ const chromeHeadlessShellPath = path8.join(outputPath, "chrome-linux", "chrome-headless-shell");
4768
+ if (fs6.existsSync(chromePath)) {
4769
+ fs6.renameSync(chromePath, chromeHeadlessShellPath);
4770
+ }
4771
+ const chromeLinuxFolder = path8.join(outputPath, "chrome-linux");
4772
+ if (fs6.existsSync(chromeLinuxFolder)) {
4773
+ fs6.renameSync(chromeLinuxFolder, path8.join(outputPath, "chrome-headless-shell-linux-arm64"));
4774
+ }
4775
+ } catch (err) {
4776
+ return Promise.reject(err);
4777
+ } finally {
4778
+ if (await existsAsync(archivePath)) {
4779
+ await unlinkAsync(archivePath);
4780
+ }
4781
+ }
4782
+ const revisionInfo = getRevisionInfo(chromeMode);
4783
+ makeFileExecutableIfItIsNot(revisionInfo.executablePath);
4784
+ return revisionInfo;
5078
4785
  };
5079
- var symbolicateFromSources = (frames, mapValues) => {
5080
- return frames.map((frame) => {
5081
- const map = mapValues[frame.fileName];
5082
- if (!map) {
5083
- return null;
4786
+ var getFolderPath = (downloadsFolder, platform2) => {
4787
+ return path8.resolve(downloadsFolder, platform2);
4788
+ };
4789
+ var getExecutablePath2 = (chromeMode) => {
4790
+ const downloadsFolder = getDownloadsFolder(chromeMode);
4791
+ const platform2 = getPlatform();
4792
+ const folderPath = getFolderPath(downloadsFolder, platform2);
4793
+ if (chromeMode === "chrome-for-testing") {
4794
+ if (platform2 === "mac-arm64" || platform2 === "mac-x64") {
4795
+ return path8.join(folderPath, `chrome-${platform2}`, "Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing");
5084
4796
  }
5085
- return symbolicateStackFrame(frame, map);
5086
- }).filter(truthy);
4797
+ if (platform2 === "win64") {
4798
+ return path8.join(folderPath, "chrome-win64", "chrome.exe");
4799
+ }
4800
+ if (platform2 === "linux64" || platform2 === "linux-arm64") {
4801
+ return path8.join(folderPath, "chrome-linux64", "chrome");
4802
+ }
4803
+ throw new Error("unsupported platform" + platform2);
4804
+ }
4805
+ if (chromeMode === "headless-shell") {
4806
+ return path8.join(folderPath, `chrome-headless-shell-${platform2}`, platform2 === "win64" ? "chrome-headless-shell.exe" : platform2 === "linux-arm64" ? "headless_shell" : "chrome-headless-shell");
4807
+ }
4808
+ throw new Error("unsupported chrome mode" + chromeMode);
5087
4809
  };
5088
- var symbolicateStackFrame = (frame, map) => {
5089
- const pos = getOriginalPosition(map, frame.lineNumber, frame.columnNumber);
5090
- const hasSource = pos.source ? map.sourceContentFor(pos.source, false) : null;
5091
- const scriptCode = hasSource && pos.line ? getLinesAround(pos.line, 3, hasSource.split(`
5092
- `)) : null;
4810
+ var getRevisionInfo = (chromeMode) => {
4811
+ const executablePath = getExecutablePath2(chromeMode);
4812
+ const downloadsFolder = getDownloadsFolder(chromeMode);
4813
+ const platform2 = getPlatform();
4814
+ const folderPath = getFolderPath(downloadsFolder, platform2);
4815
+ const url = getChromeDownloadUrl({ platform: platform2, version: null, chromeMode });
4816
+ const local = fs6.existsSync(folderPath);
5093
4817
  return {
5094
- originalColumnNumber: pos.column,
5095
- originalFileName: pos.source,
5096
- originalFunctionName: frame.functionName,
5097
- originalLineNumber: pos.line,
5098
- originalScriptCode: scriptCode
4818
+ executablePath,
4819
+ folderPath,
4820
+ local,
4821
+ url
5099
4822
  };
5100
4823
  };
5101
- var getSourceMapFromRemoteFile = async (fileName) => {
5102
- const fileContents = await fetchUrl(fileName);
5103
- return getSourceMap(fileName, fileContents, "remote");
5104
- };
5105
- var getSourceMapFromLocalFile = (fileName) => {
5106
- const fileContents = readFileSync(fileName, "utf8");
5107
- return getSourceMap(fileName, fileContents, "local");
5108
- };
5109
-
5110
- // src/error-handling/handle-javascript-exception.ts
5111
- import { NoReactInternals as NoReactInternals5 } from "remotion/no-react";
5112
-
5113
- // src/delay-render-embedded-stack.ts
5114
- import { NoReactInternals as NoReactInternals4 } from "remotion/no-react";
5115
4824
 
5116
- // src/parse-browser-error-stack.ts
5117
- var regexValidFrame_Chrome = /^\s*(at|in)\s.+(:\d+)/;
5118
- var regexValidFrame_FireFox = /(^|@)\S+:\d+|.+line\s+\d+\s+>\s+(eval|Function).+/;
5119
- var regexExtractLocation = /\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/;
5120
- function extractLocation(token) {
5121
- const execed = regexExtractLocation.exec(token);
5122
- if (!execed) {
5123
- throw new Error("Could not match in extractLocation");
4825
+ // src/ensure-browser.ts
4826
+ var currentEnsureBrowserOperation = Promise.resolve();
4827
+ var internalEnsureBrowserUncapped = async ({
4828
+ indent,
4829
+ logLevel,
4830
+ browserExecutable,
4831
+ onBrowserDownload,
4832
+ chromeMode
4833
+ }) => {
4834
+ const status = getBrowserStatus({ browserExecutable, chromeMode });
4835
+ if (status.type === "no-browser") {
4836
+ const { onProgress, version } = onBrowserDownload({ chromeMode });
4837
+ await downloadBrowser({ indent, logLevel, onProgress, version, chromeMode });
5124
4838
  }
5125
- return execed.slice(1).map((v) => {
5126
- const p = Number(v);
5127
- if (!isNaN(p)) {
5128
- return p;
5129
- }
5130
- return v;
5131
- });
5132
- }
5133
- var makeStackFrame = ({
5134
- functionName,
5135
- fileName,
5136
- lineNumber,
5137
- columnNumber
4839
+ const newStatus = getBrowserStatus({ browserExecutable, chromeMode });
4840
+ return newStatus;
4841
+ };
4842
+ var internalEnsureBrowser = (options) => {
4843
+ currentEnsureBrowserOperation = currentEnsureBrowserOperation.then(() => internalEnsureBrowserUncapped(options));
4844
+ return currentEnsureBrowserOperation;
4845
+ };
4846
+ var getBrowserStatus = ({
4847
+ browserExecutable,
4848
+ chromeMode
5138
4849
  }) => {
5139
- if (functionName && functionName.indexOf("Object.") === 0) {
5140
- functionName = functionName.slice("Object.".length);
4850
+ if (browserExecutable) {
4851
+ if (!fs7.existsSync(browserExecutable)) {
4852
+ throw new Error(`"browserExecutable" was specified as '${browserExecutable}' but the path doesn't exist. Pass "null" for "browserExecutable" to download a browser automatically.`);
4853
+ }
4854
+ return { path: browserExecutable, type: "user-defined-path" };
5141
4855
  }
5142
- if (functionName === "friendlySyntaxErrorLabel" || functionName === "exports.__esModule" || functionName === "<anonymous>" || !functionName) {
5143
- functionName = null;
4856
+ const revision = getRevisionInfo(chromeMode);
4857
+ if (revision.local && fs7.existsSync(revision.executablePath)) {
4858
+ return { path: revision.executablePath, type: "local-puppeteer-browser" };
5144
4859
  }
5145
- return {
5146
- columnNumber,
5147
- fileName,
5148
- functionName,
5149
- lineNumber
5150
- };
4860
+ return { type: "no-browser" };
5151
4861
  };
5152
- var parseStack = (stack) => {
5153
- const frames = stack.filter((e) => regexValidFrame_Chrome.test(e) || regexValidFrame_FireFox.test(e)).map((e) => {
5154
- if (regexValidFrame_FireFox.test(e)) {
5155
- let isEval = false;
5156
- if (/ > (eval|Function)/.test(e)) {
5157
- e = e.replace(/ line (\d+)(?: > eval line \d+)* > (eval|Function):\d+:\d+/g, ":$1");
5158
- isEval = true;
5159
- }
5160
- const _data = e.split(/[@]/g);
5161
- const _last = _data.pop();
5162
- if (!_last) {
5163
- throw new Error("could not get last");
5164
- }
5165
- const [_fileName, _lineNumber, _columnNumber] = extractLocation(_last);
5166
- return makeStackFrame({
5167
- functionName: _data.join("@") || (isEval ? "eval" : null),
5168
- fileName: _fileName,
5169
- lineNumber: _lineNumber,
5170
- columnNumber: _columnNumber
5171
- });
5172
- }
5173
- if (e.indexOf("(eval ") !== -1) {
5174
- e = e.replace(/(\(eval at [^()]*)|(\),.*$)/g, "");
5175
- }
5176
- if (e.indexOf("(at ") !== -1) {
5177
- e = e.replace(/\(at /, "(");
5178
- }
5179
- const data = e.trim().split(/\s+/g).slice(1);
5180
- const last = data.pop();
5181
- if (!last) {
5182
- throw new Error("could not get last");
5183
- }
5184
- const [fileName, lineNumber, columnNumber] = extractLocation(last);
5185
- return makeStackFrame({
5186
- functionName: data.join(" ") || null,
5187
- fileName,
5188
- lineNumber,
5189
- columnNumber
5190
- });
4862
+ var ensureBrowser = (options) => {
4863
+ const indent = false;
4864
+ const logLevel = options?.logLevel ?? "info";
4865
+ return internalEnsureBrowser({
4866
+ browserExecutable: options?.browserExecutable ?? null,
4867
+ indent,
4868
+ logLevel: options?.logLevel ?? "info",
4869
+ onBrowserDownload: options?.onBrowserDownload ?? defaultBrowserDownloadProgress({
4870
+ api: "ensureBrowser()",
4871
+ indent: false,
4872
+ logLevel
4873
+ }),
4874
+ chromeMode: options?.chromeMode ?? "headless-shell"
5191
4875
  });
5192
- return frames;
5193
4876
  };
5194
4877
 
5195
- // src/delay-render-embedded-stack.ts
5196
- var parseDelayRenderEmbeddedStack = (message) => {
5197
- const index = message.indexOf(NoReactInternals4.DELAY_RENDER_CALLSTACK_TOKEN);
5198
- if (index === -1) {
5199
- return null;
4878
+ // src/get-local-browser-executable.ts
4879
+ import fs8 from "node:fs";
4880
+ var getBrowserStatus2 = ({
4881
+ browserExecutablePath,
4882
+ indent,
4883
+ logLevel,
4884
+ chromeMode
4885
+ }) => {
4886
+ if (browserExecutablePath) {
4887
+ if (!fs8.existsSync(browserExecutablePath)) {
4888
+ Log.warn({ indent, logLevel }, `Browser executable was specified as '${browserExecutablePath}' but the path doesn't exist.`);
4889
+ }
4890
+ return { path: browserExecutablePath, type: "user-defined-path" };
5200
4891
  }
5201
- const msg = message.substring(index + NoReactInternals4.DELAY_RENDER_CALLSTACK_TOKEN.length).trim();
5202
- const parsed = parseStack(msg.split(`
5203
- `));
5204
- return parsed;
4892
+ const revision = getRevisionInfo(chromeMode);
4893
+ if (revision.local && fs8.existsSync(revision.executablePath)) {
4894
+ return { path: revision.executablePath, type: "local-puppeteer-browser" };
4895
+ }
4896
+ return { type: "no-browser" };
5205
4897
  };
5206
-
5207
- // src/error-handling/symbolicateable-error.ts
5208
- class SymbolicateableError extends Error {
5209
- stackFrame;
5210
- delayRenderCall;
5211
- frame;
5212
- chunk;
5213
- constructor({
5214
- message,
5215
- stack,
5216
- stackFrame,
5217
- frame,
5218
- name,
5219
- chunk
5220
- }) {
5221
- super(message);
5222
- this.stack = stack;
5223
- this.stackFrame = stackFrame;
5224
- this.frame = frame;
5225
- this.chunk = chunk;
5226
- this.name = name;
5227
- this.delayRenderCall = stack ? parseDelayRenderEmbeddedStack(stack) : null;
4898
+ var getLocalBrowserExecutable = ({
4899
+ preferredBrowserExecutable,
4900
+ logLevel,
4901
+ indent,
4902
+ chromeMode
4903
+ }) => {
4904
+ const status = getBrowserStatus2({
4905
+ browserExecutablePath: preferredBrowserExecutable,
4906
+ indent,
4907
+ logLevel,
4908
+ chromeMode
4909
+ });
4910
+ if (status.type === "no-browser") {
4911
+ throw new TypeError("No browser found for rendering frames! Please open a GitHub issue and describe " + "how you reached this error: https://remotion.dev/issue");
5228
4912
  }
5229
- }
4913
+ return status.path;
4914
+ };
5230
4915
 
5231
- // src/error-handling/handle-javascript-exception.ts
5232
- class ErrorWithStackFrame extends Error {
5233
- symbolicatedStackFrames;
5234
- frame;
5235
- chunk;
5236
- name;
5237
- delayRenderCall;
5238
- constructor({
5239
- message,
5240
- symbolicatedStackFrames,
5241
- frame,
5242
- name,
5243
- delayRenderCall,
5244
- stack,
5245
- chunk
5246
- }) {
5247
- super(message);
5248
- this.symbolicatedStackFrames = symbolicatedStackFrames;
5249
- this.frame = frame;
5250
- this.chunk = chunk;
5251
- this.name = name;
5252
- this.delayRenderCall = delayRenderCall;
5253
- this.stack = stack;
4916
+ // src/get-available-memory.ts
4917
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
4918
+ import { freemem } from "node:os";
4919
+ var getFreeMemoryFromProcMeminfo = () => {
4920
+ const data = readFileSync2("/proc/meminfo", "utf-8");
4921
+ const lines = data.split(`
4922
+ `);
4923
+ const memAvailableLine = lines.find((line) => line.startsWith("MemAvailable"));
4924
+ if (!memAvailableLine) {
4925
+ throw new Error("MemAvailable not found in /proc/meminfo");
5254
4926
  }
5255
- }
5256
- var cleanUpErrorMessage = (exception) => {
5257
- let errorMessage = exception.exceptionDetails.exception?.description;
5258
- if (!errorMessage) {
5259
- return null;
4927
+ const matches = memAvailableLine.match(/(\d+)\s+(\w+)/);
4928
+ if (!matches || matches.length !== 3) {
4929
+ throw new Error("Failed to parse MemAvailable value");
5260
4930
  }
5261
- const errorType = exception.exceptionDetails.exception?.className;
5262
- const prefix = `${errorType}: `;
5263
- if (errorMessage.startsWith(prefix)) {
5264
- errorMessage = errorMessage.substring(prefix.length);
4931
+ const value = parseInt(matches[1], 10);
4932
+ const unit = matches[2].toLowerCase();
4933
+ switch (unit) {
4934
+ case "kb":
4935
+ return value * 1024;
4936
+ case "mb":
4937
+ return value * 1024 * 1024;
4938
+ case "gb":
4939
+ return value * 1024 * 1024 * 1024;
4940
+ default:
4941
+ throw new Error(`Unknown unit: ${unit}`);
5265
4942
  }
5266
- const frames = exception.exceptionDetails.stackTrace?.callFrames.length ?? 0;
5267
- const split = errorMessage.split(`
5268
- `);
5269
- return split.slice(0, Math.max(1, split.length - frames)).join(`
5270
- `);
5271
4943
  };
5272
- var removeDelayRenderStack = (message) => {
5273
- const index = message.indexOf(NoReactInternals5.DELAY_RENDER_CALLSTACK_TOKEN);
5274
- if (index === -1) {
5275
- return message;
4944
+ var maxLambdaMemory = () => {
4945
+ if (process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE) {
4946
+ return parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE, 10) * 1024 * 1024;
5276
4947
  }
5277
- return message.substring(0, index);
5278
- };
5279
- var callFrameToStackFrame = (callFrame) => {
5280
- return {
5281
- columnNumber: callFrame.columnNumber,
5282
- fileName: callFrame.url,
5283
- functionName: callFrame.functionName,
5284
- lineNumber: callFrame.lineNumber
5285
- };
4948
+ return Infinity;
5286
4949
  };
5287
- var handleJavascriptException = ({
5288
- page,
5289
- onError,
5290
- frame
5291
- }) => {
5292
- const client = page._client();
5293
- const handler = (exception) => {
5294
- const rawErrorMessage = exception.exceptionDetails.exception?.description;
5295
- const cleanErrorMessage = cleanUpErrorMessage(exception);
5296
- if (!cleanErrorMessage) {
5297
- console.error(exception);
5298
- const err = new Error(rawErrorMessage);
5299
- err.stack = rawErrorMessage;
5300
- onError(err);
5301
- return;
5302
- }
5303
- if (!exception.exceptionDetails.stackTrace) {
5304
- const err = new Error(removeDelayRenderStack(cleanErrorMessage));
5305
- err.stack = rawErrorMessage;
5306
- onError(err);
5307
- return;
4950
+ var getAvailableMemory = (logLevel) => {
4951
+ const maxMemory = maxLambdaMemory();
4952
+ if (existsSync3("/proc/meminfo")) {
4953
+ try {
4954
+ const val = getFreeMemoryFromProcMeminfo();
4955
+ if (val !== null) {
4956
+ return Math.min(val, maxMemory);
4957
+ }
4958
+ } catch (err) {
4959
+ Log.warn({ indent: false, logLevel }, "Tried to get available memory from /proc/meminfo but failed. Falling back to os.freemem(). Error:");
4960
+ Log.warn({ indent: false, logLevel }, err);
5308
4961
  }
5309
- const errorType = exception.exceptionDetails.exception?.className;
5310
- const symbolicatedErr = new SymbolicateableError({
5311
- message: removeDelayRenderStack(cleanErrorMessage),
5312
- stackFrame: exception.exceptionDetails.stackTrace.callFrames.map((f) => callFrameToStackFrame(f)),
5313
- frame,
5314
- name: errorType,
5315
- stack: exception.exceptionDetails.exception?.description,
5316
- chunk: null
5317
- });
5318
- onError(symbolicatedErr);
5319
- };
5320
- client.on("Runtime.exceptionThrown", handler);
5321
- return () => {
5322
- client.off("Runtime.exceptionThrown", handler);
5323
- return Promise.resolve();
5324
- };
4962
+ }
4963
+ return Math.min(freemem(), maxMemory);
5325
4964
  };
5326
4965
 
5327
- // src/error-handling/symbolicate-error.ts
5328
- var symbolicateError = async (symbolicateableError) => {
5329
- const { delayRenderCall, stackFrame } = symbolicateableError;
5330
- const [mainErrorFrames, delayRenderFrames] = await Promise.all([
5331
- stackFrame ? symbolicateStackTraceFromRemoteFrames(stackFrame) : null,
5332
- delayRenderCall ? symbolicateStackTraceFromRemoteFrames(delayRenderCall) : null
5333
- ].filter(truthy));
5334
- const symbolicatedErr = new ErrorWithStackFrame({
5335
- message: symbolicateableError.message,
5336
- symbolicatedStackFrames: mainErrorFrames,
5337
- frame: symbolicateableError.frame,
5338
- name: symbolicateableError.name,
5339
- delayRenderCall: delayRenderFrames,
5340
- stack: symbolicateableError.stack,
5341
- chunk: symbolicateableError.chunk
5342
- });
5343
- return symbolicatedErr;
4966
+ // src/get-cpu-count.ts
4967
+ import { execSync as execSync2 } from "node:child_process";
4968
+ import { cpus } from "node:os";
4969
+ var getConcurrencyFromNProc = () => {
4970
+ try {
4971
+ return parseInt(execSync2("nproc", { stdio: "pipe" }).toString().trim(), 10);
4972
+ } catch {
4973
+ return null;
4974
+ }
4975
+ };
4976
+ var getCpuCount = () => {
4977
+ const node = cpus().length;
4978
+ const nproc = getConcurrencyFromNProc();
4979
+ if (nproc === null) {
4980
+ return node;
4981
+ }
4982
+ return Math.min(nproc, node);
5344
4983
  };
5345
4984
 
5346
- // src/frame-range.ts
5347
- var validateFrameRange = (frameRange) => {
5348
- if (frameRange === null) {
5349
- return;
4985
+ // src/get-video-threads-flag.ts
4986
+ var MEMORY_USAGE_PER_THREAD = 400000000;
4987
+ var RESERVED_MEMORY = 2000000000;
4988
+ var getIdealVideoThreadsFlag = (logLevel) => {
4989
+ const freeMemory = getAvailableMemory(logLevel);
4990
+ const cpus2 = getCpuCount();
4991
+ const maxRecommendedBasedOnCpus = cpus2 * 2 / 3;
4992
+ const maxRecommendedBasedOnMemory = (freeMemory - RESERVED_MEMORY) / MEMORY_USAGE_PER_THREAD;
4993
+ const maxRecommended = Math.min(maxRecommendedBasedOnCpus, maxRecommendedBasedOnMemory);
4994
+ return Math.max(1, Math.round(maxRecommended));
4995
+ };
4996
+
4997
+ // src/options/gl.tsx
4998
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
4999
+ var validOpenGlRenderers = [
5000
+ "swangle",
5001
+ "angle",
5002
+ "egl",
5003
+ "swiftshader",
5004
+ "vulkan",
5005
+ "angle-egl"
5006
+ ];
5007
+ var DEFAULT_OPENGL_RENDERER = null;
5008
+ var validateOpenGlRenderer = (option) => {
5009
+ if (option === null) {
5010
+ return null;
5350
5011
  }
5351
- if (typeof frameRange === "number") {
5352
- if (frameRange < 0) {
5353
- throw new TypeError("Frame must be a non-negative number, got " + frameRange);
5354
- }
5355
- if (!Number.isFinite(frameRange)) {
5356
- throw new TypeError("Frame must be a finite number, got " + frameRange);
5357
- }
5358
- if (!Number.isInteger(frameRange)) {
5359
- throw new Error(`Frame must be an integer, but got a float (${frameRange})`);
5360
- }
5361
- return;
5362
- }
5363
- if (Array.isArray(frameRange)) {
5364
- if (frameRange.length !== 2) {
5365
- throw new TypeError("Frame range must be a tuple, got an array with length " + frameRange.length);
5366
- }
5367
- for (const value of frameRange) {
5368
- if (typeof value !== "number") {
5369
- throw new Error(`Each value of frame range must be a number, but got ${typeof value} (${JSON.stringify(value)})`);
5370
- }
5371
- if (!Number.isFinite(value)) {
5372
- throw new TypeError("Each value of frame range must be finite, but got " + value);
5373
- }
5374
- if (!Number.isInteger(value)) {
5375
- throw new Error(`Each value of frame range must be an integer, but got a float (${value})`);
5376
- }
5377
- if (value < 0) {
5378
- throw new Error(`Each value of frame range must be non-negative, but got ${value}`);
5379
- }
5380
- }
5381
- const [first, second] = frameRange;
5382
- if (second < first) {
5383
- throw new Error("The second value of frame range must be not smaller than the first one, but got " + frameRange.join("-"));
5384
- }
5385
- return;
5386
- }
5387
- throw new TypeError("Frame range must be a number or a tuple of numbers, but got object of type " + typeof frameRange);
5388
- };
5389
-
5390
- // src/get-compositions.ts
5391
- import { NoReactInternals as NoReactInternals8 } from "remotion/no-react";
5392
-
5393
- // src/to-megabytes.ts
5394
- function toMegabytes(bytes) {
5395
- const mb = bytes / 1024 / 1024;
5396
- return `${Math.round(mb * 10) / 10} Mb`;
5397
- }
5398
-
5399
- // src/browser/browser-download-progress-bar.ts
5400
- var defaultBrowserDownloadProgress = ({
5401
- indent,
5402
- logLevel,
5403
- api
5404
- }) => ({ chromeMode }) => {
5405
- if (chromeMode === "chrome-for-testing") {
5406
- Log.info({ indent, logLevel }, "Downloading Chrome for Testing https://www.remotion.dev/chrome-for-testing");
5407
- } else {
5408
- Log.info({ indent, logLevel }, "Downloading Chrome Headless Shell https://www.remotion.dev/chrome-headless-shell");
5012
+ if (!validOpenGlRenderers.includes(option)) {
5013
+ throw new TypeError(`${option} is not a valid GL backend. Accepted values: ${validOpenGlRenderers.join(", ")}`);
5409
5014
  }
5410
- Log.info({ indent, logLevel }, `Customize this behavior by adding a onBrowserDownload function to ${api}.`);
5411
- let lastProgress = 0;
5412
- return {
5413
- onProgress: (progress) => {
5414
- if (progress.downloadedBytes > lastProgress + 1e7 || progress.percent === 1) {
5415
- lastProgress = progress.downloadedBytes;
5416
- if (chromeMode === "chrome-for-testing") {
5417
- Log.info({ indent, logLevel }, `Downloading Chrome for Testing - ${toMegabytes(progress.downloadedBytes)}/${toMegabytes(progress.totalSizeInBytes)}`);
5418
- } else {
5419
- Log.info({ indent, logLevel }, `Downloading Chrome Headless Shell - ${toMegabytes(progress.downloadedBytes)}/${toMegabytes(progress.totalSizeInBytes)}`);
5420
- }
5421
- }
5422
- },
5423
- version: null
5424
- };
5015
+ return option;
5425
5016
  };
5426
5017
 
5427
5018
  // src/open-browser.ts
5428
- import fs9 from "node:fs";
5429
- import os3 from "node:os";
5430
- import path9 from "node:path";
5431
-
5432
- // src/browser/Launcher.ts
5433
- var launchChrome = async ({
5434
- args,
5435
- executablePath,
5436
- defaultViewport,
5437
- indent,
5438
- logLevel,
5439
- userDataDir
5440
- }) => {
5441
- const timeout = 60000;
5442
- const browser = await HeadlessBrowser.create({
5443
- defaultViewport,
5444
- args,
5445
- executablePath,
5446
- timeout,
5447
- userDataDir,
5448
- logLevel,
5449
- indent
5450
- });
5451
- try {
5452
- await browser.waitForTarget((t) => {
5453
- return t.type() === "page";
5454
- }, { timeout });
5455
- } catch (error) {
5456
- await browser.close({ silent: false });
5457
- throw error;
5458
- }
5459
- return browser;
5460
- };
5461
-
5462
- // src/ensure-browser.ts
5463
- import fs7 from "fs";
5464
-
5465
- // src/browser/BrowserFetcher.ts
5466
- import * as fs6 from "node:fs";
5467
- import * as os2 from "node:os";
5468
- import * as path8 from "node:path";
5469
- import extractZip from "extract-zip";
5470
- import { promisify } from "node:util";
5471
-
5472
- // src/browser/get-download-destination.ts
5473
- import fs5 from "node:fs";
5474
- import path7 from "node:path";
5475
- var getDownloadsCacheDir = () => {
5476
- const cwd = process.cwd();
5477
- let dir = cwd;
5478
- for (;; ) {
5479
- try {
5480
- if (fs5.statSync(path7.join(dir, "package.json")).isFile()) {
5481
- break;
5482
- }
5483
- } catch (e) {
5484
- }
5485
- const parent = path7.dirname(dir);
5486
- if (dir === parent) {
5487
- dir = undefined;
5488
- break;
5489
- }
5490
- dir = parent;
5491
- }
5492
- if (!dir) {
5493
- return path7.resolve(cwd, ".remotion");
5494
- }
5495
- if (process.versions.pnp === "1") {
5496
- return path7.resolve(dir, ".pnp/.remotion");
5019
+ var featuresToEnable = (option) => {
5020
+ const renderer = option ?? DEFAULT_OPENGL_RENDERER;
5021
+ const enableAlways = ["NetworkService", "NetworkServiceInProcess"];
5022
+ if (renderer === "vulkan") {
5023
+ return [...enableAlways, "Vulkan", "UseSkiaRenderer"];
5497
5024
  }
5498
- if (process.versions.pnp === "3") {
5499
- return path7.resolve(dir, ".yarn/.remotion");
5025
+ if (renderer === "angle-egl") {
5026
+ return [...enableAlways, "VaapiVideoDecoder"];
5500
5027
  }
5501
- return path7.resolve(dir, "node_modules/.remotion");
5028
+ return enableAlways;
5502
5029
  };
5503
-
5504
- // src/browser/BrowserFetcher.ts
5505
- var TESTED_VERSION = "133.0.6943.0";
5506
- var PLAYWRIGHT_VERSION = "1155";
5507
- function getChromeDownloadUrl({
5508
- platform: platform2,
5509
- version,
5510
- chromeMode
5511
- }) {
5512
- if (platform2 === "linux-arm64") {
5513
- return `https://playwright.azureedge.net/builds/chromium/${version ?? PLAYWRIGHT_VERSION}/chromium-headless-shell-linux-arm64.zip`;
5030
+ var getOpenGlRenderer = (option) => {
5031
+ const renderer = option ?? DEFAULT_OPENGL_RENDERER;
5032
+ validateOpenGlRenderer(renderer);
5033
+ if (renderer === "swangle") {
5034
+ return ["--use-gl=angle", "--use-angle=swiftshader"];
5514
5035
  }
5515
- if (chromeMode === "headless-shell") {
5516
- return `https://storage.googleapis.com/chrome-for-testing-public/${version ?? TESTED_VERSION}/${platform2}/chrome-headless-shell-${platform2}.zip`;
5036
+ if (renderer === "angle-egl") {
5037
+ return ["--use-gl=angle", "--use-angle=gl-egl"];
5517
5038
  }
5518
- return `https://storage.googleapis.com/chrome-for-testing-public/${version ?? TESTED_VERSION}/${platform2}/chrome-${platform2}.zip`;
5519
- }
5520
- var mkdirAsync = fs6.promises.mkdir;
5521
- var unlinkAsync = promisify(fs6.unlink.bind(fs6));
5522
- function existsAsync(filePath) {
5523
- return new Promise((resolve2) => {
5524
- fs6.access(filePath, (err) => {
5525
- return resolve2(!err);
5526
- });
5527
- });
5528
- }
5529
- var getPlatform = () => {
5530
- const platform2 = os2.platform();
5531
- switch (platform2) {
5532
- case "darwin":
5533
- return os2.arch() === "arm64" ? "mac-arm64" : "mac-x64";
5534
- case "linux":
5535
- return os2.arch() === "arm64" ? "linux-arm64" : "linux64";
5536
- case "win32":
5537
- return "win64";
5538
- default:
5539
- throw new Error("Unsupported platform: " + platform2);
5039
+ if (renderer === "vulkan") {
5040
+ return [
5041
+ "--use-angle=vulkan",
5042
+ "--use-vulkan=native",
5043
+ "--disable-vulkan-fallback-to-gl-for-testing",
5044
+ "--disable-vulkan-surface",
5045
+ "--ignore-gpu-blocklist",
5046
+ "--enable-gpu"
5047
+ ];
5540
5048
  }
5049
+ if (renderer === null) {
5050
+ return [];
5051
+ }
5052
+ return [`--use-gl=${renderer}`];
5541
5053
  };
5542
- var getDownloadsFolder = (chromeMode) => {
5543
- const destination = chromeMode === "headless-shell" ? "chrome-headless-shell" : "chrome-for-testing";
5544
- return path8.join(getDownloadsCacheDir(), destination);
5545
- };
5546
- var downloadBrowser = async ({
5547
- logLevel,
5054
+ var internalOpenBrowser = async ({
5055
+ browser,
5056
+ browserExecutable,
5057
+ chromiumOptions,
5058
+ forceDeviceScaleFactor,
5548
5059
  indent,
5549
- onProgress,
5550
- version,
5060
+ viewport,
5061
+ logLevel,
5062
+ onBrowserDownload,
5551
5063
  chromeMode
5552
5064
  }) => {
5553
- const platform2 = getPlatform();
5554
- const downloadURL = getChromeDownloadUrl({ platform: platform2, version, chromeMode });
5555
- const fileName = downloadURL.split("/").pop();
5556
- if (!fileName) {
5557
- throw new Error(`A malformed download URL was found: ${downloadURL}.`);
5065
+ if (browser === "firefox") {
5066
+ throw new TypeError("Firefox supported is not yet turned on. Stay tuned for the future.");
5558
5067
  }
5559
- const downloadsFolder = getDownloadsFolder(chromeMode);
5560
- const archivePath = path8.join(downloadsFolder, fileName);
5561
- const outputPath = getFolderPath(downloadsFolder, platform2);
5562
- if (await existsAsync(outputPath)) {
5563
- return getRevisionInfo(chromeMode);
5068
+ await internalEnsureBrowser({
5069
+ browserExecutable,
5070
+ logLevel,
5071
+ indent,
5072
+ onBrowserDownload,
5073
+ chromeMode
5074
+ });
5075
+ const executablePath = getLocalBrowserExecutable({
5076
+ preferredBrowserExecutable: browserExecutable,
5077
+ logLevel,
5078
+ indent,
5079
+ chromeMode
5080
+ });
5081
+ const customGlRenderer = getOpenGlRenderer(chromiumOptions.gl ?? null);
5082
+ const enableMultiProcessOnLinux = chromiumOptions.enableMultiProcessOnLinux ?? true;
5083
+ Log.verbose({ indent, logLevel, tag: "openBrowser()" }, `Opening browser: gl = ${chromiumOptions.gl}, executable = ${executablePath}, enableMultiProcessOnLinux = ${enableMultiProcessOnLinux}`);
5084
+ if (chromiumOptions.userAgent) {
5085
+ Log.verbose({ indent, logLevel: "verbose", tag: "openBrowser()" }, `Using custom user agent: ${chromiumOptions.userAgent}`);
5564
5086
  }
5565
- if (!await existsAsync(downloadsFolder)) {
5566
- await mkdirAsync(downloadsFolder, {
5567
- recursive: true
5568
- });
5569
- }
5570
- if (os2.platform() !== "darwin" && os2.platform() !== "linux" && os2.arch() === "arm64") {
5571
- throw new Error([
5572
- "Chrome Headless Shell is not available for Windows for arm64 architecture."
5573
- ].join(`
5574
- `));
5575
- }
5576
- try {
5577
- await downloadFile({
5578
- url: downloadURL,
5579
- to: () => archivePath,
5580
- onProgress: (progress) => {
5581
- if (progress.totalSize === null || progress.percent === null) {
5582
- throw new Error("Expected totalSize and percent to be defined");
5583
- }
5584
- onProgress({
5585
- downloadedBytes: progress.downloaded,
5586
- totalSizeInBytes: progress.totalSize,
5587
- percent: progress.percent
5588
- });
5589
- },
5590
- indent,
5591
- logLevel
5592
- });
5593
- await extractZip(archivePath, { dir: outputPath });
5594
- const chromePath = path8.join(outputPath, "chrome-linux", "chrome");
5595
- const chromeHeadlessShellPath = path8.join(outputPath, "chrome-linux", "chrome-headless-shell");
5596
- if (fs6.existsSync(chromePath)) {
5597
- fs6.renameSync(chromePath, chromeHeadlessShellPath);
5598
- }
5599
- const chromeLinuxFolder = path8.join(outputPath, "chrome-linux");
5600
- if (fs6.existsSync(chromeLinuxFolder)) {
5601
- fs6.renameSync(chromeLinuxFolder, path8.join(outputPath, "chrome-headless-shell-linux-arm64"));
5602
- }
5603
- } catch (err) {
5604
- return Promise.reject(err);
5605
- } finally {
5606
- if (await existsAsync(archivePath)) {
5607
- await unlinkAsync(archivePath);
5608
- }
5609
- }
5610
- const revisionInfo = getRevisionInfo(chromeMode);
5611
- makeFileExecutableIfItIsNot(revisionInfo.executablePath);
5612
- return revisionInfo;
5613
- };
5614
- var getFolderPath = (downloadsFolder, platform2) => {
5615
- return path8.resolve(downloadsFolder, platform2);
5616
- };
5617
- var getExecutablePath2 = (chromeMode) => {
5618
- const downloadsFolder = getDownloadsFolder(chromeMode);
5619
- const platform2 = getPlatform();
5620
- const folderPath = getFolderPath(downloadsFolder, platform2);
5621
- if (chromeMode === "chrome-for-testing") {
5622
- if (platform2 === "mac-arm64" || platform2 === "mac-x64") {
5623
- return path8.join(folderPath, `chrome-${platform2}`, "Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing");
5624
- }
5625
- if (platform2 === "win64") {
5626
- return path8.join(folderPath, "chrome-win64", "chrome.exe");
5627
- }
5628
- if (platform2 === "linux64" || platform2 === "linux-arm64") {
5629
- return path8.join(folderPath, "chrome-linux64", "chrome");
5630
- }
5631
- throw new Error("unsupported platform" + platform2);
5632
- }
5633
- if (chromeMode === "headless-shell") {
5634
- return path8.join(folderPath, `chrome-headless-shell-${platform2}`, platform2 === "win64" ? "chrome-headless-shell.exe" : platform2 === "linux-arm64" ? "headless_shell" : "chrome-headless-shell");
5635
- }
5636
- throw new Error("unsupported chrome mode" + chromeMode);
5637
- };
5638
- var getRevisionInfo = (chromeMode) => {
5639
- const executablePath = getExecutablePath2(chromeMode);
5640
- const downloadsFolder = getDownloadsFolder(chromeMode);
5641
- const platform2 = getPlatform();
5642
- const folderPath = getFolderPath(downloadsFolder, platform2);
5643
- const url = getChromeDownloadUrl({ platform: platform2, version: null, chromeMode });
5644
- const local = fs6.existsSync(folderPath);
5645
- return {
5087
+ const userDataDir = await fs9.promises.mkdtemp(path9.join(os3.tmpdir(), "puppeteer_dev_chrome_profile-"));
5088
+ const browserInstance = await launchChrome({
5646
5089
  executablePath,
5647
- folderPath,
5648
- local,
5649
- url
5650
- };
5651
- };
5652
-
5653
- // src/ensure-browser.ts
5654
- var currentEnsureBrowserOperation = Promise.resolve();
5655
- var internalEnsureBrowserUncapped = async ({
5656
- indent,
5657
- logLevel,
5658
- browserExecutable,
5659
- onBrowserDownload,
5660
- chromeMode
5661
- }) => {
5662
- const status = getBrowserStatus({ browserExecutable, chromeMode });
5663
- if (status.type === "no-browser") {
5664
- const { onProgress, version } = onBrowserDownload({ chromeMode });
5665
- await downloadBrowser({ indent, logLevel, onProgress, version, chromeMode });
5666
- }
5667
- const newStatus = getBrowserStatus({ browserExecutable, chromeMode });
5668
- return newStatus;
5669
- };
5670
- var internalEnsureBrowser = (options) => {
5671
- currentEnsureBrowserOperation = currentEnsureBrowserOperation.then(() => internalEnsureBrowserUncapped(options));
5672
- return currentEnsureBrowserOperation;
5673
- };
5674
- var getBrowserStatus = ({
5675
- browserExecutable,
5676
- chromeMode
5677
- }) => {
5678
- if (browserExecutable) {
5679
- if (!fs7.existsSync(browserExecutable)) {
5680
- throw new Error(`"browserExecutable" was specified as '${browserExecutable}' but the path doesn't exist. Pass "null" for "browserExecutable" to download a browser automatically.`);
5090
+ logLevel,
5091
+ indent,
5092
+ userDataDir,
5093
+ args: [
5094
+ "about:blank",
5095
+ "--allow-pre-commit-input",
5096
+ "--disable-background-networking",
5097
+ `--enable-features=${featuresToEnable(chromiumOptions.gl).join(",")}`,
5098
+ "--disable-background-timer-throttling",
5099
+ "--disable-backgrounding-occluded-windows",
5100
+ "--disable-breakpad",
5101
+ "--disable-client-side-phishing-detection",
5102
+ "--disable-component-extensions-with-background-pages",
5103
+ "--disable-default-apps",
5104
+ "--disable-dev-shm-usage",
5105
+ "--no-proxy-server",
5106
+ "--proxy-server='direct://'",
5107
+ "--proxy-bypass-list=*",
5108
+ "--force-gpu-mem-available-mb=4096",
5109
+ "--disable-hang-monitor",
5110
+ "--disable-extensions",
5111
+ "--allow-chrome-scheme-url",
5112
+ "--disable-ipc-flooding-protection",
5113
+ "--disable-popup-blocking",
5114
+ "--disable-prompt-on-repost",
5115
+ "--disable-renderer-backgrounding",
5116
+ "--disable-sync",
5117
+ "--force-color-profile=srgb",
5118
+ "--metrics-recording-only",
5119
+ "--mute-audio",
5120
+ "--no-first-run",
5121
+ `--video-threads=${getIdealVideoThreadsFlag(logLevel)}`,
5122
+ "--enable-automation",
5123
+ "--password-store=basic",
5124
+ "--use-mock-keychain",
5125
+ "--enable-blink-features=IdleDetection",
5126
+ "--export-tagged-pdf",
5127
+ "--intensive-wake-up-throttling-policy=0",
5128
+ chromiumOptions.headless ?? true ? chromeMode === "chrome-for-testing" ? "--headless=new" : "--headless=old" : null,
5129
+ "--no-sandbox",
5130
+ "--disable-setuid-sandbox",
5131
+ ...customGlRenderer,
5132
+ "--disable-background-media-suspend",
5133
+ process.platform === "linux" && chromiumOptions.gl !== "vulkan" && !enableMultiProcessOnLinux ? "--single-process" : null,
5134
+ "--allow-running-insecure-content",
5135
+ "--disable-component-update",
5136
+ "--disable-domain-reliability",
5137
+ "--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process,Translate,BackForwardCache,AvoidUnnecessaryBeforeUnloadCheckSync,IntensiveWakeUpThrottling",
5138
+ "--disable-print-preview",
5139
+ "--disable-site-isolation-trials",
5140
+ "--disk-cache-size=268435456",
5141
+ "--hide-scrollbars",
5142
+ "--no-default-browser-check",
5143
+ "--no-pings",
5144
+ "--font-render-hinting=none",
5145
+ "--no-zygote",
5146
+ "--ignore-gpu-blocklist",
5147
+ "--enable-unsafe-webgpu",
5148
+ typeof forceDeviceScaleFactor === "undefined" ? null : `--force-device-scale-factor=${forceDeviceScaleFactor}`,
5149
+ chromiumOptions.ignoreCertificateErrors ? "--ignore-certificate-errors" : null,
5150
+ ...chromiumOptions?.disableWebSecurity ? ["--disable-web-security"] : [],
5151
+ chromiumOptions?.userAgent ? `--user-agent="${chromiumOptions.userAgent}"` : null,
5152
+ "--remote-debugging-port=0",
5153
+ `--user-data-dir=${userDataDir}`
5154
+ ].filter(Boolean),
5155
+ defaultViewport: viewport ?? {
5156
+ height: 720,
5157
+ width: 1280,
5158
+ deviceScaleFactor: 1
5681
5159
  }
5682
- return { path: browserExecutable, type: "user-defined-path" };
5683
- }
5684
- const revision = getRevisionInfo(chromeMode);
5685
- if (revision.local && fs7.existsSync(revision.executablePath)) {
5686
- return { path: revision.executablePath, type: "local-puppeteer-browser" };
5687
- }
5688
- return { type: "no-browser" };
5160
+ });
5161
+ const pages = await browserInstance.pages();
5162
+ await pages[0]?.close();
5163
+ return browserInstance;
5689
5164
  };
5690
- var ensureBrowser = (options) => {
5165
+ var openBrowser = (browser, options) => {
5166
+ const { browserExecutable, chromiumOptions, forceDeviceScaleFactor } = options ?? {};
5691
5167
  const indent = false;
5692
- const logLevel = options?.logLevel ?? "info";
5693
- return internalEnsureBrowser({
5694
- browserExecutable: options?.browserExecutable ?? null,
5168
+ const logLevel = options?.logLevel ?? (options?.shouldDumpIo ? "verbose" : "info");
5169
+ return internalOpenBrowser({
5170
+ browser,
5171
+ browserExecutable: browserExecutable ?? null,
5172
+ chromiumOptions: chromiumOptions ?? {},
5173
+ forceDeviceScaleFactor,
5695
5174
  indent,
5696
- logLevel: options?.logLevel ?? "info",
5697
- onBrowserDownload: options?.onBrowserDownload ?? defaultBrowserDownloadProgress({
5698
- api: "ensureBrowser()",
5699
- indent: false,
5700
- logLevel
5175
+ viewport: null,
5176
+ logLevel,
5177
+ onBrowserDownload: defaultBrowserDownloadProgress({
5178
+ indent,
5179
+ logLevel,
5180
+ api: "openBrowser()"
5701
5181
  }),
5702
5182
  chromeMode: options?.chromeMode ?? "headless-shell"
5703
5183
  });
5704
5184
  };
5705
5185
 
5706
- // src/get-local-browser-executable.ts
5707
- import fs8 from "node:fs";
5708
- var getBrowserStatus2 = ({
5709
- browserExecutablePath,
5186
+ // src/get-browser-instance.ts
5187
+ var getPageAndCleanupFn = async ({
5188
+ passedInInstance,
5189
+ browserExecutable,
5190
+ chromiumOptions,
5191
+ forceDeviceScaleFactor,
5710
5192
  indent,
5711
5193
  logLevel,
5712
- chromeMode
5713
- }) => {
5714
- if (browserExecutablePath) {
5715
- if (!fs8.existsSync(browserExecutablePath)) {
5716
- Log.warn({ indent, logLevel }, `Browser executable was specified as '${browserExecutablePath}' but the path doesn't exist.`);
5717
- }
5718
- return { path: browserExecutablePath, type: "user-defined-path" };
5719
- }
5720
- const revision = getRevisionInfo(chromeMode);
5721
- if (revision.local && fs8.existsSync(revision.executablePath)) {
5722
- return { path: revision.executablePath, type: "local-puppeteer-browser" };
5723
- }
5724
- return { type: "no-browser" };
5725
- };
5726
- var getLocalBrowserExecutable = ({
5727
- preferredBrowserExecutable,
5728
- logLevel,
5729
- indent,
5730
- chromeMode
5194
+ onBrowserDownload,
5195
+ chromeMode,
5196
+ pageIndex,
5197
+ onBrowserLog
5731
5198
  }) => {
5732
- const status = getBrowserStatus2({
5733
- browserExecutablePath: preferredBrowserExecutable,
5734
- indent,
5735
- logLevel,
5199
+ if (passedInInstance) {
5200
+ const page = await passedInInstance.newPage({
5201
+ context: () => null,
5202
+ logLevel,
5203
+ indent,
5204
+ pageIndex,
5205
+ onBrowserLog
5206
+ });
5207
+ return {
5208
+ page,
5209
+ cleanupPage: () => {
5210
+ page.close().catch((err) => {
5211
+ if (!err.message.includes("Target closed")) {
5212
+ Log.error({ indent, logLevel }, "Was not able to close puppeteer page", err);
5213
+ }
5214
+ });
5215
+ return Promise.resolve();
5216
+ }
5217
+ };
5218
+ }
5219
+ const browserInstance = await internalOpenBrowser({
5220
+ browser: DEFAULT_BROWSER,
5221
+ browserExecutable,
5222
+ chromiumOptions,
5223
+ forceDeviceScaleFactor,
5224
+ indent,
5225
+ viewport: null,
5226
+ logLevel,
5227
+ onBrowserDownload,
5736
5228
  chromeMode
5737
5229
  });
5738
- if (status.type === "no-browser") {
5739
- throw new TypeError("No browser found for rendering frames! Please open a GitHub issue and describe " + "how you reached this error: https://remotion.dev/issue");
5740
- }
5741
- return status.path;
5742
- };
5743
-
5744
- // src/get-available-memory.ts
5745
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
5746
- import { freemem } from "node:os";
5747
- var getFreeMemoryFromProcMeminfo = () => {
5748
- const data = readFileSync2("/proc/meminfo", "utf-8");
5749
- const lines = data.split(`
5750
- `);
5751
- const memAvailableLine = lines.find((line) => line.startsWith("MemAvailable"));
5752
- if (!memAvailableLine) {
5753
- throw new Error("MemAvailable not found in /proc/meminfo");
5754
- }
5755
- const matches = memAvailableLine.match(/(\d+)\s+(\w+)/);
5756
- if (!matches || matches.length !== 3) {
5757
- throw new Error("Failed to parse MemAvailable value");
5758
- }
5759
- const value = parseInt(matches[1], 10);
5760
- const unit = matches[2].toLowerCase();
5761
- switch (unit) {
5762
- case "kb":
5763
- return value * 1024;
5764
- case "mb":
5765
- return value * 1024 * 1024;
5766
- case "gb":
5767
- return value * 1024 * 1024 * 1024;
5768
- default:
5769
- throw new Error(`Unknown unit: ${unit}`);
5770
- }
5771
- };
5772
- var maxLambdaMemory = () => {
5773
- if (process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE) {
5774
- return parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE, 10) * 1024 * 1024;
5775
- }
5776
- return Infinity;
5777
- };
5778
- var getAvailableMemory = (logLevel) => {
5779
- const maxMemory = maxLambdaMemory();
5780
- if (existsSync3("/proc/meminfo")) {
5781
- try {
5782
- const val = getFreeMemoryFromProcMeminfo();
5783
- if (val !== null) {
5784
- return Math.min(val, maxMemory);
5785
- }
5786
- } catch (err) {
5787
- Log.warn({ indent: false, logLevel }, "Tried to get available memory from /proc/meminfo but failed. Falling back to os.freemem(). Error:");
5788
- Log.warn({ indent: false, logLevel }, err);
5230
+ const browserPage = await browserInstance.newPage({
5231
+ context: () => null,
5232
+ logLevel,
5233
+ indent,
5234
+ pageIndex,
5235
+ onBrowserLog
5236
+ });
5237
+ return {
5238
+ page: browserPage,
5239
+ cleanupPage: () => {
5240
+ browserInstance.close({ silent: true }).catch((err) => {
5241
+ if (!err.message.includes("Target closed")) {
5242
+ Log.error({ indent, logLevel }, "Was not able to close puppeteer page", err);
5243
+ }
5244
+ });
5245
+ return Promise.resolve();
5789
5246
  }
5790
- }
5791
- return Math.min(freemem(), maxMemory);
5247
+ };
5792
5248
  };
5793
5249
 
5794
- // src/get-cpu-count.ts
5795
- import { execSync as execSync2 } from "node:child_process";
5796
- import { cpus } from "node:os";
5797
- var getConcurrencyFromNProc = () => {
5798
- try {
5799
- return parseInt(execSync2("nproc", { stdio: "pipe" }).toString().trim(), 10);
5800
- } catch {
5801
- return null;
5802
- }
5803
- };
5804
- var getCpuCount = () => {
5805
- const node = cpus().length;
5806
- const nproc = getConcurrencyFromNProc();
5807
- if (nproc === null) {
5808
- return node;
5809
- }
5810
- return Math.min(nproc, node);
5811
- };
5250
+ // src/options/offthreadvideo-threads.tsx
5251
+ import { jsx as jsx2, jsxs as jsxs2, Fragment as Fragment2 } from "react/jsx-runtime";
5252
+ var DEFAULT_RENDER_FRAMES_OFFTHREAD_VIDEO_THREADS = 2;
5812
5253
 
5813
- // src/get-video-threads-flag.ts
5814
- var MEMORY_USAGE_PER_THREAD = 400000000;
5815
- var RESERVED_MEMORY = 2000000000;
5816
- var getIdealVideoThreadsFlag = (logLevel) => {
5817
- const freeMemory = getAvailableMemory(logLevel);
5818
- const cpus2 = getCpuCount();
5819
- const maxRecommendedBasedOnCpus = cpus2 * 2 / 3;
5820
- const maxRecommendedBasedOnMemory = (freeMemory - RESERVED_MEMORY) / MEMORY_USAGE_PER_THREAD;
5821
- const maxRecommended = Math.min(maxRecommendedBasedOnCpus, maxRecommendedBasedOnMemory);
5822
- return Math.max(1, Math.round(maxRecommended));
5823
- };
5254
+ // src/prepare-server.ts
5255
+ import { existsSync as existsSync4 } from "node:fs";
5256
+ import path18 from "node:path";
5257
+ import { NoReactInternals as NoReactInternals7 } from "remotion/no-react";
5824
5258
 
5825
- // src/options/gl.tsx
5826
- var jsx_runtime = __toESM(require_jsx_runtime(), 1);
5827
- var validOpenGlRenderers = [
5828
- "swangle",
5829
- "angle",
5830
- "egl",
5831
- "swiftshader",
5832
- "vulkan",
5833
- "angle-egl"
5834
- ];
5835
- var DEFAULT_OPENGL_RENDERER = null;
5836
- var validateOpenGlRenderer = (option) => {
5837
- if (option === null) {
5838
- return null;
5839
- }
5840
- if (!validOpenGlRenderers.includes(option)) {
5841
- throw new TypeError(`${option} is not a valid GL backend. Accepted values: ${validOpenGlRenderers.join(", ")}`);
5842
- }
5843
- return option;
5844
- };
5259
+ // src/assets/download-and-map-assets-to-file.ts
5260
+ import fs10 from "node:fs";
5261
+ import path11, { extname as extname2 } from "node:path";
5262
+ import { random } from "remotion/no-react";
5845
5263
 
5846
- // src/open-browser.ts
5847
- var featuresToEnable = (option) => {
5848
- const renderer = option ?? DEFAULT_OPENGL_RENDERER;
5849
- const enableAlways = ["NetworkService", "NetworkServiceInProcess"];
5850
- if (renderer === "vulkan") {
5851
- return [...enableAlways, "Vulkan", "UseSkiaRenderer"];
5264
+ // src/compress-assets.ts
5265
+ var compressAsset = (previousRenderAssets, newRenderAsset) => {
5266
+ if (newRenderAsset.src.length < 400) {
5267
+ return newRenderAsset;
5852
5268
  }
5853
- if (renderer === "angle-egl") {
5854
- return [...enableAlways, "VaapiVideoDecoder"];
5269
+ const assetWithSameSrc = previousRenderAssets.find((a) => a.src === newRenderAsset.src);
5270
+ if (!assetWithSameSrc) {
5271
+ return newRenderAsset;
5855
5272
  }
5856
- return enableAlways;
5273
+ return {
5274
+ ...newRenderAsset,
5275
+ src: `same-as-${assetWithSameSrc.id}-${assetWithSameSrc.frame}`
5276
+ };
5857
5277
  };
5858
- var getOpenGlRenderer = (option) => {
5859
- const renderer = option ?? DEFAULT_OPENGL_RENDERER;
5860
- validateOpenGlRenderer(renderer);
5861
- if (renderer === "swangle") {
5862
- return ["--use-gl=angle", "--use-angle=swiftshader"];
5863
- }
5864
- if (renderer === "angle-egl") {
5865
- return ["--use-gl=angle", "--use-angle=gl-egl"];
5866
- }
5867
- if (renderer === "vulkan") {
5868
- return [
5869
- "--use-angle=vulkan",
5870
- "--use-vulkan=native",
5871
- "--disable-vulkan-fallback-to-gl-for-testing",
5872
- "--disable-vulkan-surface",
5873
- "--ignore-gpu-blocklist",
5874
- "--enable-gpu"
5875
- ];
5876
- }
5877
- if (renderer === null) {
5878
- return [];
5879
- }
5880
- return [`--use-gl=${renderer}`];
5881
- };
5882
- var internalOpenBrowser = async ({
5883
- browser,
5884
- browserExecutable,
5885
- chromiumOptions,
5886
- forceDeviceScaleFactor,
5887
- indent,
5888
- viewport,
5889
- logLevel,
5890
- onBrowserDownload,
5891
- chromeMode
5892
- }) => {
5893
- if (browser === "firefox") {
5894
- throw new TypeError("Firefox supported is not yet turned on. Stay tuned for the future.");
5895
- }
5896
- await internalEnsureBrowser({
5897
- browserExecutable,
5898
- logLevel,
5899
- indent,
5900
- onBrowserDownload,
5901
- chromeMode
5902
- });
5903
- const executablePath = getLocalBrowserExecutable({
5904
- preferredBrowserExecutable: browserExecutable,
5905
- logLevel,
5906
- indent,
5907
- chromeMode
5908
- });
5909
- const customGlRenderer = getOpenGlRenderer(chromiumOptions.gl ?? null);
5910
- const enableMultiProcessOnLinux = chromiumOptions.enableMultiProcessOnLinux ?? true;
5911
- Log.verbose({ indent, logLevel, tag: "openBrowser()" }, `Opening browser: gl = ${chromiumOptions.gl}, executable = ${executablePath}, enableMultiProcessOnLinux = ${enableMultiProcessOnLinux}`);
5912
- if (chromiumOptions.userAgent) {
5913
- Log.verbose({ indent, logLevel: "verbose", tag: "openBrowser()" }, `Using custom user agent: ${chromiumOptions.userAgent}`);
5914
- }
5915
- const userDataDir = await fs9.promises.mkdtemp(path9.join(os3.tmpdir(), "puppeteer_dev_chrome_profile-"));
5916
- const browserInstance = await launchChrome({
5917
- executablePath,
5918
- logLevel,
5919
- indent,
5920
- userDataDir,
5921
- args: [
5922
- "about:blank",
5923
- "--allow-pre-commit-input",
5924
- "--disable-background-networking",
5925
- `--enable-features=${featuresToEnable(chromiumOptions.gl).join(",")}`,
5926
- "--disable-background-timer-throttling",
5927
- "--disable-backgrounding-occluded-windows",
5928
- "--disable-breakpad",
5929
- "--disable-client-side-phishing-detection",
5930
- "--disable-component-extensions-with-background-pages",
5931
- "--disable-default-apps",
5932
- "--disable-dev-shm-usage",
5933
- "--no-proxy-server",
5934
- "--proxy-server='direct://'",
5935
- "--proxy-bypass-list=*",
5936
- "--force-gpu-mem-available-mb=4096",
5937
- "--disable-hang-monitor",
5938
- "--disable-extensions",
5939
- "--allow-chrome-scheme-url",
5940
- "--disable-ipc-flooding-protection",
5941
- "--disable-popup-blocking",
5942
- "--disable-prompt-on-repost",
5943
- "--disable-renderer-backgrounding",
5944
- "--disable-sync",
5945
- "--force-color-profile=srgb",
5946
- "--metrics-recording-only",
5947
- "--mute-audio",
5948
- "--no-first-run",
5949
- `--video-threads=${getIdealVideoThreadsFlag(logLevel)}`,
5950
- "--enable-automation",
5951
- "--password-store=basic",
5952
- "--use-mock-keychain",
5953
- "--enable-blink-features=IdleDetection",
5954
- "--export-tagged-pdf",
5955
- "--intensive-wake-up-throttling-policy=0",
5956
- chromiumOptions.headless ?? true ? chromeMode === "chrome-for-testing" ? "--headless=new" : "--headless=old" : null,
5957
- "--no-sandbox",
5958
- "--disable-setuid-sandbox",
5959
- ...customGlRenderer,
5960
- "--disable-background-media-suspend",
5961
- process.platform === "linux" && chromiumOptions.gl !== "vulkan" && !enableMultiProcessOnLinux ? "--single-process" : null,
5962
- "--allow-running-insecure-content",
5963
- "--disable-component-update",
5964
- "--disable-domain-reliability",
5965
- "--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process,Translate,BackForwardCache,AvoidUnnecessaryBeforeUnloadCheckSync,IntensiveWakeUpThrottling",
5966
- "--disable-print-preview",
5967
- "--disable-site-isolation-trials",
5968
- "--disk-cache-size=268435456",
5969
- "--hide-scrollbars",
5970
- "--no-default-browser-check",
5971
- "--no-pings",
5972
- "--font-render-hinting=none",
5973
- "--no-zygote",
5974
- "--ignore-gpu-blocklist",
5975
- "--enable-unsafe-webgpu",
5976
- typeof forceDeviceScaleFactor === "undefined" ? null : `--force-device-scale-factor=${forceDeviceScaleFactor}`,
5977
- chromiumOptions.ignoreCertificateErrors ? "--ignore-certificate-errors" : null,
5978
- ...chromiumOptions?.disableWebSecurity ? ["--disable-web-security"] : [],
5979
- chromiumOptions?.userAgent ? `--user-agent="${chromiumOptions.userAgent}"` : null,
5980
- "--remote-debugging-port=0",
5981
- `--user-data-dir=${userDataDir}`
5982
- ].filter(Boolean),
5983
- defaultViewport: viewport ?? {
5984
- height: 720,
5985
- width: 1280,
5986
- deviceScaleFactor: 1
5987
- }
5988
- });
5989
- const pages = await browserInstance.pages();
5990
- await pages[0]?.close();
5991
- return browserInstance;
5992
- };
5993
- var openBrowser = (browser, options) => {
5994
- const { browserExecutable, chromiumOptions, forceDeviceScaleFactor } = options ?? {};
5995
- const indent = false;
5996
- const logLevel = options?.logLevel ?? (options?.shouldDumpIo ? "verbose" : "info");
5997
- return internalOpenBrowser({
5998
- browser,
5999
- browserExecutable: browserExecutable ?? null,
6000
- chromiumOptions: chromiumOptions ?? {},
6001
- forceDeviceScaleFactor,
6002
- indent,
6003
- viewport: null,
6004
- logLevel,
6005
- onBrowserDownload: defaultBrowserDownloadProgress({
6006
- indent,
6007
- logLevel,
6008
- api: "openBrowser()"
6009
- }),
6010
- chromeMode: options?.chromeMode ?? "headless-shell"
6011
- });
6012
- };
6013
-
6014
- // src/get-browser-instance.ts
6015
- var getPageAndCleanupFn = async ({
6016
- passedInInstance,
6017
- browserExecutable,
6018
- chromiumOptions,
6019
- forceDeviceScaleFactor,
6020
- indent,
6021
- logLevel,
6022
- onBrowserDownload,
6023
- chromeMode,
6024
- pageIndex,
6025
- onBrowserLog
6026
- }) => {
6027
- if (passedInInstance) {
6028
- const page = await passedInInstance.newPage({
6029
- context: () => null,
6030
- logLevel,
6031
- indent,
6032
- pageIndex,
6033
- onBrowserLog
6034
- });
6035
- return {
6036
- page,
6037
- cleanupPage: () => {
6038
- page.close().catch((err) => {
6039
- if (!err.message.includes("Target closed")) {
6040
- Log.error({ indent, logLevel }, "Was not able to close puppeteer page", err);
6041
- }
6042
- });
6043
- return Promise.resolve();
6044
- }
6045
- };
6046
- }
6047
- const browserInstance = await internalOpenBrowser({
6048
- browser: DEFAULT_BROWSER,
6049
- browserExecutable,
6050
- chromiumOptions,
6051
- forceDeviceScaleFactor,
6052
- indent,
6053
- viewport: null,
6054
- logLevel,
6055
- onBrowserDownload,
6056
- chromeMode
6057
- });
6058
- const browserPage = await browserInstance.newPage({
6059
- context: () => null,
6060
- logLevel,
6061
- indent,
6062
- pageIndex,
6063
- onBrowserLog
6064
- });
6065
- return {
6066
- page: browserPage,
6067
- cleanupPage: () => {
6068
- browserInstance.close({ silent: true }).catch((err) => {
6069
- if (!err.message.includes("Target closed")) {
6070
- Log.error({ indent, logLevel }, "Was not able to close puppeteer page", err);
6071
- }
6072
- });
6073
- return Promise.resolve();
6074
- }
6075
- };
6076
- };
6077
-
6078
- // src/options/offthreadvideo-threads.tsx
6079
- var jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
6080
- var DEFAULT_RENDER_FRAMES_OFFTHREAD_VIDEO_THREADS = 2;
6081
-
6082
- // src/prepare-server.ts
6083
- import { existsSync as existsSync4 } from "node:fs";
6084
- import path18 from "node:path";
6085
- import { NoReactInternals as NoReactInternals7 } from "remotion/no-react";
6086
-
6087
- // src/assets/download-and-map-assets-to-file.ts
6088
- import fs10 from "node:fs";
6089
- import path11, { extname as extname2 } from "node:path";
6090
- import { random } from "remotion/no-react";
6091
-
6092
- // src/compress-assets.ts
6093
- var compressAsset = (previousRenderAssets, newRenderAsset) => {
6094
- if (newRenderAsset.src.length < 400) {
6095
- return newRenderAsset;
6096
- }
6097
- const assetWithSameSrc = previousRenderAssets.find((a) => a.src === newRenderAsset.src);
6098
- if (!assetWithSameSrc) {
6099
- return newRenderAsset;
6100
- }
6101
- return {
6102
- ...newRenderAsset,
6103
- src: `same-as-${assetWithSameSrc.id}-${assetWithSameSrc.frame}`
6104
- };
6105
- };
6106
- var isAssetCompressed = (src) => {
6107
- return src.startsWith("same-as");
5278
+ var isAssetCompressed = (src) => {
5279
+ return src.startsWith("same-as");
6108
5280
  };
6109
5281
 
6110
5282
  // src/mime-types.ts
@@ -15494,7 +14666,7 @@ ${parsed.backtrace}`));
15494
14666
  };
15495
14667
 
15496
14668
  // src/options/offthreadvideo-cache-size.tsx
15497
- var jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
14669
+ import { jsx as jsx3, jsxs as jsxs3, Fragment as Fragment3 } from "react/jsx-runtime";
15498
14670
  var validateOffthreadVideoCacheSizeInBytes = (option) => {
15499
14671
  if (option === undefined || option === null) {
15500
14672
  return;
@@ -16799,7 +15971,7 @@ var seekToFrame = async ({
16799
15971
  };
16800
15972
 
16801
15973
  // src/set-props-and-env.ts
16802
- import { VERSION as VERSION5 } from "remotion/version";
15974
+ import { VERSION as VERSION2 } from "remotion/version";
16803
15975
 
16804
15976
  // src/goto-page-or-throw.ts
16805
15977
  var gotoPageOrThrow = async (page, urlToVisit, actualTimeout) => {
@@ -16988,7 +16160,7 @@ var innerSetPropsAndEnv = async ({
16988
16160
  throw new Error([
16989
16161
  `Incompatible site: When visiting ${urlToVisit}, a bundle was found, but one that is not compatible with this version of Remotion. Found version: ${siteVersion} - Required version: ${requiredVersion}. To resolve this error:`,
16990
16162
  "When using server-side rendering:",
16991
- ` ▸ Use 'bundle()' with '@remotion/bundler' of version ${VERSION5} to create a compatible bundle.`,
16163
+ ` ▸ Use 'bundle()' with '@remotion/bundler' of version ${VERSION2} to create a compatible bundle.`,
16992
16164
  "When using the Remotion Lambda:",
16993
16165
  " ▸ Use `npx remotion lambda sites create` to redeploy the site with the latest version.",
16994
16166
  " ℹ Use --site-name with the same name as before to overwrite your site.",
@@ -16996,13 +16168,13 @@ var innerSetPropsAndEnv = async ({
16996
16168
  ].join(`
16997
16169
  `));
16998
16170
  }
16999
- if (remotionVersion !== VERSION5 && true) {
16171
+ if (remotionVersion !== VERSION2 && true) {
17000
16172
  if (remotionVersion) {
17001
16173
  Log.warn({
17002
16174
  indent,
17003
16175
  logLevel
17004
16176
  }, [
17005
- `The site was bundled with version ${remotionVersion} of @remotion/bundler, while @remotion/renderer is on version ${VERSION5}. You may not have the newest bugfixes and features.`,
16177
+ `The site was bundled with version ${remotionVersion} of @remotion/bundler, while @remotion/renderer is on version ${VERSION2}. You may not have the newest bugfixes and features.`,
17006
16178
  `To resolve this warning:`,
17007
16179
  "▸ Use `npx remotion lambda sites create` to redeploy the site with the latest version.",
17008
16180
  " ℹ Use --site-name with the same name as before to overwrite your site.",
@@ -17013,7 +16185,7 @@ var innerSetPropsAndEnv = async ({
17013
16185
  Log.warn({
17014
16186
  indent,
17015
16187
  logLevel
17016
- }, `The site was bundled with an old version of Remotion, while @remotion/renderer is on version ${VERSION5}. You may not have the newest bugfixes and features. Re-bundle the site to fix this issue.`);
16188
+ }, `The site was bundled with an old version of Remotion, while @remotion/renderer is on version ${VERSION2}. You may not have the newest bugfixes and features. Re-bundle the site to fix this issue.`);
17017
16189
  }
17018
16190
  }
17019
16191
  };
@@ -17126,6 +16298,14 @@ var printUsefulErrorMessage = (err, logLevel, indent) => {
17126
16298
  Log.info({ indent, logLevel }, "\uD83D\uDCA1 On Lambda, one reason this could happen is that Chrome is rejecting an asset to be loaded when it is running low on disk space.");
17127
16299
  Log.info({ indent, logLevel }, "Try increasing the disk size of your Lambda function.");
17128
16300
  }
16301
+ if (err.message.includes("Invalid value specified for cpu")) {
16302
+ Log.info({ indent, logLevel });
16303
+ Log.info({ indent, logLevel }, "\uD83D\uDCA1 This error indicates that your GCP account does have a limit. Try setting `--maxInstances=5` / `maxInstances: 5` when deploying this service.");
16304
+ Log.info({
16305
+ indent,
16306
+ logLevel
16307
+ });
16308
+ }
17129
16309
  };
17130
16310
 
17131
16311
  // src/wrap-with-error-handling.ts
@@ -17372,332 +16552,810 @@ var resolveConcurrency = (userPreference) => {
17372
16552
  } else {
17373
16553
  rounded = Math.floor(userPreference);
17374
16554
  }
17375
- if (rounded > maxCpus) {
17376
- throw new Error(`Maximum for --concurrency is ${maxCpus} (number of cores on this system)`);
16555
+ if (rounded > maxCpus) {
16556
+ throw new Error(`Maximum for --concurrency is ${maxCpus} (number of cores on this system)`);
16557
+ }
16558
+ if (rounded < min) {
16559
+ throw new Error(`Minimum for concurrency is ${min}.`);
16560
+ }
16561
+ return rounded;
16562
+ };
16563
+
16564
+ // src/get-duration-from-frame-range.ts
16565
+ var getFramesToRender = (frameRange, everyNthFrame) => {
16566
+ if (everyNthFrame === 0) {
16567
+ throw new Error("everyNthFrame cannot be 0");
16568
+ }
16569
+ return new Array(frameRange[1] - frameRange[0] + 1).fill(true).map((_, index) => {
16570
+ return index + frameRange[0];
16571
+ }).filter((index) => {
16572
+ return index % everyNthFrame === 0;
16573
+ });
16574
+ };
16575
+
16576
+ // src/get-extension-from-codec.ts
16577
+ var getFileExtensionFromCodec = (codec, audioCodec) => {
16578
+ if (!validCodecs.includes(codec)) {
16579
+ throw new Error(`Codec must be one of the following: ${validCodecs.join(", ")}, but got ${codec}`);
16580
+ }
16581
+ const map2 = defaultFileExtensionMap[codec];
16582
+ if (audioCodec === null) {
16583
+ return map2.default;
16584
+ }
16585
+ const typedAudioCodec = audioCodec;
16586
+ if (!(typedAudioCodec in map2.forAudioCodec)) {
16587
+ throw new Error(`Audio codec ${typedAudioCodec} is not supported for codec ${codec}`);
16588
+ }
16589
+ return map2.forAudioCodec[audioCodec].default;
16590
+ };
16591
+ var makeFileExtensionMap = () => {
16592
+ const map2 = {};
16593
+ Object.keys(defaultFileExtensionMap).forEach((_codec) => {
16594
+ const codec = _codec;
16595
+ const fileExtMap = defaultFileExtensionMap[codec];
16596
+ const audioCodecs = Object.keys(fileExtMap.forAudioCodec);
16597
+ const possibleExtensionsForAudioCodec = audioCodecs.map((audioCodec) => fileExtMap.forAudioCodec[audioCodec].possible);
16598
+ const allPossibleExtensions = [
16599
+ fileExtMap.default,
16600
+ ...possibleExtensionsForAudioCodec.flat(1)
16601
+ ];
16602
+ for (const extension of allPossibleExtensions) {
16603
+ if (!map2[extension]) {
16604
+ map2[extension] = [];
16605
+ }
16606
+ if (!map2[extension].includes(codec)) {
16607
+ map2[extension].push(codec);
16608
+ }
16609
+ }
16610
+ });
16611
+ return map2;
16612
+ };
16613
+ var defaultCodecsForFileExtension = {
16614
+ "3gp": "aac",
16615
+ aac: "aac",
16616
+ gif: "gif",
16617
+ hevc: "h265",
16618
+ m4a: "aac",
16619
+ m4b: "aac",
16620
+ mkv: "h264-mkv",
16621
+ mov: "prores",
16622
+ mp3: "mp3",
16623
+ mp4: "h264",
16624
+ mpeg: "aac",
16625
+ mpg: "aac",
16626
+ mxf: "prores",
16627
+ wav: "wav",
16628
+ webm: "vp8",
16629
+ ts: "h264-ts"
16630
+ };
16631
+
16632
+ // src/path-normalize.ts
16633
+ var SLASH = 47;
16634
+ var DOT = 46;
16635
+ var assertPath = (path19) => {
16636
+ const t = typeof path19;
16637
+ if (t !== "string") {
16638
+ throw new TypeError(`Expected a string, got a ${t}`);
16639
+ }
16640
+ };
16641
+ var posixNormalize = (path19, allowAboveRoot) => {
16642
+ let res = "";
16643
+ let lastSegmentLength = 0;
16644
+ let lastSlash = -1;
16645
+ let dots = 0;
16646
+ let code;
16647
+ for (let i = 0;i <= path19.length; ++i) {
16648
+ if (i < path19.length) {
16649
+ code = path19.charCodeAt(i);
16650
+ } else if (code === SLASH) {
16651
+ break;
16652
+ } else {
16653
+ code = SLASH;
16654
+ }
16655
+ if (code === SLASH) {
16656
+ if (lastSlash === i - 1 || dots === 1) {
16657
+ } else if (lastSlash !== i - 1 && dots === 2) {
16658
+ if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== DOT || res.charCodeAt(res.length - 2) !== DOT) {
16659
+ if (res.length > 2) {
16660
+ const lastSlashIndex = res.lastIndexOf("/");
16661
+ if (lastSlashIndex !== res.length - 1) {
16662
+ if (lastSlashIndex === -1) {
16663
+ res = "";
16664
+ lastSegmentLength = 0;
16665
+ } else {
16666
+ res = res.slice(0, lastSlashIndex);
16667
+ lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
16668
+ }
16669
+ lastSlash = i;
16670
+ dots = 0;
16671
+ continue;
16672
+ }
16673
+ } else if (res.length === 2 || res.length === 1) {
16674
+ res = "";
16675
+ lastSegmentLength = 0;
16676
+ lastSlash = i;
16677
+ dots = 0;
16678
+ continue;
16679
+ }
16680
+ }
16681
+ if (allowAboveRoot) {
16682
+ if (res.length > 0) {
16683
+ res += "/..";
16684
+ } else {
16685
+ res = "..";
16686
+ }
16687
+ lastSegmentLength = 2;
16688
+ }
16689
+ } else {
16690
+ if (res.length > 0) {
16691
+ res += "/" + path19.slice(lastSlash + 1, i);
16692
+ } else {
16693
+ res = path19.slice(lastSlash + 1, i);
16694
+ }
16695
+ lastSegmentLength = i - lastSlash - 1;
16696
+ }
16697
+ lastSlash = i;
16698
+ dots = 0;
16699
+ } else if (code === DOT && dots !== -1) {
16700
+ ++dots;
16701
+ } else {
16702
+ dots = -1;
16703
+ }
16704
+ }
16705
+ return res;
16706
+ };
16707
+ var decode = (s) => {
16708
+ try {
16709
+ return decodeURIComponent(s);
16710
+ } catch {
16711
+ return s;
16712
+ }
16713
+ };
16714
+ var pathNormalize = (p) => {
16715
+ assertPath(p);
16716
+ let path19 = p;
16717
+ if (path19.length === 0) {
16718
+ return ".";
16719
+ }
16720
+ const isAbsolute = path19.charCodeAt(0) === SLASH;
16721
+ const trailingSeparator = path19.charCodeAt(path19.length - 1) === SLASH;
16722
+ path19 = decode(path19);
16723
+ path19 = posixNormalize(path19, !isAbsolute);
16724
+ if (path19.length === 0 && !isAbsolute) {
16725
+ path19 = ".";
16726
+ }
16727
+ if (path19.length > 0 && trailingSeparator) {
16728
+ path19 += "/";
16729
+ }
16730
+ if (isAbsolute) {
16731
+ return "/" + path19;
16732
+ }
16733
+ return path19;
16734
+ };
16735
+
16736
+ // src/get-extension-of-filename.ts
16737
+ var getExtensionOfFilename = (filename) => {
16738
+ if (filename === null) {
16739
+ return null;
16740
+ }
16741
+ const filenameArr = pathNormalize(filename).split(".");
16742
+ const hasExtension = filenameArr.length >= 2;
16743
+ const filenameArrLength = filenameArr.length;
16744
+ const extension = hasExtension ? filenameArr[filenameArrLength - 1] : null;
16745
+ return extension;
16746
+ };
16747
+
16748
+ // src/get-frame-to-render.ts
16749
+ var getRealFrameRange = (durationInFrames, frameRange) => {
16750
+ if (frameRange === null) {
16751
+ return [0, durationInFrames - 1];
16752
+ }
16753
+ if (typeof frameRange === "number") {
16754
+ if (frameRange < 0 || frameRange >= durationInFrames) {
16755
+ throw new Error(`Frame number is out of range, must be between 0 and ${durationInFrames - 1} but got ${frameRange}`);
16756
+ }
16757
+ return [frameRange, frameRange];
16758
+ }
16759
+ if (frameRange[1] >= durationInFrames || frameRange[0] < 0) {
16760
+ throw new Error(`The "durationInFrames" of the <Composition /> was evaluated to be ${durationInFrames}, but frame range ${frameRange.join("-")} is not inbetween 0-${durationInFrames - 1}`);
16761
+ }
16762
+ return frameRange;
16763
+ };
16764
+
16765
+ // src/image-format.ts
16766
+ var validVideoImageFormats = ["png", "jpeg", "none"];
16767
+ var validStillImageFormats = ["png", "jpeg", "pdf", "webp"];
16768
+ var DEFAULT_VIDEO_IMAGE_FORMAT = "jpeg";
16769
+ var DEFAULT_STILL_IMAGE_FORMAT = "png";
16770
+ var validateSelectedPixelFormatAndImageFormatCombination = (pixelFormat, videoImageFormat) => {
16771
+ if (videoImageFormat === "none") {
16772
+ return "none";
16773
+ }
16774
+ if (typeof pixelFormat === "undefined") {
16775
+ return "valid";
16776
+ }
16777
+ if (!validVideoImageFormats.includes(videoImageFormat)) {
16778
+ throw new TypeError(`Value ${videoImageFormat} is not valid as an image format.`);
16779
+ }
16780
+ if (pixelFormat !== "yuva420p" && pixelFormat !== "yuva444p10le") {
16781
+ return "valid";
16782
+ }
16783
+ if (videoImageFormat !== "png") {
16784
+ throw new TypeError(`Pixel format was set to '${pixelFormat}' but the image format is not PNG. To render transparent videos, you need to set PNG as the image format.`);
16785
+ }
16786
+ return "valid";
16787
+ };
16788
+ var validateStillImageFormat = (imageFormat) => {
16789
+ if (!validStillImageFormats.includes(imageFormat)) {
16790
+ throw new TypeError(String(`Image format should be one of: ${validStillImageFormats.map((v) => `"${v}"`).join(", ")}`));
16791
+ }
16792
+ };
16793
+
16794
+ // src/jpeg-quality.ts
16795
+ var DEFAULT_JPEG_QUALITY = 80;
16796
+ var validateJpegQuality = (q) => {
16797
+ if (typeof q !== "undefined" && typeof q !== "number") {
16798
+ throw new Error(`JPEG Quality option must be a number or undefined. Got ${typeof q} (${JSON.stringify(q)})`);
16799
+ }
16800
+ if (typeof q === "undefined") {
16801
+ return;
16802
+ }
16803
+ if (!Number.isFinite(q)) {
16804
+ throw new RangeError(`JPEG Quality must be a finite number, but is ${q}`);
16805
+ }
16806
+ if (Number.isNaN(q)) {
16807
+ throw new RangeError(`JPEG Quality is NaN, but must be a real number`);
16808
+ }
16809
+ if (q > 100 || q < 0) {
16810
+ throw new RangeError("JPEG Quality option must be between 0 and 100.");
16811
+ }
16812
+ };
16813
+
16814
+ // src/perf.ts
16815
+ var exports_perf = {};
16816
+ __export(exports_perf, {
16817
+ stopPerfMeasure: () => stopPerfMeasure,
16818
+ startPerfMeasure: () => startPerfMeasure,
16819
+ getPerf: () => getPerf
16820
+ });
16821
+ var perf = {
16822
+ "activate-target": [],
16823
+ capture: [],
16824
+ save: [],
16825
+ "extract-frame": [],
16826
+ piping: []
16827
+ };
16828
+ var map2 = {};
16829
+ var startPerfMeasure = (marker) => {
16830
+ const id = Math.random();
16831
+ map2[id] = {
16832
+ id,
16833
+ marker,
16834
+ start: Date.now()
16835
+ };
16836
+ return id;
16837
+ };
16838
+ var stopPerfMeasure = (id) => {
16839
+ const now = Date.now();
16840
+ const diff = now - map2[id].start;
16841
+ perf[map2[id].marker].push(diff);
16842
+ delete map2[id];
16843
+ };
16844
+ var getPerf = () => {
16845
+ return [
16846
+ "Render performance:",
16847
+ ...Object.keys(perf).filter((p) => perf[p].length).map((p) => {
16848
+ return ` ${p} => ${Math.round(perf[p].reduce((a, b) => a + b, 0) / perf[p].length)}ms (n = ${perf[p].length})`;
16849
+ })
16850
+ ];
16851
+ };
16852
+
16853
+ // src/pixel-format.ts
16854
+ var validPixelFormats = [
16855
+ "yuv420p",
16856
+ "yuva420p",
16857
+ "yuv422p",
16858
+ "yuv444p",
16859
+ "yuv420p10le",
16860
+ "yuv422p10le",
16861
+ "yuv444p10le",
16862
+ "yuva444p10le"
16863
+ ];
16864
+ var DEFAULT_PIXEL_FORMAT = "yuv420p";
16865
+ var validPixelFormatsForCodec = (codec) => {
16866
+ if (codec === "vp8" || codec === "vp9") {
16867
+ return validPixelFormats;
16868
+ }
16869
+ return validPixelFormats.filter((format) => format !== "yuva420p");
16870
+ };
16871
+ var validateSelectedPixelFormatAndCodecCombination = (pixelFormat, codec) => {
16872
+ if (typeof pixelFormat === "undefined") {
16873
+ return pixelFormat;
16874
+ }
16875
+ if (!validPixelFormats.includes(pixelFormat)) {
16876
+ throw new TypeError(`Value ${pixelFormat} is not valid as a pixel format.`);
16877
+ }
16878
+ if (pixelFormat !== "yuva420p") {
16879
+ return;
17377
16880
  }
17378
- if (rounded < min) {
17379
- throw new Error(`Minimum for concurrency is ${min}.`);
16881
+ const validFormats = validPixelFormatsForCodec(codec);
16882
+ if (!validFormats.includes(pixelFormat)) {
16883
+ throw new TypeError(`Pixel format was set to 'yuva420p' but codec ${codec} does not support it. Valid pixel formats for codec ${codec} are: ${validFormats.join(", ")}.`);
17380
16884
  }
17381
- return rounded;
17382
16885
  };
17383
16886
 
17384
- // src/get-duration-from-frame-range.ts
17385
- var getFramesToRender = (frameRange, everyNthFrame) => {
17386
- if (everyNthFrame === 0) {
17387
- throw new Error("everyNthFrame cannot be 0");
17388
- }
17389
- return new Array(frameRange[1] - frameRange[0] + 1).fill(true).map((_, index) => {
17390
- return index + frameRange[0];
17391
- }).filter((index) => {
17392
- return index % everyNthFrame === 0;
17393
- });
17394
- };
16887
+ // src/render-frames.ts
16888
+ import fs14 from "node:fs";
16889
+ import path20 from "node:path";
16890
+ import { NoReactInternals as NoReactInternals12 } from "remotion/no-react";
17395
16891
 
17396
- // src/path-normalize.ts
17397
- var SLASH = 47;
17398
- var DOT = 46;
17399
- var assertPath = (path19) => {
17400
- const t = typeof path19;
17401
- if (t !== "string") {
17402
- throw new TypeError(`Expected a string, got a ${t}`);
16892
+ // src/cycle-browser-tabs.ts
16893
+ var cycleBrowserTabs = ({
16894
+ puppeteerInstance,
16895
+ concurrency,
16896
+ logLevel,
16897
+ indent
16898
+ }) => {
16899
+ if (concurrency <= 1) {
16900
+ return {
16901
+ stopCycling: () => {
16902
+ return;
16903
+ }
16904
+ };
17403
16905
  }
17404
- };
17405
- var posixNormalize = (path19, allowAboveRoot) => {
17406
- let res = "";
17407
- let lastSegmentLength = 0;
17408
- let lastSlash = -1;
17409
- let dots = 0;
17410
- let code;
17411
- for (let i = 0;i <= path19.length; ++i) {
17412
- if (i < path19.length) {
17413
- code = path19.charCodeAt(i);
17414
- } else if (code === SLASH) {
17415
- break;
17416
- } else {
17417
- code = SLASH;
17418
- }
17419
- if (code === SLASH) {
17420
- if (lastSlash === i - 1 || dots === 1) {
17421
- } else if (lastSlash !== i - 1 && dots === 2) {
17422
- if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== DOT || res.charCodeAt(res.length - 2) !== DOT) {
17423
- if (res.length > 2) {
17424
- const lastSlashIndex = res.lastIndexOf("/");
17425
- if (lastSlashIndex !== res.length - 1) {
17426
- if (lastSlashIndex === -1) {
17427
- res = "";
17428
- lastSegmentLength = 0;
17429
- } else {
17430
- res = res.slice(0, lastSlashIndex);
17431
- lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
17432
- }
17433
- lastSlash = i;
17434
- dots = 0;
17435
- continue;
17436
- }
17437
- } else if (res.length === 2 || res.length === 1) {
17438
- res = "";
17439
- lastSegmentLength = 0;
17440
- lastSlash = i;
17441
- dots = 0;
17442
- continue;
17443
- }
16906
+ let interval = null;
16907
+ let i = 0;
16908
+ let stopped = false;
16909
+ const set = () => {
16910
+ interval = setTimeout(() => {
16911
+ puppeteerInstance.getBrowser().pages().then((pages) => {
16912
+ if (pages.length === 0) {
16913
+ return;
17444
16914
  }
17445
- if (allowAboveRoot) {
17446
- if (res.length > 0) {
17447
- res += "/..";
17448
- } else {
17449
- res = "..";
17450
- }
17451
- lastSegmentLength = 2;
16915
+ const currentPage = pages[i % pages.length];
16916
+ i++;
16917
+ if (!currentPage?.closed && !stopped && currentPage?.url() !== "about:blank") {
16918
+ return currentPage.bringToFront();
17452
16919
  }
17453
- } else {
17454
- if (res.length > 0) {
17455
- res += "/" + path19.slice(lastSlash + 1, i);
17456
- } else {
17457
- res = path19.slice(lastSlash + 1, i);
16920
+ }).catch((err) => Log.error({ indent, logLevel }, err)).finally(() => {
16921
+ if (!stopped) {
16922
+ set();
17458
16923
  }
17459
- lastSegmentLength = i - lastSlash - 1;
16924
+ });
16925
+ }, 200);
16926
+ };
16927
+ set();
16928
+ return {
16929
+ stopCycling: () => {
16930
+ if (!interval) {
16931
+ return;
17460
16932
  }
17461
- lastSlash = i;
17462
- dots = 0;
17463
- } else if (code === DOT && dots !== -1) {
17464
- ++dots;
17465
- } else {
17466
- dots = -1;
16933
+ stopped = true;
16934
+ return clearTimeout(interval);
17467
16935
  }
17468
- }
17469
- return res;
16936
+ };
17470
16937
  };
17471
- var decode = (s) => {
17472
- try {
17473
- return decodeURIComponent(s);
17474
- } catch {
17475
- return s;
17476
- }
16938
+
16939
+ // src/combine-audio.ts
16940
+ import { rmSync as rmSync2, writeFileSync } from "fs";
16941
+ import { join as join3 } from "path";
16942
+ import { VERSION as VERSION3 } from "remotion/version";
16943
+
16944
+ // src/options/separate-audio.tsx
16945
+ var DEFAULT = null;
16946
+ var cliFlag = "separate-audio-to";
16947
+ var separateAudioOption = {
16948
+ cliFlag,
16949
+ description: () => `If set, the audio will not be included in the main output but rendered as a separate file at the location you pass. It is recommended to use an absolute path. If a relative path is passed, it is relative to the Remotion Root.`,
16950
+ docLink: "https://remotion.dev/docs/renderer/render-media",
16951
+ getValue: ({ commandLine }) => {
16952
+ if (commandLine[cliFlag]) {
16953
+ return {
16954
+ source: "cli",
16955
+ value: commandLine[cliFlag]
16956
+ };
16957
+ }
16958
+ return {
16959
+ source: "default",
16960
+ value: DEFAULT
16961
+ };
16962
+ },
16963
+ name: "Separate audio to",
16964
+ setConfig: () => {
16965
+ throw new Error("Not implemented");
16966
+ },
16967
+ ssrName: "separateAudioTo",
16968
+ type: "string"
17477
16969
  };
17478
- var pathNormalize = (p) => {
17479
- assertPath(p);
17480
- let path19 = p;
17481
- if (path19.length === 0) {
17482
- return ".";
16970
+
16971
+ // src/options/audio-codec.tsx
16972
+ var validAudioCodecs = ["pcm-16", "aac", "mp3", "opus"];
16973
+ var supportedAudioCodecs = {
16974
+ h264: ["aac", "pcm-16", "mp3"],
16975
+ "h264-mkv": ["pcm-16", "mp3"],
16976
+ "h264-ts": ["pcm-16", "aac"],
16977
+ aac: ["aac", "pcm-16"],
16978
+ avi: [],
16979
+ gif: [],
16980
+ h265: ["aac", "pcm-16"],
16981
+ mp3: ["mp3", "pcm-16"],
16982
+ prores: ["aac", "pcm-16"],
16983
+ vp8: ["opus", "pcm-16"],
16984
+ vp9: ["opus", "pcm-16"],
16985
+ wav: ["pcm-16"]
16986
+ };
16987
+ var _satisfies = supportedAudioCodecs;
16988
+ if (_satisfies) {
16989
+ }
16990
+ var mapAudioCodecToFfmpegAudioCodecName = (audioCodec) => {
16991
+ if (audioCodec === "aac") {
16992
+ return "libfdk_aac";
17483
16993
  }
17484
- const isAbsolute = path19.charCodeAt(0) === SLASH;
17485
- const trailingSeparator = path19.charCodeAt(path19.length - 1) === SLASH;
17486
- path19 = decode(path19);
17487
- path19 = posixNormalize(path19, !isAbsolute);
17488
- if (path19.length === 0 && !isAbsolute) {
17489
- path19 = ".";
16994
+ if (audioCodec === "mp3") {
16995
+ return "libmp3lame";
17490
16996
  }
17491
- if (path19.length > 0 && trailingSeparator) {
17492
- path19 += "/";
16997
+ if (audioCodec === "opus") {
16998
+ return "libopus";
17493
16999
  }
17494
- if (isAbsolute) {
17495
- return "/" + path19;
17000
+ if (audioCodec === "pcm-16") {
17001
+ return "pcm_s16le";
17002
+ }
17003
+ throw new Error("unknown audio codec: " + audioCodec);
17004
+ };
17005
+ var cliFlag2 = "audio-codec";
17006
+ var ssrName = "audioCodec";
17007
+ var defaultAudioCodecs = {
17008
+ "h264-mkv": {
17009
+ lossless: "pcm-16",
17010
+ compressed: "pcm-16"
17011
+ },
17012
+ "h264-ts": {
17013
+ lossless: "pcm-16",
17014
+ compressed: "aac"
17015
+ },
17016
+ aac: {
17017
+ lossless: "pcm-16",
17018
+ compressed: "aac"
17019
+ },
17020
+ gif: {
17021
+ lossless: null,
17022
+ compressed: null
17023
+ },
17024
+ h264: {
17025
+ lossless: "pcm-16",
17026
+ compressed: "aac"
17027
+ },
17028
+ h265: {
17029
+ lossless: "pcm-16",
17030
+ compressed: "aac"
17031
+ },
17032
+ mp3: {
17033
+ lossless: "pcm-16",
17034
+ compressed: "mp3"
17035
+ },
17036
+ prores: {
17037
+ lossless: "pcm-16",
17038
+ compressed: "pcm-16"
17039
+ },
17040
+ vp8: {
17041
+ lossless: "pcm-16",
17042
+ compressed: "opus"
17043
+ },
17044
+ vp9: {
17045
+ lossless: "pcm-16",
17046
+ compressed: "opus"
17047
+ },
17048
+ wav: {
17049
+ lossless: "pcm-16",
17050
+ compressed: "pcm-16"
17496
17051
  }
17497
- return path19;
17498
17052
  };
17499
-
17500
- // src/get-extension-of-filename.ts
17501
- var getExtensionOfFilename = (filename) => {
17502
- if (filename === null) {
17503
- return null;
17504
- }
17505
- const filenameArr = pathNormalize(filename).split(".");
17506
- const hasExtension = filenameArr.length >= 2;
17507
- const filenameArrLength = filenameArr.length;
17508
- const extension = hasExtension ? filenameArr[filenameArrLength - 1] : null;
17509
- return extension;
17053
+ var extensionMap = {
17054
+ aac: "aac",
17055
+ mp3: "mp3",
17056
+ opus: "opus",
17057
+ "pcm-16": "wav"
17510
17058
  };
17511
-
17512
- // src/get-frame-to-render.ts
17513
- var getRealFrameRange = (durationInFrames, frameRange) => {
17514
- if (frameRange === null) {
17515
- return [0, durationInFrames - 1];
17516
- }
17517
- if (typeof frameRange === "number") {
17518
- if (frameRange < 0 || frameRange >= durationInFrames) {
17519
- throw new Error(`Frame number is out of range, must be between 0 and ${durationInFrames - 1} but got ${frameRange}`);
17520
- }
17521
- return [frameRange, frameRange];
17522
- }
17523
- if (frameRange[1] >= durationInFrames || frameRange[0] < 0) {
17524
- throw new Error(`The "durationInFrames" of the <Composition /> was evaluated to be ${durationInFrames}, but frame range ${frameRange.join("-")} is not inbetween 0-${durationInFrames - 1}`);
17059
+ var getExtensionFromAudioCodec = (audioCodec) => {
17060
+ if (extensionMap[audioCodec]) {
17061
+ return extensionMap[audioCodec];
17525
17062
  }
17526
- return frameRange;
17063
+ throw new Error(`Unsupported audio codec: ${audioCodec}`);
17527
17064
  };
17528
-
17529
- // src/image-format.ts
17530
- var validVideoImageFormats = ["png", "jpeg", "none"];
17531
- var validStillImageFormats = ["png", "jpeg", "pdf", "webp"];
17532
- var DEFAULT_VIDEO_IMAGE_FORMAT = "jpeg";
17533
- var DEFAULT_STILL_IMAGE_FORMAT = "png";
17534
- var validateSelectedPixelFormatAndImageFormatCombination = (pixelFormat, videoImageFormat) => {
17535
- if (videoImageFormat === "none") {
17536
- return "none";
17537
- }
17538
- if (typeof pixelFormat === "undefined") {
17539
- return "valid";
17065
+ var resolveAudioCodec = ({
17066
+ codec,
17067
+ setting,
17068
+ preferLossless,
17069
+ separateAudioTo
17070
+ }) => {
17071
+ let derivedFromSeparateAudioToExtension = null;
17072
+ if (separateAudioTo) {
17073
+ const extension = separateAudioTo.split(".").pop();
17074
+ for (const [key, value] of Object.entries(extensionMap)) {
17075
+ if (value === extension) {
17076
+ derivedFromSeparateAudioToExtension = key;
17077
+ if (!supportedAudioCodecs[codec].includes(derivedFromSeparateAudioToExtension) && derivedFromSeparateAudioToExtension) {
17078
+ throw new Error(`The codec is ${codec} but the audio codec derived from --${separateAudioOption.cliFlag} is ${derivedFromSeparateAudioToExtension}. The only supported codecs are: ${supportedAudioCodecs[codec].join(", ")}`);
17079
+ }
17080
+ }
17081
+ }
17540
17082
  }
17541
- if (!validVideoImageFormats.includes(videoImageFormat)) {
17542
- throw new TypeError(`Value ${videoImageFormat} is not valid as an image format.`);
17083
+ if (preferLossless) {
17084
+ const selected = getDefaultAudioCodec({ codec, preferLossless });
17085
+ if (derivedFromSeparateAudioToExtension && selected !== derivedFromSeparateAudioToExtension) {
17086
+ throw new Error(`The audio codec derived from --${separateAudioOption.cliFlag} is ${derivedFromSeparateAudioToExtension}, but does not match the audio codec derived from the "Prefer lossless" option (${selected}). Remove any conflicting options.`);
17087
+ }
17088
+ return selected;
17543
17089
  }
17544
- if (pixelFormat !== "yuva420p" && pixelFormat !== "yuva444p10le") {
17545
- return "valid";
17090
+ if (setting === null) {
17091
+ if (derivedFromSeparateAudioToExtension) {
17092
+ return derivedFromSeparateAudioToExtension;
17093
+ }
17094
+ return getDefaultAudioCodec({ codec, preferLossless });
17546
17095
  }
17547
- if (videoImageFormat !== "png") {
17548
- throw new TypeError(`Pixel format was set to '${pixelFormat}' but the image format is not PNG. To render transparent videos, you need to set PNG as the image format.`);
17096
+ if (derivedFromSeparateAudioToExtension !== setting && derivedFromSeparateAudioToExtension) {
17097
+ throw new Error(`The audio codec derived from --${separateAudioOption.cliFlag} is ${derivedFromSeparateAudioToExtension}, but does not match the audio codec derived from your ${audioCodecOption.name} setting (${setting}). Remove any conflicting options.`);
17549
17098
  }
17550
- return "valid";
17099
+ return setting;
17551
17100
  };
17552
- var validateStillImageFormat = (imageFormat) => {
17553
- if (!validStillImageFormats.includes(imageFormat)) {
17554
- throw new TypeError(String(`Image format should be one of: ${validStillImageFormats.map((v) => `"${v}"`).join(", ")}`));
17555
- }
17101
+ var getDefaultAudioCodec = ({
17102
+ codec,
17103
+ preferLossless
17104
+ }) => {
17105
+ return defaultAudioCodecs[codec][preferLossless ? "lossless" : "compressed"];
17106
+ };
17107
+ var _audioCodec = null;
17108
+ var audioCodecOption = {
17109
+ cliFlag: cliFlag2,
17110
+ setConfig: (audioCodec) => {
17111
+ if (audioCodec === null) {
17112
+ _audioCodec = null;
17113
+ return;
17114
+ }
17115
+ if (!validAudioCodecs.includes(audioCodec)) {
17116
+ throw new Error(`Audio codec must be one of the following: ${validAudioCodecs.join(", ")}, but got ${audioCodec}`);
17117
+ }
17118
+ _audioCodec = audioCodec;
17119
+ },
17120
+ getValue: ({ commandLine }) => {
17121
+ if (commandLine[cliFlag2]) {
17122
+ const codec = commandLine[cliFlag2];
17123
+ if (!validAudioCodecs.includes(commandLine[cliFlag2])) {
17124
+ throw new Error(`Audio codec must be one of the following: ${validAudioCodecs.join(", ")}, but got ${codec}`);
17125
+ }
17126
+ return {
17127
+ source: "cli",
17128
+ value: commandLine[cliFlag2]
17129
+ };
17130
+ }
17131
+ if (_audioCodec !== null) {
17132
+ return {
17133
+ source: "config",
17134
+ value: _audioCodec
17135
+ };
17136
+ }
17137
+ return {
17138
+ source: "default",
17139
+ value: null
17140
+ };
17141
+ },
17142
+ description: () => `Set the format of the audio that is embedded in the video. Not all codec and audio codec combinations are supported and certain combinations require a certain file extension and container format. See the table in the docs to see possible combinations.`,
17143
+ docLink: "https://www.remotion.dev/docs/encoding/#audio-codec",
17144
+ name: "Audio Codec",
17145
+ ssrName,
17146
+ type: "aac"
17556
17147
  };
17557
17148
 
17558
- // src/jpeg-quality.ts
17559
- var DEFAULT_JPEG_QUALITY = 80;
17560
- var validateJpegQuality = (q) => {
17561
- if (typeof q !== "undefined" && typeof q !== "number") {
17562
- throw new Error(`JPEG Quality option must be a number or undefined. Got ${typeof q} (${JSON.stringify(q)})`);
17563
- }
17564
- if (typeof q === "undefined") {
17565
- return;
17566
- }
17567
- if (!Number.isFinite(q)) {
17568
- throw new RangeError(`JPEG Quality must be a finite number, but is ${q}`);
17569
- }
17570
- if (Number.isNaN(q)) {
17571
- throw new RangeError(`JPEG Quality is NaN, but must be a real number`);
17149
+ // src/parse-ffmpeg-progress.ts
17150
+ var parseFfmpegProgress = (input, fps) => {
17151
+ const match = input.match(/frame=(\s+)?([0-9]+)\s/);
17152
+ if (match) {
17153
+ return Number(match[2]);
17572
17154
  }
17573
- if (q > 100 || q < 0) {
17574
- throw new RangeError("JPEG Quality option must be between 0 and 100.");
17155
+ const match2 = input.match(/time=(\d+):(\d+):(\d+).(\d+)\s/);
17156
+ if (match2) {
17157
+ const [, hours, minutes, seconds, hundreds] = match2;
17158
+ return Number(hundreds) / 100 * fps + Number(seconds) * fps + Number(minutes) * fps * 60 + Number(hours) * fps * 60 * 60;
17575
17159
  }
17576
17160
  };
17577
17161
 
17578
- // src/perf.ts
17579
- var exports_perf = {};
17580
- __export(exports_perf, {
17581
- stopPerfMeasure: () => stopPerfMeasure,
17582
- startPerfMeasure: () => startPerfMeasure,
17583
- getPerf: () => getPerf
17584
- });
17585
- var perf = {
17586
- "activate-target": [],
17587
- capture: [],
17588
- save: [],
17589
- "extract-frame": [],
17590
- piping: []
17591
- };
17592
- var map2 = {};
17593
- var startPerfMeasure = (marker) => {
17594
- const id = Math.random();
17595
- map2[id] = {
17596
- id,
17597
- marker,
17598
- start: Date.now()
17599
- };
17600
- return id;
17601
- };
17602
- var stopPerfMeasure = (id) => {
17603
- const now = Date.now();
17604
- const diff = now - map2[id].start;
17605
- perf[map2[id].marker].push(diff);
17606
- delete map2[id];
17607
- };
17608
- var getPerf = () => {
17609
- return [
17610
- "Render performance:",
17611
- ...Object.keys(perf).filter((p) => perf[p].length).map((p) => {
17612
- return ` ${p} => ${Math.round(perf[p].reduce((a, b) => a + b, 0) / perf[p].length)}ms (n = ${perf[p].length})`;
17613
- })
17614
- ];
17615
- };
17162
+ // src/sample-rate.ts
17163
+ var DEFAULT_SAMPLE_RATE = 48000;
17616
17164
 
17617
- // src/pixel-format.ts
17618
- var validPixelFormats = [
17619
- "yuv420p",
17620
- "yuva420p",
17621
- "yuv422p",
17622
- "yuv444p",
17623
- "yuv420p10le",
17624
- "yuv422p10le",
17625
- "yuv444p10le",
17626
- "yuva444p10le"
17627
- ];
17628
- var DEFAULT_PIXEL_FORMAT = "yuv420p";
17629
- var validPixelFormatsForCodec = (codec) => {
17630
- if (codec === "vp8" || codec === "vp9") {
17631
- return validPixelFormats;
17165
+ // src/combine-audio.ts
17166
+ var durationOf1Frame = 1024 / DEFAULT_SAMPLE_RATE * 1e6;
17167
+ var getClosestAlignedTime = (targetTime) => {
17168
+ const decimalFramesToTargetTime = targetTime * 1e6 / durationOf1Frame;
17169
+ const nearestFrameIndexForTargetTime = Math.round(decimalFramesToTargetTime);
17170
+ return nearestFrameIndexForTargetTime * durationOf1Frame / 1e6;
17171
+ };
17172
+ var encodeAudio = async ({
17173
+ files,
17174
+ resolvedAudioCodec,
17175
+ audioBitrate,
17176
+ filelistDir,
17177
+ output,
17178
+ indent,
17179
+ logLevel,
17180
+ addRemotionMetadata,
17181
+ fps,
17182
+ binariesDirectory,
17183
+ cancelSignal,
17184
+ onProgress
17185
+ }) => {
17186
+ const fileList = files.map((p) => `file '${p}'`).join(`
17187
+ `);
17188
+ const fileListTxt = join3(filelistDir, "audio-files.txt");
17189
+ writeFileSync(fileListTxt, fileList);
17190
+ const startCombining = Date.now();
17191
+ const command = [
17192
+ "-hide_banner",
17193
+ "-f",
17194
+ "concat",
17195
+ "-safe",
17196
+ "0",
17197
+ "-i",
17198
+ fileListTxt,
17199
+ "-c:a",
17200
+ mapAudioCodecToFfmpegAudioCodecName(resolvedAudioCodec),
17201
+ resolvedAudioCodec === "aac" ? "-cutoff" : null,
17202
+ resolvedAudioCodec === "aac" ? "18000" : null,
17203
+ "-b:a",
17204
+ audioBitrate ? audioBitrate : "320k",
17205
+ "-vn",
17206
+ addRemotionMetadata ? `-metadata` : null,
17207
+ addRemotionMetadata ? `comment=Made with Remotion ${VERSION3}` : null,
17208
+ "-y",
17209
+ output
17210
+ ];
17211
+ Log.verbose({ indent, logLevel }, `Combining audio with re-encoding, command: ${command.join(" ")}`);
17212
+ try {
17213
+ const task = callFf({
17214
+ args: command,
17215
+ bin: "ffmpeg",
17216
+ indent,
17217
+ logLevel,
17218
+ binariesDirectory,
17219
+ cancelSignal
17220
+ });
17221
+ task.stderr?.on("data", (data) => {
17222
+ const utf8 = data.toString("utf8");
17223
+ const parsed = parseFfmpegProgress(utf8, fps);
17224
+ if (parsed === undefined) {
17225
+ Log.verbose({ indent, logLevel }, utf8);
17226
+ } else {
17227
+ onProgress(parsed);
17228
+ Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
17229
+ }
17230
+ });
17231
+ await task;
17232
+ Log.verbose({ indent, logLevel }, `Encoded audio in ${Date.now() - startCombining}ms`);
17233
+ return output;
17234
+ } catch (e) {
17235
+ rmSync2(fileListTxt, { recursive: true });
17236
+ throw e;
17632
17237
  }
17633
- return validPixelFormats.filter((format) => format !== "yuva420p");
17634
17238
  };
17635
- var validateSelectedPixelFormatAndCodecCombination = (pixelFormat, codec) => {
17636
- if (typeof pixelFormat === "undefined") {
17637
- return pixelFormat;
17638
- }
17639
- if (!validPixelFormats.includes(pixelFormat)) {
17640
- throw new TypeError(`Value ${pixelFormat} is not valid as a pixel format.`);
17641
- }
17642
- if (pixelFormat !== "yuva420p") {
17643
- return;
17644
- }
17645
- const validFormats = validPixelFormatsForCodec(codec);
17646
- if (!validFormats.includes(pixelFormat)) {
17647
- throw new TypeError(`Pixel format was set to 'yuva420p' but codec ${codec} does not support it. Valid pixel formats for codec ${codec} are: ${validFormats.join(", ")}.`);
17239
+ var combineAudioSeamlessly = async ({
17240
+ files,
17241
+ filelistDir,
17242
+ indent,
17243
+ logLevel,
17244
+ output,
17245
+ chunkDurationInSeconds,
17246
+ addRemotionMetadata,
17247
+ fps,
17248
+ binariesDirectory,
17249
+ cancelSignal,
17250
+ onProgress
17251
+ }) => {
17252
+ const startConcatenating = Date.now();
17253
+ const fileList = files.map((p, i) => {
17254
+ const isLast = i === files.length - 1;
17255
+ const targetStart = i * chunkDurationInSeconds;
17256
+ const endStart = (i + 1) * chunkDurationInSeconds;
17257
+ const startTime = getClosestAlignedTime(targetStart) * 1e6;
17258
+ const endTime = getClosestAlignedTime(endStart) * 1e6;
17259
+ const realDuration = endTime - startTime;
17260
+ let inpoint = 0;
17261
+ if (i > 0) {
17262
+ inpoint = durationOf1Frame * 4;
17263
+ }
17264
+ const outpoint = (i === 0 ? durationOf1Frame * 2 : inpoint) + realDuration - (isLast ? 0 : durationOf1Frame);
17265
+ return [`file '${p}'`, `inpoint ${inpoint}us`, `outpoint ${outpoint}us`].filter(truthy).join(`
17266
+ `);
17267
+ }).join(`
17268
+ `);
17269
+ const fileListTxt = join3(filelistDir, "audio-files.txt");
17270
+ writeFileSync(fileListTxt, fileList);
17271
+ const command = [
17272
+ "-hide_banner",
17273
+ "-f",
17274
+ "concat",
17275
+ "-safe",
17276
+ "0",
17277
+ "-i",
17278
+ fileListTxt,
17279
+ "-c:a",
17280
+ "copy",
17281
+ "-vn",
17282
+ addRemotionMetadata ? `-metadata` : null,
17283
+ addRemotionMetadata ? `comment=Made with Remotion ${VERSION3}` : null,
17284
+ "-y",
17285
+ output
17286
+ ];
17287
+ Log.verbose({ indent, logLevel }, `Combining AAC audio seamlessly, command: ${command.join(" ")}`);
17288
+ try {
17289
+ const task = callFf({
17290
+ args: command,
17291
+ bin: "ffmpeg",
17292
+ indent,
17293
+ logLevel,
17294
+ binariesDirectory,
17295
+ cancelSignal
17296
+ });
17297
+ task.stderr?.on("data", (data) => {
17298
+ const utf8 = data.toString("utf8");
17299
+ const parsed = parseFfmpegProgress(utf8, fps);
17300
+ if (parsed !== undefined) {
17301
+ onProgress(parsed);
17302
+ Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
17303
+ }
17304
+ });
17305
+ await task;
17306
+ Log.verbose({ indent, logLevel }, `Combined audio seamlessly in ${Date.now() - startConcatenating}ms`);
17307
+ return output;
17308
+ } catch (e) {
17309
+ rmSync2(fileListTxt, { recursive: true });
17310
+ Log.error({ indent, logLevel }, e);
17311
+ throw e;
17648
17312
  }
17649
17313
  };
17650
-
17651
- // src/render-frames.ts
17652
- import fs14 from "node:fs";
17653
- import path20 from "node:path";
17654
- import { NoReactInternals as NoReactInternals12 } from "remotion/no-react";
17655
-
17656
- // src/cycle-browser-tabs.ts
17657
- var cycleBrowserTabs = ({
17658
- puppeteerInstance,
17659
- concurrency,
17314
+ var createCombinedAudio = ({
17315
+ seamless,
17316
+ filelistDir,
17317
+ files,
17318
+ indent,
17660
17319
  logLevel,
17661
- indent
17320
+ audioBitrate,
17321
+ resolvedAudioCodec,
17322
+ output,
17323
+ chunkDurationInSeconds,
17324
+ addRemotionMetadata,
17325
+ binariesDirectory,
17326
+ fps,
17327
+ cancelSignal,
17328
+ onProgress
17662
17329
  }) => {
17663
- if (concurrency <= 1) {
17664
- return {
17665
- stopCycling: () => {
17666
- return;
17667
- }
17668
- };
17330
+ if (seamless) {
17331
+ return combineAudioSeamlessly({
17332
+ filelistDir,
17333
+ files,
17334
+ indent,
17335
+ logLevel,
17336
+ output,
17337
+ chunkDurationInSeconds,
17338
+ addRemotionMetadata,
17339
+ binariesDirectory,
17340
+ fps,
17341
+ cancelSignal,
17342
+ onProgress
17343
+ });
17669
17344
  }
17670
- let interval = null;
17671
- let i = 0;
17672
- let stopped = false;
17673
- const set = () => {
17674
- interval = setTimeout(() => {
17675
- puppeteerInstance.getBrowser().pages().then((pages) => {
17676
- if (pages.length === 0) {
17677
- return;
17678
- }
17679
- const currentPage = pages[i % pages.length];
17680
- i++;
17681
- if (!currentPage?.closed && !stopped && currentPage?.url() !== "about:blank") {
17682
- return currentPage.bringToFront();
17683
- }
17684
- }).catch((err) => Log.error({ indent, logLevel }, err)).finally(() => {
17685
- if (!stopped) {
17686
- set();
17687
- }
17688
- });
17689
- }, 200);
17690
- };
17691
- set();
17692
- return {
17693
- stopCycling: () => {
17694
- if (!interval) {
17695
- return;
17696
- }
17697
- stopped = true;
17698
- return clearTimeout(interval);
17699
- }
17700
- };
17345
+ return encodeAudio({
17346
+ filelistDir,
17347
+ files,
17348
+ resolvedAudioCodec,
17349
+ audioBitrate,
17350
+ output,
17351
+ indent,
17352
+ logLevel,
17353
+ addRemotionMetadata,
17354
+ binariesDirectory,
17355
+ fps,
17356
+ cancelSignal,
17357
+ onProgress
17358
+ });
17701
17359
  };
17702
17360
 
17703
17361
  // src/get-extra-frames-to-capture.ts
@@ -19408,15 +19066,15 @@ var ensureFramesInOrder = (framesToRender) => {
19408
19066
  };
19409
19067
 
19410
19068
  // src/options/color-space.tsx
19411
- var jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
19412
19069
  import { NoReactInternals as NoReactInternals13 } from "remotion/no-react";
19070
+ import { jsx as jsx4, jsxs as jsxs4, Fragment as Fragment4 } from "react/jsx-runtime";
19413
19071
  var validV4ColorSpaces = ["default", "bt709", "bt2020-ncl"];
19414
19072
  var validV5ColorSpaces = ["bt601", "bt709", "bt2020-ncl"];
19415
19073
  var validColorSpaces = NoReactInternals13.ENABLE_V5_BREAKING_CHANGES ? validV5ColorSpaces : validV4ColorSpaces;
19416
19074
  var DEFAULT_COLOR_SPACE = NoReactInternals13.ENABLE_V5_BREAKING_CHANGES ? "bt709" : "default";
19417
19075
 
19418
19076
  // src/options/x264-preset.tsx
19419
- var jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
19077
+ import { jsx as jsx5, jsxs as jsxs5, Fragment as Fragment5 } from "react/jsx-runtime";
19420
19078
  var x264PresetOptions = [
19421
19079
  "ultrafast",
19422
19080
  "superfast",
@@ -19665,14 +19323,20 @@ var validateEvenDimensionsWithCodec = ({
19665
19323
  indent,
19666
19324
  logLevel
19667
19325
  }) => {
19326
+ let actualWidth = width * scale;
19327
+ let actualHeight = height * scale;
19668
19328
  if (wantsImageSequence) {
19669
- return;
19329
+ return {
19330
+ actualWidth,
19331
+ actualHeight
19332
+ };
19670
19333
  }
19671
19334
  if (codec !== "h264-mkv" && codec !== "h264" && codec !== "h265" && codec !== "h264-ts") {
19672
- return;
19335
+ return {
19336
+ actualWidth,
19337
+ actualHeight
19338
+ };
19673
19339
  }
19674
- let actualWidth = width * scale;
19675
- let actualHeight = height * scale;
19676
19340
  if (actualWidth % 1 !== 0 && (actualWidth % 1 < 0.005 || actualWidth % 1 > 0.005)) {
19677
19341
  Log.verbose({ indent, logLevel }, `Rounding width to an even number from ${actualWidth} to ${Math.round(actualWidth)}`);
19678
19342
  actualWidth = Math.round(actualWidth);
@@ -19698,6 +19362,10 @@ var validateEvenDimensionsWithCodec = ({
19698
19362
  ].join(" ");
19699
19363
  throw new Error(message);
19700
19364
  }
19365
+ return {
19366
+ actualWidth,
19367
+ actualHeight
19368
+ };
19701
19369
  };
19702
19370
 
19703
19371
  // src/prespawn-ffmpeg.ts
@@ -19844,9 +19512,20 @@ var validateSelectedCodecAndProResCombination = ({
19844
19512
  };
19845
19513
 
19846
19514
  // src/stitch-frames-to-video.ts
19847
- import { cpSync as cpSync2, promises as promises3, rmSync as rmSync5 } from "node:fs";
19515
+ import { cpSync as cpSync2, promises as promises3, rmSync as rmSync3 } from "node:fs";
19848
19516
  import path24 from "node:path";
19849
19517
 
19518
+ // src/convert-number-of-gif-loops-to-ffmpeg.ts
19519
+ var convertNumberOfGifLoopsToFfmpegSyntax = (loops) => {
19520
+ if (loops === null) {
19521
+ return "0";
19522
+ }
19523
+ if (loops === 0) {
19524
+ return "-1";
19525
+ }
19526
+ return String(loops);
19527
+ };
19528
+
19850
19529
  // src/create-audio.ts
19851
19530
  import path23 from "path";
19852
19531
 
@@ -19941,7 +19620,6 @@ var calculateAssetPositions = (frames) => {
19941
19620
  trimLeft: asset.mediaFrame,
19942
19621
  volume: [],
19943
19622
  playbackRate: asset.playbackRate,
19944
- allowAmplificationDuringRender: asset.allowAmplificationDuringRender,
19945
19623
  toneFrequency: asset.toneFrequency,
19946
19624
  audioStartFrame: asset.audioStartFrame
19947
19625
  });
@@ -20343,28 +20021,25 @@ var ffmpegBuildVolumeExpression = ({
20343
20021
  var ffmpegVolumeExpression = ({
20344
20022
  volume,
20345
20023
  fps,
20346
- trimLeft,
20347
- allowAmplificationDuringRender
20024
+ trimLeft
20348
20025
  }) => {
20349
- const maxVolume = allowAmplificationDuringRender ? Infinity : 1;
20350
20026
  if (typeof volume === "number") {
20351
20027
  return {
20352
20028
  eval: "once",
20353
- value: String(Math.min(maxVolume, volume))
20029
+ value: String(volume)
20354
20030
  };
20355
20031
  }
20356
20032
  if ([...new Set(volume)].length === 1) {
20357
20033
  return ffmpegVolumeExpression({
20358
20034
  volume: volume[0],
20359
20035
  fps,
20360
- trimLeft,
20361
- allowAmplificationDuringRender
20036
+ trimLeft
20362
20037
  });
20363
20038
  }
20364
20039
  const paddedVolume = [...volume, volume[volume.length - 1]];
20365
20040
  const volumeMap = {};
20366
20041
  paddedVolume.forEach((baseVolume, frame) => {
20367
- const actualVolume = roundVolumeToAvoidStackOverflow(Math.min(maxVolume, baseVolume));
20042
+ const actualVolume = roundVolumeToAvoidStackOverflow(baseVolume);
20368
20043
  if (!volumeMap[actualVolume]) {
20369
20044
  volumeMap[actualVolume] = [];
20370
20045
  }
@@ -20506,8 +20181,7 @@ var stringifyFfmpegFilter = ({
20506
20181
  const volumeFilter = ffmpegVolumeExpression({
20507
20182
  volume,
20508
20183
  fps,
20509
- trimLeft: actualTrimLeft,
20510
- allowAmplificationDuringRender: asset.allowAmplificationDuringRender
20184
+ trimLeft: actualTrimLeft
20511
20185
  });
20512
20186
  const padAtEnd = chunkLengthInSeconds - audibleDuration - startInVideoSeconds;
20513
20187
  const padStart = startInVideoSeconds + presentationTimeOffsetInSeconds;
@@ -20710,6 +20384,25 @@ var createAudio = async ({
20710
20384
  return outName;
20711
20385
  };
20712
20386
 
20387
+ // src/make-metadata-args.ts
20388
+ import { VERSION as VERSION4 } from "remotion/version";
20389
+ var makeMetadataArgs = (metadata) => {
20390
+ const defaultComment = `Made with Remotion ${VERSION4}`;
20391
+ const newMetadata = {
20392
+ comment: defaultComment
20393
+ };
20394
+ Object.keys(metadata).forEach((key) => {
20395
+ const lowercaseKey = key.toLowerCase();
20396
+ if (lowercaseKey === "comment") {
20397
+ newMetadata[lowercaseKey] = `${defaultComment}; ${metadata[key]}`;
20398
+ } else {
20399
+ newMetadata[lowercaseKey] = metadata[key];
20400
+ }
20401
+ });
20402
+ const metadataArgs = Object.entries(newMetadata).map(([key, value]) => ["-metadata", `${key}=${value}`]);
20403
+ return metadataArgs.flat(1);
20404
+ };
20405
+
20713
20406
  // src/render-has-audio.ts
20714
20407
  var getShouldRenderAudio = ({
20715
20408
  codec,
@@ -20990,7 +20683,7 @@ var innerStitchFramesToVideo = async ({
20990
20683
  }
20991
20684
  const finalDestination = path24.resolve(remotionRoot, separateAudioTo);
20992
20685
  cpSync2(audio, finalDestination);
20993
- rmSync5(audio);
20686
+ rmSync3(audio);
20994
20687
  }
20995
20688
  return new Promise((resolve2, reject) => {
20996
20689
  task.once("close", (code, signal) => {
@@ -21376,7 +21069,7 @@ var internalRenderMediaRaw = ({
21376
21069
  if (onCtrlCExit && workingDir) {
21377
21070
  onCtrlCExit(`Delete ${workingDir}`, () => deleteDirectory(workingDir));
21378
21071
  }
21379
- validateEvenDimensionsWithCodec({
21072
+ const { actualWidth, actualHeight } = validateEvenDimensionsWithCodec({
21380
21073
  codec,
21381
21074
  height: composition.height,
21382
21075
  scale,
@@ -21412,8 +21105,8 @@ var internalRenderMediaRaw = ({
21412
21105
  const createPrestitcherIfNecessary = () => {
21413
21106
  if (preEncodedFileLocation) {
21414
21107
  preStitcher = prespawnFfmpeg({
21415
- width: composition.width * scale,
21416
- height: composition.height * scale,
21108
+ width: actualWidth,
21109
+ height: actualHeight,
21417
21110
  fps,
21418
21111
  outputLocation: preEncodedFileLocation,
21419
21112
  pixelFormat,
@@ -21572,8 +21265,8 @@ var internalRenderMediaRaw = ({
21572
21265
  }
21573
21266
  const stitchStart = Date.now();
21574
21267
  return internalStitchFramesToVideo({
21575
- width: composition.width * scale,
21576
- height: composition.height * scale,
21268
+ width: actualWidth,
21269
+ height: actualHeight,
21577
21270
  fps,
21578
21271
  outputLocation: absoluteOutputLocation,
21579
21272
  preEncodedFileLocation,
@@ -21729,7 +21422,8 @@ var renderMedia = ({
21729
21422
  metadata,
21730
21423
  hardwareAcceleration,
21731
21424
  chromeMode,
21732
- offthreadVideoThreads
21425
+ offthreadVideoThreads,
21426
+ compositionStart
21733
21427
  }) => {
21734
21428
  const indent = false;
21735
21429
  const logLevel = verbose || dumpBrowserLogs ? "verbose" : passedLogLevel ?? "info";
@@ -21810,7 +21504,7 @@ var renderMedia = ({
21810
21504
  }),
21811
21505
  onArtifact: onArtifact ?? null,
21812
21506
  metadata: metadata ?? null,
21813
- compositionStart: 0,
21507
+ compositionStart: compositionStart ?? 0,
21814
21508
  hardwareAcceleration: hardwareAcceleration ?? "disable",
21815
21509
  chromeMode: chromeMode ?? "headless-shell"
21816
21510
  });
@@ -22449,6 +22143,312 @@ var getMaxConcurrency = () => {
22449
22143
  return getCpuCount();
22450
22144
  };
22451
22145
  var getMinConcurrency = () => 1;
22146
+
22147
+ // src/combine-chunks.ts
22148
+ import { rmSync as rmSync5 } from "node:fs";
22149
+ import { join as join5 } from "node:path";
22150
+
22151
+ // src/can-concat-seamlessly.ts
22152
+ var canConcatAudioSeamlessly = (audioCodec, chunkDurationInFrames) => {
22153
+ if (chunkDurationInFrames <= 4) {
22154
+ return false;
22155
+ }
22156
+ return audioCodec === "aac";
22157
+ };
22158
+ var canConcatVideoSeamlessly = (codec) => {
22159
+ return codec === "h264";
22160
+ };
22161
+
22162
+ // src/combine-video-streams.ts
22163
+ import { rmSync as rmSync4, writeFileSync as writeFileSync2 } from "fs";
22164
+ import { join as join4 } from "path";
22165
+ import { VERSION as VERSION5 } from "remotion/version";
22166
+ var combineVideoStreams = async ({
22167
+ fps,
22168
+ codec,
22169
+ filelistDir,
22170
+ numberOfGifLoops,
22171
+ output,
22172
+ indent,
22173
+ logLevel,
22174
+ onProgress,
22175
+ files,
22176
+ addRemotionMetadata,
22177
+ binariesDirectory,
22178
+ cancelSignal
22179
+ }) => {
22180
+ const fileList = files.map((p) => `file '${p}'`).join(`
22181
+ `);
22182
+ const fileListTxt = join4(filelistDir, "video-files.txt");
22183
+ writeFileSync2(fileListTxt, fileList);
22184
+ const encoder = codec === "gif" ? "gif" : "copy";
22185
+ const command = [
22186
+ "-hide_banner",
22187
+ "-r",
22188
+ String(fps),
22189
+ "-f",
22190
+ "concat",
22191
+ "-safe",
22192
+ "0",
22193
+ "-i",
22194
+ fileListTxt,
22195
+ numberOfGifLoops === null ? null : "-loop",
22196
+ numberOfGifLoops === null ? null : convertNumberOfGifLoopsToFfmpegSyntax(numberOfGifLoops),
22197
+ codec === "gif" ? "-filter_complex" : null,
22198
+ codec === "gif" ? "split[v],palettegen,[v]paletteuse" : null,
22199
+ "-an",
22200
+ "-c:v",
22201
+ encoder,
22202
+ codec === "h265" ? "-tag:v" : null,
22203
+ codec === "h265" ? "hvc1" : null,
22204
+ addRemotionMetadata ? `-metadata` : null,
22205
+ addRemotionMetadata ? `comment=Made with Remotion ${VERSION5}` : null,
22206
+ "-y",
22207
+ output
22208
+ ].filter(truthy);
22209
+ const doesReencode = encoder !== "copy";
22210
+ const startTime = Date.now();
22211
+ Log.verbose({ indent, logLevel }, `Combining video ${doesReencode ? "with reencoding" : "without reencoding"}, command: ${command.join(" ")}`);
22212
+ try {
22213
+ const task = callFf({
22214
+ args: command,
22215
+ bin: "ffmpeg",
22216
+ indent,
22217
+ logLevel,
22218
+ binariesDirectory,
22219
+ cancelSignal
22220
+ });
22221
+ task.stderr?.on("data", (data) => {
22222
+ const parsed = parseFfmpegProgress(data.toString("utf8"), fps);
22223
+ if (parsed === undefined) {
22224
+ Log.verbose({ indent, logLevel }, data.toString("utf8"));
22225
+ } else {
22226
+ Log.verbose({ indent, logLevel }, `Encoded ${parsed} video frames`);
22227
+ onProgress(parsed);
22228
+ }
22229
+ });
22230
+ await task;
22231
+ Log.verbose({ indent, logLevel }, `Finished combining video in ${Date.now() - startTime}ms`);
22232
+ return output;
22233
+ } catch (e) {
22234
+ rmSync4(fileListTxt, { recursive: true });
22235
+ throw e;
22236
+ }
22237
+ };
22238
+
22239
+ // src/combine-video-streams-seamlessly.ts
22240
+ var combineVideoStreamsSeamlessly = ({ files }) => {
22241
+ const fileList = `concat:${files.join("|")}`;
22242
+ return fileList;
22243
+ };
22244
+
22245
+ // src/mux-video-and-audio.ts
22246
+ var muxVideoAndAudio = async ({
22247
+ videoOutput,
22248
+ audioOutput,
22249
+ output,
22250
+ indent,
22251
+ logLevel,
22252
+ onProgress,
22253
+ binariesDirectory,
22254
+ fps,
22255
+ cancelSignal,
22256
+ addFaststart,
22257
+ metadata
22258
+ }) => {
22259
+ const startTime = Date.now();
22260
+ Log.verbose({ indent, logLevel }, "Muxing video and audio together");
22261
+ const command = [
22262
+ "-hide_banner",
22263
+ videoOutput ? "-i" : null,
22264
+ videoOutput,
22265
+ audioOutput ? "-i" : null,
22266
+ audioOutput,
22267
+ videoOutput ? "-c:v" : null,
22268
+ videoOutput ? "copy" : null,
22269
+ audioOutput ? "-c:a" : null,
22270
+ audioOutput ? "copy" : null,
22271
+ addFaststart ? "-movflags" : null,
22272
+ addFaststart ? "faststart" : null,
22273
+ ...makeMetadataArgs(metadata ?? {}),
22274
+ "-y",
22275
+ output
22276
+ ].filter(truthy);
22277
+ Log.verbose({ indent, logLevel }, "Combining command: ", command);
22278
+ const task = callFf({
22279
+ bin: "ffmpeg",
22280
+ args: command,
22281
+ indent,
22282
+ logLevel,
22283
+ binariesDirectory,
22284
+ cancelSignal
22285
+ });
22286
+ task.stderr?.on("data", (data) => {
22287
+ const utf8 = data.toString("utf8");
22288
+ const parsed = parseFfmpegProgress(utf8, fps);
22289
+ if (parsed === undefined) {
22290
+ if (!utf8.includes("Estimating duration from bitrate, this may be inaccurate")) {
22291
+ Log.verbose({ indent, logLevel }, utf8);
22292
+ }
22293
+ } else {
22294
+ Log.verbose({ indent, logLevel }, `Combined ${parsed} frames`);
22295
+ onProgress(parsed);
22296
+ }
22297
+ });
22298
+ await task;
22299
+ Log.verbose({ indent, logLevel }, `Muxing done in ${Date.now() - startTime}ms`);
22300
+ };
22301
+
22302
+ // src/combine-chunks.ts
22303
+ var codecSupportsFastStart = {
22304
+ "h264-mkv": false,
22305
+ "h264-ts": false,
22306
+ h264: true,
22307
+ h265: true,
22308
+ aac: false,
22309
+ gif: false,
22310
+ mp3: false,
22311
+ prores: false,
22312
+ vp8: false,
22313
+ vp9: false,
22314
+ wav: false
22315
+ };
22316
+ var REMOTION_FILELIST_TOKEN = "remotion-filelist";
22317
+ var internalCombineChunks = async ({
22318
+ outputLocation: output,
22319
+ onProgress,
22320
+ codec,
22321
+ fps,
22322
+ numberOfGifLoops,
22323
+ audioBitrate,
22324
+ indent,
22325
+ logLevel,
22326
+ binariesDirectory,
22327
+ cancelSignal,
22328
+ metadata,
22329
+ audioFiles,
22330
+ videoFiles,
22331
+ framesPerChunk,
22332
+ audioCodec,
22333
+ preferLossless,
22334
+ everyNthFrame,
22335
+ frameRange,
22336
+ compositionDurationInFrames
22337
+ }) => {
22338
+ const filelistDir = tmpDir(REMOTION_FILELIST_TOKEN);
22339
+ const shouldCreateVideo = !isAudioCodec(codec);
22340
+ const resolvedAudioCodec = resolveAudioCodec({
22341
+ setting: audioCodec,
22342
+ codec,
22343
+ preferLossless,
22344
+ separateAudioTo: null
22345
+ });
22346
+ const shouldCreateAudio = resolvedAudioCodec !== null && audioFiles.length > 0;
22347
+ const seamlessVideo = canConcatVideoSeamlessly(codec);
22348
+ const seamlessAudio = canConcatAudioSeamlessly(resolvedAudioCodec, framesPerChunk);
22349
+ const realFrameRange = getRealFrameRange(compositionDurationInFrames, frameRange);
22350
+ const numberOfFrames = getFramesToRender(realFrameRange, everyNthFrame).length;
22351
+ const videoOutput = shouldCreateVideo ? join5(filelistDir, `video.${getFileExtensionFromCodec(codec, resolvedAudioCodec)}`) : null;
22352
+ const audioOutput = shouldCreateAudio ? join5(filelistDir, `audio.${getExtensionFromAudioCodec(resolvedAudioCodec)}`) : null;
22353
+ const chunkDurationInSeconds = framesPerChunk / fps;
22354
+ let concatenatedAudio = 0;
22355
+ let concatenatedVideo = 0;
22356
+ let muxing = 0;
22357
+ const updateProgress = () => {
22358
+ const totalFrames = (shouldCreateAudio ? numberOfFrames : 0) + (shouldCreateVideo ? numberOfFrames : 0) + numberOfFrames;
22359
+ const actualProgress = concatenatedAudio + concatenatedVideo + muxing;
22360
+ onProgress({
22361
+ frames: actualProgress / totalFrames * numberOfFrames,
22362
+ totalProgress: actualProgress / totalFrames
22363
+ });
22364
+ };
22365
+ Log.verbose({ indent, logLevel }, `Combining chunks, audio = ${shouldCreateAudio === false ? "no" : seamlessAudio ? "seamlessly" : "normally"}, video = ${shouldCreateVideo === false ? "no" : seamlessVideo ? "seamlessly" : "normally"}`);
22366
+ await Promise.all([
22367
+ shouldCreateAudio && audioOutput ? createCombinedAudio({
22368
+ audioBitrate,
22369
+ filelistDir,
22370
+ files: audioFiles,
22371
+ indent,
22372
+ logLevel,
22373
+ output: audioOutput,
22374
+ resolvedAudioCodec,
22375
+ seamless: seamlessAudio,
22376
+ chunkDurationInSeconds,
22377
+ addRemotionMetadata: !shouldCreateVideo,
22378
+ binariesDirectory,
22379
+ fps,
22380
+ cancelSignal,
22381
+ onProgress: (frames) => {
22382
+ concatenatedAudio = frames;
22383
+ updateProgress();
22384
+ }
22385
+ }) : null,
22386
+ shouldCreateVideo && !seamlessVideo && videoOutput ? combineVideoStreams({
22387
+ codec,
22388
+ filelistDir,
22389
+ fps,
22390
+ indent,
22391
+ logLevel,
22392
+ numberOfGifLoops,
22393
+ output: videoOutput,
22394
+ files: videoFiles,
22395
+ addRemotionMetadata: !shouldCreateAudio,
22396
+ binariesDirectory,
22397
+ cancelSignal,
22398
+ onProgress: (frames) => {
22399
+ concatenatedVideo = frames;
22400
+ updateProgress();
22401
+ }
22402
+ }) : null
22403
+ ].filter(truthy));
22404
+ try {
22405
+ await muxVideoAndAudio({
22406
+ audioOutput,
22407
+ indent,
22408
+ logLevel,
22409
+ onProgress: (frames) => {
22410
+ muxing = frames;
22411
+ updateProgress();
22412
+ },
22413
+ output,
22414
+ videoOutput: seamlessVideo ? combineVideoStreamsSeamlessly({ files: videoFiles }) : videoOutput,
22415
+ binariesDirectory,
22416
+ fps,
22417
+ cancelSignal,
22418
+ addFaststart: codecSupportsFastStart[codec],
22419
+ metadata
22420
+ });
22421
+ onProgress({ totalProgress: 1, frames: numberOfFrames });
22422
+ rmSync5(filelistDir, { recursive: true });
22423
+ } catch (err) {
22424
+ rmSync5(filelistDir, { recursive: true });
22425
+ throw err;
22426
+ }
22427
+ };
22428
+ var combineChunks = (options) => {
22429
+ return internalCombineChunks({
22430
+ audioBitrate: options.audioBitrate ?? null,
22431
+ numberOfGifLoops: options.numberOfGifLoops ?? null,
22432
+ indent: false,
22433
+ logLevel: options.logLevel ?? "info",
22434
+ binariesDirectory: options.binariesDirectory ?? null,
22435
+ cancelSignal: options.cancelSignal,
22436
+ metadata: options.metadata ?? null,
22437
+ audioCodec: options.audioCodec ?? null,
22438
+ preferLossless: options.preferLossless,
22439
+ audioFiles: options.audioFiles,
22440
+ codec: options.codec,
22441
+ fps: options.fps,
22442
+ framesPerChunk: options.framesPerChunk,
22443
+ outputLocation: options.outputLocation,
22444
+ onProgress: options.onProgress ?? (() => {
22445
+ }),
22446
+ videoFiles: options.videoFiles,
22447
+ everyNthFrame: options.everyNthFrame ?? 1,
22448
+ frameRange: options.frameRange ?? null,
22449
+ compositionDurationInFrames: options.compositionDurationInFrames
22450
+ });
22451
+ };
22452
22452
  // src/extract-audio.ts
22453
22453
  var extractAudio = async (options) => {
22454
22454
  const compositor = startLongRunningCompositor({
@@ -22593,7 +22593,6 @@ var RenderInternals = {
22593
22593
  convertToPositiveFrameIndex,
22594
22594
  findRemotionRoot,
22595
22595
  validateBitrate,
22596
- combineChunks,
22597
22596
  getMinConcurrency,
22598
22597
  getMaxConcurrency,
22599
22598
  getDefaultAudioCodec,
@@ -22634,7 +22633,10 @@ var RenderInternals = {
22634
22633
  toMegabytes,
22635
22634
  internalEnsureBrowser,
22636
22635
  printUsefulErrorMessage,
22637
- DEFAULT_RENDER_FRAMES_OFFTHREAD_VIDEO_THREADS
22636
+ DEFAULT_RENDER_FRAMES_OFFTHREAD_VIDEO_THREADS,
22637
+ canConcatVideoSeamlessly,
22638
+ canConcatAudioSeamlessly,
22639
+ internalCombineChunks
22638
22640
  };
22639
22641
  checkRuntimeVersion("info", false);
22640
22642
  export {
@@ -22652,6 +22654,7 @@ export {
22652
22654
  getCompositions,
22653
22655
  extractAudio,
22654
22656
  ensureBrowser,
22657
+ combineChunks,
22655
22658
  RenderInternals,
22656
22659
  ErrorWithStackFrame
22657
22660
  };