@remotion/renderer 4.0.538 → 4.0.539

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.
@@ -46,8 +46,7 @@ const readFile = async (url, redirectsSoFar = 0) => {
46
46
  if (!response.headers.location) {
47
47
  throw new Error(`Received a status code ${response.statusCode} but no "Location" header while calling ${response.headers.location}`);
48
48
  }
49
- const { origin } = new URL(url);
50
- const redirectUrl = new URL(response.headers.location, origin).toString();
49
+ const redirectUrl = new URL(response.headers.location, url).toString();
51
50
  request.destroy();
52
51
  response.destroy();
53
52
  return (0, exports.readFile)(redirectUrl, redirectsSoFar + 1);
@@ -3291,12 +3291,11 @@ var isProductionOption = {
3291
3291
  /* @__PURE__ */ jsx47("code", {
3292
3292
  children: "false"
3293
3293
  }),
3294
- " if this a development render to not count it as a billable render on remotion.pro. Only can be used in conjuction with",
3295
- " ",
3294
+ " for a development render so it is not counted as a billable render on remotion.pro. Defaults to ",
3296
3295
  /* @__PURE__ */ jsx47("code", {
3297
- children: "licenseKey"
3296
+ children: "true"
3298
3297
  }),
3299
- "."
3298
+ ". Only has an effect when a license key is provided."
3300
3299
  ]
3301
3300
  }),
3302
3301
  ssrName: "isProduction",
@@ -3498,6 +3497,26 @@ var licenseKeyOption = {
3498
3497
  /* @__PURE__ */ jsx51("code", {
3499
3498
  children: "@remotion/licensing"
3500
3499
  }),
3500
+ ". For ",
3501
+ /* @__PURE__ */ jsx51("code", {
3502
+ children: "npx remotion render"
3503
+ }),
3504
+ " and",
3505
+ " ",
3506
+ /* @__PURE__ */ jsx51("code", {
3507
+ children: "npx remotion still"
3508
+ }),
3509
+ ", pass a private API key starting with",
3510
+ " ",
3511
+ /* @__PURE__ */ jsx51("code", {
3512
+ children: "rm_sec_"
3513
+ }),
3514
+ " from the License keys page on",
3515
+ " ",
3516
+ /* @__PURE__ */ jsx51("a", {
3517
+ href: "https://www.remotion.pro/dashboard",
3518
+ children: "remotion.pro"
3519
+ }),
3501
3520
  "."
3502
3521
  ]
3503
3522
  }),
@@ -1155,8 +1155,7 @@ var readFile = async (url, redirectsSoFar = 0) => {
1155
1155
  if (!response.headers.location) {
1156
1156
  throw new Error(`Received a status code ${response.statusCode} but no "Location" header while calling ${response.headers.location}`);
1157
1157
  }
1158
- const { origin } = new URL(url);
1159
- const redirectUrl = new URL(response.headers.location, origin).toString();
1158
+ const redirectUrl = new URL(response.headers.location, url).toString();
1160
1159
  request.destroy();
1161
1160
  response.destroy();
1162
1161
  return readFile(redirectUrl, redirectsSoFar + 1);
@@ -17698,6 +17697,69 @@ var makeRange = (from, to) => {
17698
17697
  });
17699
17698
  };
17700
17699
 
17700
+ // src/port-config.ts
17701
+ import os9 from "os";
17702
+ var cached = null;
17703
+ var getPortConfig = (preferIpv4) => {
17704
+ if (cached) {
17705
+ return cached;
17706
+ }
17707
+ const networkInterfaces = os9.networkInterfaces();
17708
+ const flattened = flattenNetworkInterfaces(networkInterfaces);
17709
+ const host = getHostToBind(flattened, preferIpv4);
17710
+ const hostsToTry = getHostsToTry(flattened);
17711
+ const response = { host, hostsToTry };
17712
+ cached = response;
17713
+ return response;
17714
+ };
17715
+ var getHostToBind = (flattened, preferIpv4) => {
17716
+ if (preferIpv4 || !isIpV6Supported(flattened)) {
17717
+ return "0.0.0.0";
17718
+ }
17719
+ return "::";
17720
+ };
17721
+ var getHostsToTry = (flattened) => {
17722
+ return [
17723
+ hasIPv6LoopbackAddress(flattened) ? "::1" : null,
17724
+ hasIpv4LoopbackAddress(flattened) ? "127.0.0.1" : null,
17725
+ isIpV6Supported(flattened) ? "::" : null,
17726
+ "0.0.0.0"
17727
+ ].filter(truthy);
17728
+ };
17729
+ var flattenNetworkInterfaces = (networkInterfaces) => {
17730
+ const result = [];
17731
+ for (const iface in networkInterfaces) {
17732
+ for (const configuration of networkInterfaces[iface]) {
17733
+ result.push(configuration);
17734
+ }
17735
+ }
17736
+ return result;
17737
+ };
17738
+ var isIpV6Supported = (flattened) => {
17739
+ for (const configuration of flattened) {
17740
+ if (configuration.family === "IPv6" && !configuration.internal) {
17741
+ return true;
17742
+ }
17743
+ }
17744
+ return false;
17745
+ };
17746
+ var hasIPv6LoopbackAddress = (flattened) => {
17747
+ for (const configuration of flattened) {
17748
+ if (configuration.family === "IPv6" && configuration.internal && configuration.address === "::1") {
17749
+ return true;
17750
+ }
17751
+ }
17752
+ return false;
17753
+ };
17754
+ var hasIpv4LoopbackAddress = (flattened) => {
17755
+ for (const configuration of flattened) {
17756
+ if (configuration.family === "IPv4" && configuration.internal && configuration.address === "127.0.0.1") {
17757
+ return true;
17758
+ }
17759
+ }
17760
+ return false;
17761
+ };
17762
+
17701
17763
  // src/serve-handler/index.ts
17702
17764
  import { createReadStream, promises as promises3 } from "node:fs";
17703
17765
  import path19 from "node:path";
@@ -17743,7 +17805,7 @@ var rangeParser = (size, str) => {
17743
17805
  let start = parseInt(range[0], 10);
17744
17806
  let end = parseInt(range[1], 10);
17745
17807
  if (isNaN(start)) {
17746
- start = size - end;
17808
+ start = Math.max(size - end, 0);
17747
17809
  end = size - 1;
17748
17810
  } else if (isNaN(end)) {
17749
17811
  end = size - 1;
@@ -17985,6 +18047,7 @@ var serveStatic = async (path, options2) => {
17985
18047
  let selectedPort = null;
17986
18048
  const maxTries = 10;
17987
18049
  const host = "127.0.0.1";
18050
+ const { hostsToTry } = getPortConfig(options2.forceIPv4);
17988
18051
  for (let i = 0;i < maxTries; i++) {
17989
18052
  let unlock = () => {};
17990
18053
  try {
@@ -17993,7 +18056,7 @@ var serveStatic = async (path, options2) => {
17993
18056
  desiredPort: options2?.port ?? undefined,
17994
18057
  from: 3000,
17995
18058
  to: 3100,
17996
- hostsToTry: [host]
18059
+ hostsToTry
17997
18060
  }).then(({ port, unlockPort }) => {
17998
18061
  unlock = unlockPort;
17999
18062
  server.listen({ port, host });
@@ -19496,69 +19559,6 @@ var validateSelectedPixelFormatAndCodecCombination = (pixelFormat, codec) => {
19496
19559
  }
19497
19560
  };
19498
19561
 
19499
- // src/port-config.ts
19500
- import os9 from "os";
19501
- var cached = null;
19502
- var getPortConfig = (preferIpv4) => {
19503
- if (cached) {
19504
- return cached;
19505
- }
19506
- const networkInterfaces = os9.networkInterfaces();
19507
- const flattened = flattenNetworkInterfaces(networkInterfaces);
19508
- const host = getHostToBind(flattened, preferIpv4);
19509
- const hostsToTry = getHostsToTry(flattened);
19510
- const response = { host, hostsToTry };
19511
- cached = response;
19512
- return response;
19513
- };
19514
- var getHostToBind = (flattened, preferIpv4) => {
19515
- if (preferIpv4 || !isIpV6Supported(flattened)) {
19516
- return "0.0.0.0";
19517
- }
19518
- return "::";
19519
- };
19520
- var getHostsToTry = (flattened) => {
19521
- return [
19522
- hasIPv6LoopbackAddress(flattened) ? "::1" : null,
19523
- hasIpv4LoopbackAddress(flattened) ? "127.0.0.1" : null,
19524
- isIpV6Supported(flattened) ? "::" : null,
19525
- "0.0.0.0"
19526
- ].filter(truthy);
19527
- };
19528
- var flattenNetworkInterfaces = (networkInterfaces) => {
19529
- const result = [];
19530
- for (const iface in networkInterfaces) {
19531
- for (const configuration of networkInterfaces[iface]) {
19532
- result.push(configuration);
19533
- }
19534
- }
19535
- return result;
19536
- };
19537
- var isIpV6Supported = (flattened) => {
19538
- for (const configuration of flattened) {
19539
- if (configuration.family === "IPv6" && !configuration.internal) {
19540
- return true;
19541
- }
19542
- }
19543
- return false;
19544
- };
19545
- var hasIPv6LoopbackAddress = (flattened) => {
19546
- for (const configuration of flattened) {
19547
- if (configuration.family === "IPv6" && configuration.internal && configuration.address === "::1") {
19548
- return true;
19549
- }
19550
- }
19551
- return false;
19552
- };
19553
- var hasIpv4LoopbackAddress = (flattened) => {
19554
- for (const configuration of flattened) {
19555
- if (configuration.family === "IPv4" && configuration.internal && configuration.address === "127.0.0.1") {
19556
- return true;
19557
- }
19558
- }
19559
- return false;
19560
- };
19561
-
19562
19562
  // src/probe-shared-memory-capture.ts
19563
19563
  import fs16 from "node:fs";
19564
19564
  import os10 from "node:os";
@@ -23816,7 +23816,8 @@ var createSilentAudio = async ({
23816
23816
  };
23817
23817
 
23818
23818
  // src/merge-audio-track.ts
23819
- var mergeAudioTrackUnlimited = async ({
23819
+ var limit2 = pLimit(3);
23820
+ var mergeAudioTrack = async ({
23820
23821
  outName,
23821
23822
  files,
23822
23823
  downloadMap,
@@ -23831,7 +23832,7 @@ var mergeAudioTrackUnlimited = async ({
23831
23832
  sampleRate
23832
23833
  }) => {
23833
23834
  if (files.length === 0) {
23834
- await createSilentAudio({
23835
+ await limit2(createSilentAudio, {
23835
23836
  outName,
23836
23837
  chunkLengthInSeconds,
23837
23838
  indent,
@@ -23920,29 +23921,27 @@ var mergeAudioTrackUnlimited = async ({
23920
23921
  ["-y", outName]
23921
23922
  ].filter(truthy).flat(2);
23922
23923
  Log.verbose({ indent, logLevel }, `Merging audio tracks`, "Command:", `ffmpeg ${args.join(" ")}`, "Complex filter script:", complexFilter);
23923
- const task = callFf({
23924
- bin: "ffmpeg",
23925
- args,
23926
- indent,
23927
- logLevel,
23928
- binariesDirectory,
23929
- cancelSignal
23930
- });
23931
- task.stderr?.on("data", (data) => {
23932
- const utf8 = data.toString("utf8");
23933
- const parsed = parseFfmpegProgress(utf8, fps);
23934
- if (parsed !== undefined) {
23935
- onProgress(parsed / (chunkLengthInSeconds * fps));
23936
- }
23924
+ await limit2(async () => {
23925
+ const task = callFf({
23926
+ bin: "ffmpeg",
23927
+ args,
23928
+ indent,
23929
+ logLevel,
23930
+ binariesDirectory,
23931
+ cancelSignal
23932
+ });
23933
+ task.stderr?.on("data", (data) => {
23934
+ const utf8 = data.toString("utf8");
23935
+ const parsed = parseFfmpegProgress(utf8, fps);
23936
+ if (parsed !== undefined) {
23937
+ onProgress(parsed / (chunkLengthInSeconds * fps));
23938
+ }
23939
+ });
23940
+ await task;
23937
23941
  });
23938
- await task;
23939
23942
  onProgress(1);
23940
23943
  cleanup();
23941
23944
  };
23942
- var limit2 = pLimit(3);
23943
- var mergeAudioTrack = (options2) => {
23944
- return limit2(mergeAudioTrackUnlimited, options2);
23945
- };
23946
23945
 
23947
23946
  // src/merge-inline-audio-tracks.ts
23948
23947
  import path27 from "node:path";
@@ -16,5 +16,5 @@ type Options = {
16
16
  chunkLengthInSeconds: number;
17
17
  sampleRate: number;
18
18
  };
19
- export declare const mergeAudioTrack: (options: Options) => Promise<void>;
19
+ export declare const mergeAudioTrack: ({ outName, files, downloadMap, remotionRoot, indent, logLevel, binariesDirectory, cancelSignal, onProgress, fps, chunkLengthInSeconds, sampleRate, }: Options) => Promise<void>;
20
20
  export {};
@@ -16,10 +16,13 @@ const p_limit_1 = require("./p-limit");
16
16
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
17
17
  const tmp_dir_1 = require("./tmp-dir");
18
18
  const truthy_1 = require("./truthy");
19
- const mergeAudioTrackUnlimited = async ({ outName, files, downloadMap, remotionRoot, indent, logLevel, binariesDirectory, cancelSignal, onProgress, fps, chunkLengthInSeconds, sampleRate, }) => {
20
- var _a;
19
+ // Limits how many FFmpeg processes mix audio at once. Only the FFmpeg calls
20
+ // take a slot: a merge that waits for its chunks to be merged first holds none,
21
+ // so concurrent renders with many audio tracks can't block each other.
22
+ const limit = (0, p_limit_1.pLimit)(3);
23
+ const mergeAudioTrack = async ({ outName, files, downloadMap, remotionRoot, indent, logLevel, binariesDirectory, cancelSignal, onProgress, fps, chunkLengthInSeconds, sampleRate, }) => {
21
24
  if (files.length === 0) {
22
- await (0, create_silent_audio_1.createSilentAudio)({
25
+ await limit(create_silent_audio_1.createSilentAudio, {
23
26
  outName,
24
27
  chunkLengthInSeconds,
25
28
  indent,
@@ -109,28 +112,26 @@ const mergeAudioTrackUnlimited = async ({ outName, files, downloadMap, remotionR
109
112
  .filter(truthy_1.truthy)
110
113
  .flat(2);
111
114
  logger_1.Log.verbose({ indent, logLevel }, `Merging audio tracks`, 'Command:', `ffmpeg ${args.join(' ')}`, 'Complex filter script:', complexFilter);
112
- const task = (0, call_ffmpeg_1.callFf)({
113
- bin: 'ffmpeg',
114
- args,
115
- indent,
116
- logLevel,
117
- binariesDirectory,
118
- cancelSignal,
119
- });
120
- (_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
121
- const utf8 = data.toString('utf8');
122
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(utf8, fps);
123
- if (parsed !== undefined) {
124
- onProgress(parsed / (chunkLengthInSeconds * fps));
125
- }
115
+ await limit(async () => {
116
+ var _a;
117
+ const task = (0, call_ffmpeg_1.callFf)({
118
+ bin: 'ffmpeg',
119
+ args,
120
+ indent,
121
+ logLevel,
122
+ binariesDirectory,
123
+ cancelSignal,
124
+ });
125
+ (_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
126
+ const utf8 = data.toString('utf8');
127
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(utf8, fps);
128
+ if (parsed !== undefined) {
129
+ onProgress(parsed / (chunkLengthInSeconds * fps));
130
+ }
131
+ });
132
+ await task;
126
133
  });
127
- await task;
128
134
  onProgress(1);
129
135
  cleanup();
130
136
  };
131
- // Must be at least 3 because recursively called twice in mergeAudioTrack
132
- const limit = (0, p_limit_1.pLimit)(3);
133
- const mergeAudioTrack = (options) => {
134
- return limit(mergeAudioTrackUnlimited, options);
135
- };
136
137
  exports.mergeAudioTrack = mergeAudioTrack;
@@ -0,0 +1,16 @@
1
+ export declare const experimentalClientSideRenderingOption: {
2
+ name: string;
3
+ cliFlag: "enable-experimental-client-side-rendering";
4
+ description: () => import("react/jsx-runtime").JSX.Element;
5
+ ssrName: null;
6
+ docLink: string;
7
+ type: boolean;
8
+ getValue: ({ commandLine }: {
9
+ commandLine: Record<string, unknown>;
10
+ }) => {
11
+ value: boolean;
12
+ source: string;
13
+ };
14
+ setConfig(value: boolean): void;
15
+ id: "enable-experimental-client-side-rendering";
16
+ };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.experimentalClientSideRenderingOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ let experimentalClientSideRenderingEnabled = false;
6
+ const cliFlag = 'enable-experimental-client-side-rendering';
7
+ exports.experimentalClientSideRenderingOption = {
8
+ name: 'Enable Experimental Client-Side Rendering',
9
+ cliFlag,
10
+ description: () => (jsx_runtime_1.jsx(jsx_runtime_1.Fragment, { children: "Enable WIP client-side rendering in the Remotion Studio. See https://www.remotion.dev/docs/client-side-rendering/ for notes." })),
11
+ ssrName: null,
12
+ docLink: 'https://www.remotion.dev/docs/client-side-rendering',
13
+ type: false,
14
+ getValue: ({ commandLine }) => {
15
+ if (commandLine[cliFlag] !== null) {
16
+ return {
17
+ value: commandLine[cliFlag],
18
+ source: 'cli',
19
+ };
20
+ }
21
+ return {
22
+ value: experimentalClientSideRenderingEnabled,
23
+ source: 'config',
24
+ };
25
+ },
26
+ setConfig(value) {
27
+ experimentalClientSideRenderingEnabled = value;
28
+ },
29
+ id: cliFlag,
30
+ };
@@ -0,0 +1,19 @@
1
+ export declare const experimentalSequenceActivityLimitOption: {
2
+ name: string;
3
+ addedIn: string;
4
+ cliFlag: "experimental-sequence-activity-limit";
5
+ id: "experimental-sequence-activity-limit";
6
+ description: () => import("react/jsx-runtime").JSX.Element;
7
+ ssrName: null;
8
+ docLink: string;
9
+ type: number;
10
+ getValue: ({ commandLine }: {
11
+ commandLine: Record<string, unknown>;
12
+ }) => {
13
+ value: number;
14
+ source: string;
15
+ };
16
+ setConfig(value: number): void;
17
+ getConfigValue: () => number | null;
18
+ reset: () => void;
19
+ };
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.experimentalSequenceActivityLimitOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const no_react_1 = require("remotion/no-react");
6
+ let configuredLimit = null;
7
+ const cliFlag = 'experimental-sequence-activity-limit';
8
+ exports.experimentalSequenceActivityLimitOption = {
9
+ name: 'Experimental hidden Sequence Activity limit',
10
+ addedIn: '4.0.535',
11
+ cliFlag,
12
+ id: cliFlag,
13
+ description: () => (jsx_runtime_1.jsx(jsx_runtime_1.Fragment, { children: "Limit nearby hidden scenes discovered by experimental Sequence Activity in the Remotion Studio, including nested sequences and their ancestors. Visible, premounted, and postmounted scenes always render. Defaults to 20. Must be a safe integer of at least -1. Set to 0 to disable hidden discovery, or -1 for unlimited discovery without admission delays. Only applies when experimental Sequence Activity is enabled. Does not affect rendering." })),
14
+ ssrName: null,
15
+ docLink: 'https://www.remotion.dev/docs/options/experimental-sequence-activity-limit',
16
+ type: 0,
17
+ getValue: ({ commandLine }) => {
18
+ if (commandLine[cliFlag] !== undefined) {
19
+ const value = commandLine[cliFlag];
20
+ if (!Number.isSafeInteger(value) || value < -1) {
21
+ throw new Error('Sequence Activity limit must be a safe integer of at least -1.');
22
+ }
23
+ return { value: value, source: 'cli' };
24
+ }
25
+ return {
26
+ value: configuredLimit !== null && configuredLimit !== void 0 ? configuredLimit : no_react_1.NoReactInternals.DEFAULT_SEQUENCE_ACTIVITY_LIMIT,
27
+ source: 'config',
28
+ };
29
+ },
30
+ setConfig(value) {
31
+ if (!Number.isSafeInteger(value) || value < -1) {
32
+ throw new Error('Sequence Activity limit must be a safe integer of at least -1.');
33
+ }
34
+ configuredLimit = value;
35
+ },
36
+ getConfigValue: () => configuredLimit,
37
+ reset: () => {
38
+ configuredLimit = null;
39
+ },
40
+ };
@@ -0,0 +1,19 @@
1
+ export declare const experimentalSequenceActivityOption: {
2
+ name: string;
3
+ addedIn: string;
4
+ cliFlag: "experimental-sequence-activity";
5
+ description: () => import("react/jsx-runtime").JSX.Element;
6
+ ssrName: null;
7
+ docLink: string;
8
+ type: boolean;
9
+ getValue: ({ commandLine }: {
10
+ commandLine: Record<string, unknown>;
11
+ }) => {
12
+ value: boolean;
13
+ source: string;
14
+ };
15
+ setConfig(value: boolean): void;
16
+ getConfigValue: () => boolean | null;
17
+ reset: () => void;
18
+ id: "experimental-sequence-activity";
19
+ };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.experimentalSequenceActivityOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ let experimentalSequenceActivityEnabled = false;
6
+ let configuredExperimentalSequenceActivityEnabled = null;
7
+ const cliFlag = 'experimental-sequence-activity';
8
+ exports.experimentalSequenceActivityOption = {
9
+ name: 'Enable experimental Sequence Activity',
10
+ addedIn: '4.0.535',
11
+ cliFlag,
12
+ description: () => (jsx_runtime_1.jsx(jsx_runtime_1.Fragment, { children: "Discover layers in nearby hidden scenes in the Remotion Studio before they become visible. This experimental feature may use more CPU. Disabled by default. Does not affect rendering." })),
13
+ ssrName: null,
14
+ docLink: 'https://www.remotion.dev/docs/options/experimental-sequence-activity',
15
+ type: false,
16
+ getValue: ({ commandLine }) => {
17
+ if (commandLine[cliFlag] !== undefined && commandLine[cliFlag] !== null) {
18
+ return {
19
+ value: commandLine[cliFlag],
20
+ source: 'cli',
21
+ };
22
+ }
23
+ return {
24
+ value: experimentalSequenceActivityEnabled,
25
+ source: 'config',
26
+ };
27
+ },
28
+ setConfig(value) {
29
+ experimentalSequenceActivityEnabled = value;
30
+ configuredExperimentalSequenceActivityEnabled = value;
31
+ },
32
+ getConfigValue: () => configuredExperimentalSequenceActivityEnabled,
33
+ reset: () => {
34
+ experimentalSequenceActivityEnabled = false;
35
+ configuredExperimentalSequenceActivityEnabled = null;
36
+ },
37
+ id: cliFlag,
38
+ };
@@ -0,0 +1,19 @@
1
+ export declare const experimentalTracksOption: {
2
+ name: string;
3
+ addedIn: string;
4
+ cliFlag: "experimental-tracks";
5
+ description: () => import("react/jsx-runtime").JSX.Element;
6
+ ssrName: null;
7
+ docLink: string;
8
+ type: boolean;
9
+ getValue: ({ commandLine }: {
10
+ commandLine: Record<string, unknown>;
11
+ }) => {
12
+ value: boolean;
13
+ source: string;
14
+ };
15
+ setConfig(value: boolean): void;
16
+ getConfigValue: () => boolean | null;
17
+ reset: () => void;
18
+ id: "experimental-tracks";
19
+ };
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.experimentalTracksOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ let experimentalTracksEnabled = false;
6
+ let configuredExperimentalTracksEnabled = null;
7
+ const cliFlag = 'experimental-tracks';
8
+ exports.experimentalTracksOption = {
9
+ name: 'Enable experimental timeline tracks',
10
+ addedIn: '4.0.534',
11
+ cliFlag,
12
+ description: () => (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: ["Enable experimental timeline tracks in the Remotion Studio. Group clips from ",
13
+ jsx_runtime_1.jsx("code", { children: '<Track>' }),
14
+ ", ",
15
+ jsx_runtime_1.jsx("code", { children: '<Series>' }),
16
+ " and", ' ', jsx_runtime_1.jsx("code", { children: '<TransitionSeries>' }),
17
+ " on shared rows, with overlays on additional rows. Disabled by default."] })),
18
+ ssrName: null,
19
+ docLink: 'https://www.remotion.dev/docs/options/experimental-tracks',
20
+ type: false,
21
+ getValue: ({ commandLine }) => {
22
+ if (commandLine[cliFlag] !== undefined && commandLine[cliFlag] !== null) {
23
+ return {
24
+ value: commandLine[cliFlag],
25
+ source: 'cli',
26
+ };
27
+ }
28
+ return {
29
+ value: experimentalTracksEnabled,
30
+ source: 'config',
31
+ };
32
+ },
33
+ setConfig(value) {
34
+ experimentalTracksEnabled = value;
35
+ configuredExperimentalTracksEnabled = value;
36
+ },
37
+ getConfigValue: () => configuredExperimentalTracksEnabled,
38
+ reset: () => {
39
+ experimentalTracksEnabled = false;
40
+ configuredExperimentalTracksEnabled = null;
41
+ },
42
+ id: cliFlag,
43
+ };
@@ -8,7 +8,7 @@ exports.isProductionOption = {
8
8
  name: 'Is Production',
9
9
  addedIn: '4.0.409',
10
10
  cliFlag,
11
- description: () => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Pass ", (0, jsx_runtime_1.jsx)("code", { children: "false" }), " if this a development render to not count it as a billable render on remotion.pro. Only can be used in conjuction with", ' ', (0, jsx_runtime_1.jsx)("code", { children: "licenseKey" }), "."] })),
11
+ description: () => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Pass ", (0, jsx_runtime_1.jsx)("code", { children: "false" }), " for a development render so it is not counted as a billable render on remotion.pro. Defaults to ", (0, jsx_runtime_1.jsx)("code", { children: "true" }), ". Only has an effect when a license key is provided."] })),
12
12
  ssrName: 'isProduction',
13
13
  docLink: 'https://www.remotion.dev/docs/options/is-production',
14
14
  getValue: ({ commandLine }) => {
@@ -8,7 +8,7 @@ exports.licenseKeyOption = {
8
8
  name: 'License key',
9
9
  addedIn: '4.0.409',
10
10
  cliFlag,
11
- description: () => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["License key for sending a usage event using", ' ', (0, jsx_runtime_1.jsx)("code", { children: "@remotion/licensing" }), "."] })),
11
+ description: () => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["License key for sending a usage event using", ' ', (0, jsx_runtime_1.jsx)("code", { children: "@remotion/licensing" }), ". For ", (0, jsx_runtime_1.jsx)("code", { children: "npx remotion render" }), " and", ' ', (0, jsx_runtime_1.jsx)("code", { children: "npx remotion still" }), ", pass a private API key starting with", ' ', (0, jsx_runtime_1.jsx)("code", { children: "rm_sec_" }), " from the License keys page on", ' ', (0, jsx_runtime_1.jsx)("a", { href: "https://www.remotion.pro/dashboard", children: "remotion.pro" }), "."] })),
12
12
  ssrName: 'licenseKey',
13
13
  docLink: 'https://www.remotion.dev/docs/options/license-key',
14
14
  type: null,
@@ -0,0 +1,13 @@
1
+ export declare const hasEnoughMemoryForParallelEncoding: ({ freeMemory, estimatedUsage, }: {
2
+ freeMemory: number;
3
+ estimatedUsage: number;
4
+ }) => boolean;
5
+ export declare const shouldUseParallelEncoding: ({ width, height, logLevel, }: {
6
+ width: number;
7
+ height: number;
8
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
9
+ }) => {
10
+ hasEnoughMemory: boolean;
11
+ freeMemory: number;
12
+ estimatedUsage: number;
13
+ };
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldUseParallelEncoding = exports.hasEnoughMemoryForParallelEncoding = void 0;
4
+ const get_available_memory_1 = require("./memory/get-available-memory");
5
+ const MINIMUM_MEMORY_LEFT = 2000000000;
6
+ const hasEnoughMemoryForParallelEncoding = ({ freeMemory, estimatedUsage, }) => {
7
+ return freeMemory - estimatedUsage > MINIMUM_MEMORY_LEFT;
8
+ };
9
+ exports.hasEnoughMemoryForParallelEncoding = hasEnoughMemoryForParallelEncoding;
10
+ const estimateMemoryUsageForPrestitcher = ({ width, height, }) => {
11
+ // Empirically we detected that per 1 million pixels, FFMPEG uses around 1GB of memory, relatively independent of
12
+ // the duration of the video.
13
+ const memoryUsageFor4K = 1000000000;
14
+ const memoryUsageOfPixel = memoryUsageFor4K / 1000000;
15
+ return memoryUsageOfPixel * width * height;
16
+ };
17
+ const shouldUseParallelEncoding = ({ width, height, logLevel, }) => {
18
+ const freeMemory = (0, get_available_memory_1.getAvailableMemory)(logLevel);
19
+ const estimatedUsage = estimateMemoryUsageForPrestitcher({
20
+ height,
21
+ width,
22
+ });
23
+ const hasEnoughMemory = (0, exports.hasEnoughMemoryForParallelEncoding)({
24
+ estimatedUsage,
25
+ freeMemory,
26
+ });
27
+ return {
28
+ hasEnoughMemory,
29
+ freeMemory,
30
+ estimatedUsage,
31
+ };
32
+ };
33
+ exports.shouldUseParallelEncoding = shouldUseParallelEncoding;
@@ -0,0 +1,7 @@
1
+ export declare const renderPartitions: ({ frames, concurrency, }: {
2
+ frames: number[];
3
+ concurrency: number;
4
+ }) => {
5
+ partitions: number[][];
6
+ getNextFrame: (pageIndex: number) => number;
7
+ };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderPartitions = void 0;
4
+ const renderPartitions = ({ frames, concurrency, }) => {
5
+ const partitions = [];
6
+ let start = 0;
7
+ for (let i = 0; i < concurrency; i++) {
8
+ const end = start + Math.ceil((frames.length - start) / (concurrency - i));
9
+ partitions.push(frames.slice(start, end));
10
+ start = end;
11
+ }
12
+ return {
13
+ partitions,
14
+ getNextFrame: (pageIndex) => {
15
+ if (partitions[pageIndex].length === 0) {
16
+ const partitionLengths = partitions.map((p) => p.length);
17
+ if (partitionLengths.every((p) => p === 0)) {
18
+ throw new Error('No more frames to render');
19
+ }
20
+ let longestRemainingPartitionIndex = -1;
21
+ for (let i = 0; i < partitions.length; i++) {
22
+ if (longestRemainingPartitionIndex === -1) {
23
+ longestRemainingPartitionIndex = i;
24
+ continue;
25
+ }
26
+ if (partitions[i].length >
27
+ partitions[longestRemainingPartitionIndex].length) {
28
+ longestRemainingPartitionIndex = i;
29
+ }
30
+ }
31
+ if (longestRemainingPartitionIndex === -1) {
32
+ throw new Error('No more frames to render');
33
+ }
34
+ // Round up - let's say only one frame is left to render, we should take this one
35
+ const slicePoint = Math.ceil(partitions[longestRemainingPartitionIndex].length / 2) -
36
+ // but don't forget it's zero indexed :)
37
+ 1;
38
+ partitions[pageIndex] =
39
+ partitions[longestRemainingPartitionIndex].slice(slicePoint);
40
+ partitions[longestRemainingPartitionIndex] = partitions[longestRemainingPartitionIndex].slice(0, slicePoint);
41
+ }
42
+ const value = partitions[pageIndex].shift();
43
+ if (value === undefined) {
44
+ throw new Error('No more frames to render');
45
+ }
46
+ return value;
47
+ },
48
+ };
49
+ };
50
+ exports.renderPartitions = renderPartitions;
@@ -27,7 +27,7 @@ const rangeParser = (size, str) => {
27
27
  let end = parseInt(range[1], 10);
28
28
  // -nnn
29
29
  if (isNaN(start)) {
30
- start = size - end;
30
+ start = Math.max(size - end, 0);
31
31
  end = size - 1;
32
32
  // nnn-
33
33
  }
@@ -7,6 +7,7 @@ exports.serveStatic = void 0;
7
7
  const node_http_1 = __importDefault(require("node:http"));
8
8
  const get_port_1 = require("./get-port");
9
9
  const offthread_video_server_1 = require("./offthread-video-server");
10
+ const port_config_1 = require("./port-config");
10
11
  const serve_handler_1 = require("./serve-handler");
11
12
  const serveStatic = async (path, options) => {
12
13
  const { listener: offthreadRequest, close: closeCompositor, compositor, } = (0, offthread_video_server_1.startOffthreadVideoServer)({
@@ -55,6 +56,8 @@ const serveStatic = async (path, options) => {
55
56
  const maxTries = 10;
56
57
  // Rendering only needs local access, unlike the Studio server.
57
58
  const host = '127.0.0.1';
59
+ // URLs use localhost, which can resolve to another supported address family.
60
+ const { hostsToTry } = (0, port_config_1.getPortConfig)(options.forceIPv4);
58
61
  for (let i = 0; i < maxTries; i++) {
59
62
  let unlock = () => { };
60
63
  try {
@@ -64,7 +67,7 @@ const serveStatic = async (path, options) => {
64
67
  desiredPort: (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : undefined,
65
68
  from: 3000,
66
69
  to: 3100,
67
- hostsToTry: [host],
70
+ hostsToTry,
68
71
  })
69
72
  .then(({ port, unlockPort }) => {
70
73
  unlock = unlockPort;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/renderer"
4
4
  },
5
5
  "name": "@remotion/renderer",
6
- "version": "4.0.538",
6
+ "version": "4.0.539",
7
7
  "description": "Render Remotion videos using Node.js or Bun",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "execa": "5.1.1",
25
- "remotion": "4.0.538",
26
- "@remotion/streaming": "4.0.538",
25
+ "remotion": "4.0.539",
26
+ "@remotion/streaming": "4.0.539",
27
27
  "source-map": "0.8.0",
28
28
  "ws": "8.21.0",
29
- "@remotion/licensing": "4.0.538"
29
+ "@remotion/licensing": "4.0.539"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=16.8.0",
@@ -40,21 +40,21 @@
40
40
  "react-dom": "19.2.3",
41
41
  "typescript": "7.1.0-beta",
42
42
  "@types/ws": "8.5.10",
43
- "@remotion/example-videos": "4.0.538",
44
- "@remotion/eslint-config-internal": "4.0.538",
43
+ "@remotion/example-videos": "4.0.539",
44
+ "@remotion/eslint-config-internal": "4.0.539",
45
45
  "eslint": "9.19.0",
46
46
  "@types/node": "20.12.14",
47
47
  "@types/pngjs": "6.0.5",
48
48
  "pngjs": "7.0.0"
49
49
  },
50
50
  "optionalDependencies": {
51
- "@remotion/compositor-darwin-arm64": "4.0.538",
52
- "@remotion/compositor-darwin-x64": "4.0.538",
53
- "@remotion/compositor-linux-arm64-gnu": "4.0.538",
54
- "@remotion/compositor-linux-arm64-musl": "4.0.538",
55
- "@remotion/compositor-linux-x64-gnu": "4.0.538",
56
- "@remotion/compositor-linux-x64-musl": "4.0.538",
57
- "@remotion/compositor-win32-x64-msvc": "4.0.538"
51
+ "@remotion/compositor-darwin-arm64": "4.0.539",
52
+ "@remotion/compositor-darwin-x64": "4.0.539",
53
+ "@remotion/compositor-linux-arm64-gnu": "4.0.539",
54
+ "@remotion/compositor-linux-arm64-musl": "4.0.539",
55
+ "@remotion/compositor-linux-x64-gnu": "4.0.539",
56
+ "@remotion/compositor-linux-x64-musl": "4.0.539",
57
+ "@remotion/compositor-win32-x64-msvc": "4.0.539"
58
58
  },
59
59
  "keywords": [
60
60
  "remotion",