@remotion/renderer 4.0.501 → 4.0.503

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.
Files changed (39) hide show
  1. package/dist/assets/calculate-asset-positions.d.ts +1 -1
  2. package/dist/assets/calculate-asset-positions.js +7 -1
  3. package/dist/browser/BrowserFetcher.js +61 -48
  4. package/dist/browser/browser-download-lock.d.ts +1 -0
  5. package/dist/browser/browser-download-lock.js +161 -0
  6. package/dist/client.d.ts +42 -5
  7. package/dist/client.js +6 -1
  8. package/dist/combine-chunks.d.ts +2 -2
  9. package/dist/combine-chunks.js +1 -1
  10. package/dist/create-audio.js +1 -1
  11. package/dist/esm/client.mjs +907 -692
  12. package/dist/esm/index.mjs +430 -181
  13. package/dist/esm/pure.mjs +5 -4
  14. package/dist/frame-range.d.ts +10 -1
  15. package/dist/frame-range.js +60 -36
  16. package/dist/get-duration-from-frame-range.d.ts +2 -1
  17. package/dist/get-duration-from-frame-range.js +7 -4
  18. package/dist/get-frame-to-render.d.ts +2 -1
  19. package/dist/get-frame-to-render.js +21 -5
  20. package/dist/index.d.ts +12 -3
  21. package/dist/index.js +9 -1
  22. package/dist/options/default-editor.d.ts +28 -0
  23. package/dist/options/default-editor.js +84 -0
  24. package/dist/options/frames.d.ts +4 -4
  25. package/dist/options/frames.js +51 -8
  26. package/dist/options/index.d.ts +40 -5
  27. package/dist/options/index.js +4 -0
  28. package/dist/options/rspack.d.ts +2 -2
  29. package/dist/options/rspack.js +3 -4
  30. package/dist/options/skip-skills.d.ts +19 -0
  31. package/dist/options/skip-skills.js +29 -0
  32. package/dist/pure.d.ts +1 -1
  33. package/dist/render-frame-with-option-to-reject.js +2 -1
  34. package/dist/render-frames.d.ts +5 -1
  35. package/dist/render-frames.js +42 -7
  36. package/dist/render-media.js +11 -5
  37. package/dist/validate-selected-frames.d.ts +4 -0
  38. package/dist/validate-selected-frames.js +34 -0
  39. package/package.json +13 -13
@@ -68,7 +68,7 @@ var require_pend = __commonJS((exports, module) => {
68
68
 
69
69
  // vendor/yauzl-patched/fd-slicer.js
70
70
  var require_fd_slicer = __commonJS((exports) => {
71
- var fs5 = __require("fs");
71
+ var fs6 = __require("fs");
72
72
  var util = __require("util");
73
73
  var stream = __require("stream");
74
74
  var Readable = stream.Readable;
@@ -93,7 +93,7 @@ var require_fd_slicer = __commonJS((exports) => {
93
93
  FdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
94
94
  var self = this;
95
95
  self.pend.go(function(cb) {
96
- fs5.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
96
+ fs6.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
97
97
  cb();
98
98
  callback(err, bytesRead, buffer2);
99
99
  });
@@ -102,7 +102,7 @@ var require_fd_slicer = __commonJS((exports) => {
102
102
  FdSlicer.prototype.write = function(buffer, offset, length, position, callback) {
103
103
  var self = this;
104
104
  self.pend.go(function(cb) {
105
- fs5.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
105
+ fs6.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
106
106
  cb();
107
107
  callback(err, written, buffer2);
108
108
  });
@@ -125,7 +125,7 @@ var require_fd_slicer = __commonJS((exports) => {
125
125
  if (self.refCount < 0)
126
126
  throw new Error("invalid unref");
127
127
  if (self.autoClose) {
128
- fs5.close(self.fd, onCloseDone);
128
+ fs6.close(self.fd, onCloseDone);
129
129
  }
130
130
  function onCloseDone(err) {
131
131
  if (err) {
@@ -164,7 +164,7 @@ var require_fd_slicer = __commonJS((exports) => {
164
164
  if (self._destroyed)
165
165
  return cb();
166
166
  var buffer = Buffer.allocUnsafe(toRead);
167
- fs5.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
167
+ fs6.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
168
168
  if (self._destroyed)
169
169
  return cb();
170
170
  if (err) {
@@ -221,7 +221,7 @@ var require_fd_slicer = __commonJS((exports) => {
221
221
  self.context.pend.go(function(cb) {
222
222
  if (self._destroyed)
223
223
  return cb();
224
- fs5.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
224
+ fs6.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
225
225
  if (err2) {
226
226
  self.destroy();
227
227
  cb();
@@ -5372,6 +5372,45 @@ var defaultFileExtensionMap = {
5372
5372
  };
5373
5373
 
5374
5374
  // src/frame-range.ts
5375
+ var isMultipleFrameRanges = (frameRange) => {
5376
+ return Array.isArray(frameRange) && Array.isArray(frameRange[0]);
5377
+ };
5378
+ var validateFrameRangeTuple = (frameRange) => {
5379
+ if (frameRange.length !== 2) {
5380
+ throw new TypeError("Frame range must be a tuple, got an array with length " + frameRange.length);
5381
+ }
5382
+ const [first, second] = frameRange;
5383
+ if (typeof first !== "number") {
5384
+ throw new Error(`The first value of frame range must be a number, but got ${typeof first} (${JSON.stringify(first)})`);
5385
+ }
5386
+ if (!Number.isFinite(first)) {
5387
+ throw new TypeError("The first value of frame range must be finite, but got " + first);
5388
+ }
5389
+ if (!Number.isInteger(first)) {
5390
+ throw new Error(`The first value of frame range must be an integer, but got a float (${first})`);
5391
+ }
5392
+ if (first < 0) {
5393
+ throw new Error(`The first value of frame range must be non-negative, but got ${first}`);
5394
+ }
5395
+ if (second === null) {
5396
+ return;
5397
+ }
5398
+ if (typeof second !== "number") {
5399
+ throw new Error(`The second value of frame range must be a number or null, but got ${typeof second} (${JSON.stringify(second)})`);
5400
+ }
5401
+ if (!Number.isFinite(second)) {
5402
+ throw new TypeError("The second value of frame range must be finite, but got " + second);
5403
+ }
5404
+ if (!Number.isInteger(second)) {
5405
+ throw new Error(`The second value of frame range must be an integer, but got a float (${second})`);
5406
+ }
5407
+ if (second < 0) {
5408
+ throw new Error(`The second value of frame range must be non-negative, but got ${second}`);
5409
+ }
5410
+ if (second < first) {
5411
+ throw new Error("The second value of frame range must be not smaller than the first one, but got " + frameRange.join("-"));
5412
+ }
5413
+ };
5375
5414
  var validateFrameRange = (frameRange) => {
5376
5415
  if (frameRange === null) {
5377
5416
  return;
@@ -5388,43 +5427,27 @@ var validateFrameRange = (frameRange) => {
5388
5427
  }
5389
5428
  return;
5390
5429
  }
5391
- if (Array.isArray(frameRange)) {
5392
- if (frameRange.length !== 2) {
5393
- throw new TypeError("Frame range must be a tuple, got an array with length " + frameRange.length);
5394
- }
5395
- const [first, second] = frameRange;
5396
- if (typeof first !== "number") {
5397
- throw new Error(`The first value of frame range must be a number, but got ${typeof first} (${JSON.stringify(first)})`);
5398
- }
5399
- if (!Number.isFinite(first)) {
5400
- throw new TypeError("The first value of frame range must be finite, but got " + first);
5401
- }
5402
- if (!Number.isInteger(first)) {
5403
- throw new Error(`The first value of frame range must be an integer, but got a float (${first})`);
5404
- }
5405
- if (first < 0) {
5406
- throw new Error(`The first value of frame range must be non-negative, but got ${first}`);
5407
- }
5408
- if (second === null) {
5409
- return;
5430
+ if (isMultipleFrameRanges(frameRange)) {
5431
+ if (frameRange.length === 0) {
5432
+ throw new TypeError("Multiple frame ranges must not be empty.");
5410
5433
  }
5411
- if (typeof second !== "number") {
5412
- throw new Error(`The second value of frame range must be a number or null, but got ${typeof second} (${JSON.stringify(second)})`);
5413
- }
5414
- if (!Number.isFinite(second)) {
5415
- throw new TypeError("The second value of frame range must be finite, but got " + second);
5416
- }
5417
- if (!Number.isInteger(second)) {
5418
- throw new Error(`The second value of frame range must be an integer, but got a float (${second})`);
5419
- }
5420
- if (second < 0) {
5421
- throw new Error(`The second value of frame range must be non-negative, but got ${second}`);
5422
- }
5423
- if (second < first) {
5424
- throw new Error("The second value of frame range must be not smaller than the first one, but got " + frameRange.join("-"));
5434
+ for (let index = 0;index < frameRange.length; index++) {
5435
+ const range = frameRange[index];
5436
+ validateFrameRangeTuple(range);
5437
+ if (range[1] === null && index !== frameRange.length - 1) {
5438
+ throw new Error("An open-ended frame range must be the last range.");
5439
+ }
5440
+ const previous = frameRange[index - 1];
5441
+ if (previous && previous[1] !== null && range[0] <= previous[1]) {
5442
+ throw new Error(`Frame ranges must be ordered and non-overlapping, but got ${previous.join("-")} followed by ${range.join("-")}.`);
5443
+ }
5425
5444
  }
5426
5445
  return;
5427
5446
  }
5447
+ if (Array.isArray(frameRange)) {
5448
+ validateFrameRangeTuple(frameRange);
5449
+ return;
5450
+ }
5428
5451
  throw new TypeError("Frame range must be a number or a tuple of numbers, but got object of type " + typeof frameRange);
5429
5452
  };
5430
5453
 
@@ -5471,8 +5494,8 @@ var defaultOnLog = ({ logLevel, tag, previewString }) => {
5471
5494
  };
5472
5495
 
5473
5496
  // src/open-browser.ts
5474
- import fs11 from "node:fs";
5475
- import os6 from "node:os";
5497
+ import fs12 from "node:fs";
5498
+ import os7 from "node:os";
5476
5499
  import path10 from "node:path";
5477
5500
  import { NoReactInternals as NoReactInternals7 } from "remotion/no-react";
5478
5501
 
@@ -5507,14 +5530,121 @@ var launchChrome = async ({
5507
5530
  };
5508
5531
 
5509
5532
  // src/ensure-browser.ts
5510
- import fs9 from "fs";
5533
+ import fs10 from "fs";
5511
5534
 
5512
5535
  // src/browser/BrowserFetcher.ts
5513
- import * as fs8 from "node:fs";
5514
- import * as os4 from "node:os";
5536
+ import * as fs9 from "node:fs";
5537
+ import * as os5 from "node:os";
5515
5538
  import * as path9 from "node:path";
5516
5539
  import { promisify as promisify2 } from "node:util";
5517
5540
 
5541
+ // src/browser/browser-download-lock.ts
5542
+ import { randomUUID } from "node:crypto";
5543
+ import * as fs5 from "node:fs";
5544
+ import os3 from "node:os";
5545
+ var RETRY_INTERVAL_IN_MS = 100;
5546
+ var STALE_LOCK_IN_MS = 30000;
5547
+ var HEARTBEAT_INTERVAL_IN_MS = 5000;
5548
+ var wait = (timeoutInMilliseconds) => {
5549
+ return new Promise((resolve) => {
5550
+ setTimeout(resolve, timeoutInMilliseconds);
5551
+ });
5552
+ };
5553
+ var isErrorWithCode = (error, code) => {
5554
+ return error instanceof Error && "code" in error && error.code === code;
5555
+ };
5556
+ var readLock = async (lockPath) => {
5557
+ try {
5558
+ const parsed = JSON.parse(await fs5.promises.readFile(lockPath, "utf8"));
5559
+ if (typeof parsed.hostname !== "string" || typeof parsed.pid !== "number" || typeof parsed.token !== "string") {
5560
+ return null;
5561
+ }
5562
+ return parsed;
5563
+ } catch {
5564
+ return null;
5565
+ }
5566
+ };
5567
+ var isProcessRunning = (pid) => {
5568
+ try {
5569
+ process.kill(pid, 0);
5570
+ return true;
5571
+ } catch (error) {
5572
+ return !isErrorWithCode(error, "ESRCH");
5573
+ }
5574
+ };
5575
+ var removeStaleLock = async (lockPath) => {
5576
+ let stats;
5577
+ try {
5578
+ stats = await fs5.promises.stat(lockPath);
5579
+ } catch (error) {
5580
+ if (isErrorWithCode(error, "ENOENT")) {
5581
+ return;
5582
+ }
5583
+ throw error;
5584
+ }
5585
+ const lock = await readLock(lockPath);
5586
+ if (lock?.hostname === os3.hostname()) {
5587
+ if (isProcessRunning(lock.pid)) {
5588
+ return;
5589
+ }
5590
+ } else if (Date.now() - stats.mtimeMs < STALE_LOCK_IN_MS) {
5591
+ return;
5592
+ }
5593
+ try {
5594
+ await fs5.promises.unlink(lockPath);
5595
+ } catch (error) {
5596
+ if (!isErrorWithCode(error, "ENOENT")) {
5597
+ throw error;
5598
+ }
5599
+ }
5600
+ };
5601
+ var acquireBrowserDownloadLock = async (lockPath) => {
5602
+ for (;; ) {
5603
+ const token = randomUUID();
5604
+ let lockFile;
5605
+ try {
5606
+ lockFile = await fs5.promises.open(lockPath, "wx");
5607
+ } catch (error) {
5608
+ if (!isErrorWithCode(error, "EEXIST")) {
5609
+ throw error;
5610
+ }
5611
+ await removeStaleLock(lockPath);
5612
+ await wait(RETRY_INTERVAL_IN_MS);
5613
+ continue;
5614
+ }
5615
+ try {
5616
+ await lockFile.writeFile(JSON.stringify({
5617
+ hostname: os3.hostname(),
5618
+ pid: process.pid,
5619
+ token
5620
+ }));
5621
+ } catch (error) {
5622
+ await lockFile.close();
5623
+ await fs5.promises.rm(lockPath, { force: true });
5624
+ throw error;
5625
+ }
5626
+ const heartbeat = setInterval(async () => {
5627
+ const currentLock = await readLock(lockPath);
5628
+ if (currentLock?.token !== token) {
5629
+ return;
5630
+ }
5631
+ const now = new Date;
5632
+ try {
5633
+ await fs5.promises.utimes(lockPath, now, now);
5634
+ } catch {}
5635
+ }, HEARTBEAT_INTERVAL_IN_MS);
5636
+ heartbeat.unref();
5637
+ return async () => {
5638
+ clearInterval(heartbeat);
5639
+ const currentLock = await readLock(lockPath);
5640
+ await lockFile.close();
5641
+ if (currentLock?.token === token) {
5642
+ await fs5.promises.rm(lockPath, { force: true });
5643
+ }
5644
+ };
5645
+ }
5646
+ };
5647
+
5518
5648
  // src/browser/extract-zip-archive.ts
5519
5649
  import { createWriteStream as createWriteStream2 } from "node:fs";
5520
5650
  import { mkdir, realpath, symlink } from "node:fs/promises";
@@ -5524,7 +5654,7 @@ import { pipeline } from "node:stream/promises";
5524
5654
  import { promisify } from "node:util";
5525
5655
 
5526
5656
  // vendor/yauzl-patched/index.js
5527
- var fs5 = __require("fs");
5657
+ var fs6 = __require("fs");
5528
5658
  var zlib = __require("zlib");
5529
5659
  var fd_slicer = require_fd_slicer();
5530
5660
  var crc32 = require_buffer_crc32();
@@ -5553,12 +5683,12 @@ function open(path7, options2, callback) {
5553
5683
  options2.strictFileNames = false;
5554
5684
  if (callback == null)
5555
5685
  callback = defaultCallback;
5556
- fs5.open(path7, "r", function(err, fd) {
5686
+ fs6.open(path7, "r", function(err, fd) {
5557
5687
  if (err)
5558
5688
  return callback(err);
5559
5689
  fromFd(fd, options2, function(err2, zipfile) {
5560
5690
  if (err2)
5561
- fs5.close(fd, defaultCallback);
5691
+ fs6.close(fd, defaultCallback);
5562
5692
  callback(err2, zipfile);
5563
5693
  });
5564
5694
  });
@@ -5582,7 +5712,7 @@ function fromFd(fd, options2, callback) {
5582
5712
  options2.strictFileNames = false;
5583
5713
  if (callback == null)
5584
5714
  callback = defaultCallback;
5585
- fs5.fstat(fd, function(err, stats) {
5715
+ fs6.fstat(fd, function(err, stats) {
5586
5716
  if (err)
5587
5717
  return callback(err);
5588
5718
  var reader = fd_slicer.createFromFd(fd, { autoClose: true });
@@ -6409,16 +6539,16 @@ var extractZipArchive = async (archivePath, dir) => {
6409
6539
  };
6410
6540
 
6411
6541
  // src/browser/get-chrome-download-url.ts
6412
- import * as fs6 from "node:fs";
6413
- import * as os3 from "node:os";
6542
+ import * as fs7 from "node:fs";
6543
+ import * as os4 from "node:os";
6414
6544
  var TESTED_VERSION = "149.0.7790.0";
6415
6545
  var PLAYWRIGHT_VERSION = "1421";
6416
6546
  var isAmazonLinux2023 = () => {
6417
- if (os3.platform() !== "linux") {
6547
+ if (os4.platform() !== "linux") {
6418
6548
  return false;
6419
6549
  }
6420
6550
  try {
6421
- const osRelease = fs6.readFileSync("/etc/os-release", "utf-8");
6551
+ const osRelease = fs7.readFileSync("/etc/os-release", "utf-8");
6422
6552
  return osRelease.includes("Amazon Linux") && osRelease.includes('VERSION="2023"');
6423
6553
  } catch {
6424
6554
  return false;
@@ -6511,14 +6641,14 @@ var logDownloadUrl = ({
6511
6641
  };
6512
6642
 
6513
6643
  // src/browser/get-download-destination.ts
6514
- import fs7 from "node:fs";
6644
+ import fs8 from "node:fs";
6515
6645
  import path8 from "node:path";
6516
6646
  var getDownloadsCacheDir = () => {
6517
6647
  const cwd = process.cwd();
6518
6648
  let dir = cwd;
6519
6649
  for (;; ) {
6520
6650
  try {
6521
- if (fs7.statSync(path8.join(dir, "package.json")).isFile()) {
6651
+ if (fs8.statSync(path8.join(dir, "package.json")).isFile()) {
6522
6652
  break;
6523
6653
  }
6524
6654
  } catch (e) {}
@@ -6542,22 +6672,22 @@ var getDownloadsCacheDir = () => {
6542
6672
  };
6543
6673
 
6544
6674
  // src/browser/BrowserFetcher.ts
6545
- var mkdirAsync = fs8.promises.mkdir;
6546
- var unlinkAsync = promisify2(fs8.unlink.bind(fs8));
6675
+ var mkdirAsync = fs9.promises.mkdir;
6676
+ var unlinkAsync = promisify2(fs9.unlink.bind(fs9));
6547
6677
  function existsAsync(filePath) {
6548
6678
  return new Promise((resolve2) => {
6549
- fs8.access(filePath, (err) => {
6679
+ fs9.access(filePath, (err) => {
6550
6680
  return resolve2(!err);
6551
6681
  });
6552
6682
  });
6553
6683
  }
6554
6684
  var getPlatform = () => {
6555
- const platform3 = os4.platform();
6685
+ const platform3 = os5.platform();
6556
6686
  switch (platform3) {
6557
6687
  case "darwin":
6558
- return os4.arch() === "arm64" ? "mac-arm64" : "mac-x64";
6688
+ return os5.arch() === "arm64" ? "mac-arm64" : "mac-x64";
6559
6689
  case "linux":
6560
- return os4.arch() === "arm64" ? "linux-arm64" : "linux64";
6690
+ return os5.arch() === "arm64" ? "linux-arm64" : "linux64";
6561
6691
  case "win32":
6562
6692
  return "win64";
6563
6693
  default:
@@ -6581,14 +6711,14 @@ var getExpectedVersion = (version, _chromeMode) => {
6581
6711
  var readVersionFile = (chromeMode) => {
6582
6712
  const versionFilePath = getVersionFilePath(chromeMode);
6583
6713
  try {
6584
- return fs8.readFileSync(versionFilePath, "utf-8").trim();
6714
+ return fs9.readFileSync(versionFilePath, "utf-8").trim();
6585
6715
  } catch {
6586
6716
  return null;
6587
6717
  }
6588
6718
  };
6589
6719
  var writeVersionFile = (chromeMode, version) => {
6590
6720
  const versionFilePath = getVersionFilePath(chromeMode);
6591
- fs8.writeFileSync(versionFilePath, version);
6721
+ fs9.writeFileSync(versionFilePath, version);
6592
6722
  };
6593
6723
  var downloadBrowser = async ({
6594
6724
  logLevel,
@@ -6612,71 +6742,82 @@ var downloadBrowser = async ({
6612
6742
  if (installedVersion === expectedVersion) {
6613
6743
  return getRevisionInfo(chromeMode);
6614
6744
  }
6615
- fs8.rmSync(outputPath, { recursive: true, force: true });
6616
6745
  }
6617
6746
  if (!await existsAsync(downloadsFolder)) {
6618
6747
  await mkdirAsync(downloadsFolder, {
6619
6748
  recursive: true
6620
6749
  });
6621
6750
  }
6622
- if (os4.platform() !== "darwin" && os4.platform() !== "linux" && os4.arch() === "arm64") {
6751
+ if (os5.platform() !== "darwin" && os5.platform() !== "linux" && os5.arch() === "arm64") {
6623
6752
  throw new Error([
6624
6753
  "Chrome Headless Shell is not available for Windows for arm64 architecture."
6625
6754
  ].join(`
6626
6755
  `));
6627
6756
  }
6628
- logDownloadUrl({ url: downloadURL, logLevel, indent });
6757
+ const releaseLock = await acquireBrowserDownloadLock(path9.join(downloadsFolder, "download.lock"));
6629
6758
  try {
6630
- await downloadFile({
6631
- url: downloadURL,
6632
- to: () => archivePath,
6633
- onProgress: (progress) => {
6634
- if (progress.totalSize === null || progress.percent === null) {
6635
- throw new Error("Expected totalSize and percent to be defined");
6636
- }
6637
- onProgress({
6638
- downloadedBytes: progress.downloaded,
6639
- totalSizeInBytes: progress.totalSize,
6640
- percent: progress.percent,
6641
- alreadyAvailable: false
6642
- });
6643
- },
6644
- indent,
6645
- logLevel,
6646
- abortSignal: new AbortController().signal
6647
- });
6648
- await extractZipArchive(archivePath, outputPath);
6649
- const possibleSubdirs = [
6650
- "chrome-linux",
6651
- "chrome-headless-shell-linux64",
6652
- "chromium-headless-shell-amazon-linux2023-arm64",
6653
- "chromium-headless-shell-amazon-linux2023-x64"
6654
- ];
6655
- for (const subdir of possibleSubdirs) {
6656
- const chromeLinuxFolder = path9.join(outputPath, subdir);
6657
- const chromePath = path9.join(chromeLinuxFolder, "chrome");
6658
- if (fs8.existsSync(chromePath)) {
6659
- const chromeHeadlessShellPath = path9.join(chromeLinuxFolder, "chrome-headless-shell");
6660
- fs8.renameSync(chromePath, chromeHeadlessShellPath);
6759
+ if (await existsAsync(outputPath)) {
6760
+ const installedVersion = readVersionFile(chromeMode);
6761
+ if (installedVersion === expectedVersion) {
6762
+ return getRevisionInfo(chromeMode);
6661
6763
  }
6662
- if (fs8.existsSync(chromeLinuxFolder)) {
6663
- const targetFolder = path9.join(outputPath, "chrome-headless-shell-" + platform3);
6664
- if (chromeLinuxFolder !== targetFolder) {
6665
- fs8.renameSync(chromeLinuxFolder, targetFolder);
6764
+ fs9.rmSync(outputPath, { recursive: true, force: true });
6765
+ }
6766
+ logDownloadUrl({ url: downloadURL, logLevel, indent });
6767
+ try {
6768
+ await downloadFile({
6769
+ url: downloadURL,
6770
+ to: () => archivePath,
6771
+ onProgress: (progress) => {
6772
+ if (progress.totalSize === null || progress.percent === null) {
6773
+ throw new Error("Expected totalSize and percent to be defined");
6774
+ }
6775
+ onProgress({
6776
+ downloadedBytes: progress.downloaded,
6777
+ totalSizeInBytes: progress.totalSize,
6778
+ percent: progress.percent,
6779
+ alreadyAvailable: false
6780
+ });
6781
+ },
6782
+ indent,
6783
+ logLevel,
6784
+ abortSignal: new AbortController().signal
6785
+ });
6786
+ await extractZipArchive(archivePath, outputPath);
6787
+ const possibleSubdirs = [
6788
+ "chrome-linux",
6789
+ "chrome-headless-shell-linux64",
6790
+ "chromium-headless-shell-amazon-linux2023-arm64",
6791
+ "chromium-headless-shell-amazon-linux2023-x64"
6792
+ ];
6793
+ for (const subdir of possibleSubdirs) {
6794
+ const chromeLinuxFolder = path9.join(outputPath, subdir);
6795
+ const chromePath = path9.join(chromeLinuxFolder, "chrome");
6796
+ if (fs9.existsSync(chromePath)) {
6797
+ const chromeHeadlessShellPath = path9.join(chromeLinuxFolder, "chrome-headless-shell");
6798
+ fs9.renameSync(chromePath, chromeHeadlessShellPath);
6666
6799
  }
6800
+ if (fs9.existsSync(chromeLinuxFolder)) {
6801
+ const targetFolder = path9.join(outputPath, "chrome-headless-shell-" + platform3);
6802
+ if (chromeLinuxFolder !== targetFolder) {
6803
+ fs9.renameSync(chromeLinuxFolder, targetFolder);
6804
+ }
6805
+ }
6806
+ }
6807
+ } catch (err) {
6808
+ return Promise.reject(err);
6809
+ } finally {
6810
+ if (await existsAsync(archivePath)) {
6811
+ await unlinkAsync(archivePath);
6667
6812
  }
6668
6813
  }
6669
- } catch (err) {
6670
- return Promise.reject(err);
6814
+ writeVersionFile(chromeMode, expectedVersion);
6815
+ const revisionInfo = getRevisionInfo(chromeMode);
6816
+ makeFileExecutableIfItIsNot(revisionInfo.executablePath);
6817
+ return revisionInfo;
6671
6818
  } finally {
6672
- if (await existsAsync(archivePath)) {
6673
- await unlinkAsync(archivePath);
6674
- }
6819
+ await releaseLock();
6675
6820
  }
6676
- writeVersionFile(chromeMode, expectedVersion);
6677
- const revisionInfo = getRevisionInfo(chromeMode);
6678
- makeFileExecutableIfItIsNot(revisionInfo.executablePath);
6679
- return revisionInfo;
6680
6821
  };
6681
6822
  var getFolderPath = (downloadsFolder, platform3) => {
6682
6823
  return path9.resolve(downloadsFolder, platform3);
@@ -6708,7 +6849,7 @@ var getRevisionInfo = (chromeMode) => {
6708
6849
  const platform3 = getPlatform();
6709
6850
  const folderPath = getFolderPath(downloadsFolder, platform3);
6710
6851
  const url = getChromeDownloadUrl({ platform: platform3, version: null, chromeMode });
6711
- const local = fs8.existsSync(folderPath);
6852
+ const local = fs9.existsSync(folderPath);
6712
6853
  return {
6713
6854
  executablePath,
6714
6855
  folderPath,
@@ -6747,13 +6888,13 @@ var getBrowserStatus = ({
6747
6888
  chromeMode
6748
6889
  }) => {
6749
6890
  if (browserExecutable) {
6750
- if (!fs9.existsSync(browserExecutable)) {
6891
+ if (!fs10.existsSync(browserExecutable)) {
6751
6892
  throw new Error(`"browserExecutable" was specified as '${browserExecutable}' but the path doesn't exist. Pass "null" for "browserExecutable" to download a browser automatically.`);
6752
6893
  }
6753
6894
  return { path: browserExecutable, type: "user-defined-path" };
6754
6895
  }
6755
6896
  const revision = getRevisionInfo(chromeMode);
6756
- if (revision.local && fs9.existsSync(revision.executablePath)) {
6897
+ if (revision.local && fs10.existsSync(revision.executablePath)) {
6757
6898
  const actualVersion = readVersionFile(chromeMode);
6758
6899
  if (actualVersion === TESTED_VERSION) {
6759
6900
  return { path: revision.executablePath, type: "local-puppeteer-browser" };
@@ -6779,7 +6920,7 @@ var ensureBrowser = (options2) => {
6779
6920
  };
6780
6921
 
6781
6922
  // src/get-local-browser-executable.ts
6782
- import fs10 from "node:fs";
6923
+ import fs11 from "node:fs";
6783
6924
  var getBrowserStatus2 = ({
6784
6925
  browserExecutablePath,
6785
6926
  indent,
@@ -6787,13 +6928,13 @@ var getBrowserStatus2 = ({
6787
6928
  chromeMode
6788
6929
  }) => {
6789
6930
  if (browserExecutablePath) {
6790
- if (!fs10.existsSync(browserExecutablePath)) {
6931
+ if (!fs11.existsSync(browserExecutablePath)) {
6791
6932
  Log.warn({ indent, logLevel }, `Browser executable was specified as '${browserExecutablePath}' but the path doesn't exist.`);
6792
6933
  }
6793
6934
  return { path: browserExecutablePath, type: "user-defined-path" };
6794
6935
  }
6795
6936
  const revision = getRevisionInfo(chromeMode);
6796
- if (revision.local && fs10.existsSync(revision.executablePath)) {
6937
+ if (revision.local && fs11.existsSync(revision.executablePath)) {
6797
6938
  return { path: revision.executablePath, type: "local-puppeteer-browser" };
6798
6939
  }
6799
6940
  return { type: "no-browser" };
@@ -6818,7 +6959,7 @@ var getLocalBrowserExecutable = ({
6818
6959
 
6819
6960
  // src/get-cpu-count.ts
6820
6961
  import { execSync as execSync3 } from "node:child_process";
6821
- import os5 from "node:os";
6962
+ import os6 from "node:os";
6822
6963
  var nprocCount;
6823
6964
  var getConcurrencyFromNProc = () => {
6824
6965
  if (nprocCount !== undefined) {
@@ -6833,10 +6974,10 @@ var getConcurrencyFromNProc = () => {
6833
6974
  }
6834
6975
  };
6835
6976
  var getNodeCpuCount = () => {
6836
- if (typeof os5.availableParallelism === "function") {
6837
- return os5.availableParallelism();
6977
+ if (typeof os6.availableParallelism === "function") {
6978
+ return os6.availableParallelism();
6838
6979
  }
6839
- return os5.cpus().length;
6980
+ return os6.cpus().length;
6840
6981
  };
6841
6982
  var getCpuCount = () => {
6842
6983
  const node = getNodeCpuCount();
@@ -7101,7 +7242,7 @@ var internalOpenBrowser = async ({
7101
7242
  if (chromiumOptions.userAgent) {
7102
7243
  Log.verbose({ indent, logLevel, tag: "openBrowser()" }, `Using custom user agent: ${chromiumOptions.userAgent}`);
7103
7244
  }
7104
- const userDataDir = await fs11.promises.mkdtemp(path10.join(os6.tmpdir(), "puppeteer_dev_chrome_profile-"));
7245
+ const userDataDir = await fs12.promises.mkdtemp(path10.join(os7.tmpdir(), "puppeteer_dev_chrome_profile-"));
7105
7246
  const browserInstance = await launchChrome({
7106
7247
  executablePath,
7107
7248
  logLevel,
@@ -7278,7 +7419,7 @@ import path20 from "node:path";
7278
7419
  import { NoReactInternals as NoReactInternals9 } from "remotion/no-react";
7279
7420
 
7280
7421
  // src/assets/download-and-map-assets-to-file.ts
7281
- import fs12 from "node:fs";
7422
+ import fs13 from "node:fs";
7282
7423
  import path12, { extname as extname2 } from "node:path";
7283
7424
  import { random } from "remotion/no-react";
7284
7425
 
@@ -16314,7 +16455,7 @@ var downloadAsset = async ({
16314
16455
  const { downloadDir } = downloadMap;
16315
16456
  if (downloadMap.hasBeenDownloadedMap[src]?.[downloadDir]) {
16316
16457
  const claimedDownloadLocation = downloadMap.hasBeenDownloadedMap[src]?.[downloadDir];
16317
- if (fs12.existsSync(claimedDownloadLocation)) {
16458
+ if (fs13.existsSync(claimedDownloadLocation)) {
16318
16459
  return claimedDownloadLocation;
16319
16460
  }
16320
16461
  downloadMap.hasBeenDownloadedMap[src][downloadDir] = null;
@@ -16353,7 +16494,7 @@ var downloadAsset = async ({
16353
16494
  });
16354
16495
  ensureOutputDirectory(output);
16355
16496
  const buff = Buffer.from(assetData, encoding);
16356
- await fs12.promises.writeFile(output, buff);
16497
+ await fs13.promises.writeFile(output, buff);
16357
16498
  notifyAssetIsDownloaded({ src, downloadMap, downloadDir, to: output });
16358
16499
  return output;
16359
16500
  }
@@ -16939,8 +17080,8 @@ class OffthreadVideoServerEmitter {
16939
17080
  }
16940
17081
 
16941
17082
  // src/tmp-dir.ts
16942
- import fs13, { mkdirSync } from "node:fs";
16943
- import os7 from "node:os";
17083
+ import fs14, { mkdirSync } from "node:fs";
17084
+ import os8 from "node:os";
16944
17085
  import path14 from "node:path";
16945
17086
  var alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
16946
17087
  var randomHash = () => {
@@ -16949,9 +17090,9 @@ var randomHash = () => {
16949
17090
  }).join("");
16950
17091
  };
16951
17092
  var tmpDir = (str) => {
16952
- const newDir = path14.join(os7.tmpdir(), str + randomHash());
16953
- if (fs13.existsSync(newDir)) {
16954
- fs13.rmSync(newDir, {
17093
+ const newDir = path14.join(os8.tmpdir(), str + randomHash());
17094
+ if (fs14.existsSync(newDir)) {
17095
+ fs14.rmSync(newDir, {
16955
17096
  recursive: true,
16956
17097
  force: true
16957
17098
  });
@@ -16961,7 +17102,7 @@ var tmpDir = (str) => {
16961
17102
  };
16962
17103
 
16963
17104
  // src/assets/inline-audio-mixing.ts
16964
- import fs14, { writeSync } from "node:fs";
17105
+ import fs15, { writeSync } from "node:fs";
16965
17106
  import path15 from "node:path";
16966
17107
 
16967
17108
  // src/assets/apply-tone-frequency.ts
@@ -17032,7 +17173,7 @@ var makeInlineAudioMixing = (dir, sampleRate) => {
17032
17173
  const cleanup = () => {
17033
17174
  for (const fileName of Object.keys(openFiles)) {
17034
17175
  try {
17035
- fs14.closeSync(openFiles[fileName]);
17176
+ fs15.closeSync(openFiles[fileName]);
17036
17177
  delete openFiles[fileName];
17037
17178
  } catch {}
17038
17179
  }
@@ -17053,7 +17194,7 @@ var makeInlineAudioMixing = (dir, sampleRate) => {
17053
17194
  }) => {
17054
17195
  const filePath = getFilePath(asset);
17055
17196
  if (!openFiles[filePath]) {
17056
- openFiles[filePath] = fs14.openSync(filePath, "w");
17197
+ openFiles[filePath] = fs15.openSync(filePath, "w");
17057
17198
  }
17058
17199
  if (writtenHeaders[filePath]) {
17059
17200
  return;
@@ -17100,9 +17241,9 @@ var makeInlineAudioMixing = (dir, sampleRate) => {
17100
17241
  sampleRate: finishSampleRate
17101
17242
  });
17102
17243
  try {
17103
- fs14.closeSync(openFiles[fileName]);
17244
+ fs15.closeSync(openFiles[fileName]);
17104
17245
  } catch {}
17105
- fs14.renameSync(tmpFile, fileName);
17246
+ fs15.renameSync(tmpFile, fileName);
17106
17247
  }
17107
17248
  };
17108
17249
  const addAsset = ({
@@ -17396,13 +17537,13 @@ var makeRange = (from, to) => {
17396
17537
  };
17397
17538
 
17398
17539
  // src/port-config.ts
17399
- import os8 from "os";
17540
+ import os9 from "os";
17400
17541
  var cached = null;
17401
17542
  var getPortConfig = (preferIpv4) => {
17402
17543
  if (cached) {
17403
17544
  return cached;
17404
17545
  }
17405
- const networkInterfaces = os8.networkInterfaces();
17546
+ const networkInterfaces = os9.networkInterfaces();
17406
17547
  const flattened = flattenNetworkInterfaces(networkInterfaces);
17407
17548
  const host = getHostToBind(flattened, preferIpv4);
17408
17549
  const hostsToTry = getHostsToTry(flattened);
@@ -17459,7 +17600,7 @@ var hasIpv4LoopbackAddress = (flattened) => {
17459
17600
  };
17460
17601
 
17461
17602
  // src/serve-handler/index.ts
17462
- import { createReadStream, promises as promises2 } from "node:fs";
17603
+ import { createReadStream, promises as promises3 } from "node:fs";
17463
17604
  import path19 from "node:path";
17464
17605
 
17465
17606
  // src/serve-handler/is-path-inside.ts
@@ -17553,7 +17694,7 @@ var findRelated = async (current, relativePath) => {
17553
17694
  const related = possible[index];
17554
17695
  const absolutePath = path19.join(current, related);
17555
17696
  try {
17556
- stats = await promises2.lstat(absolutePath);
17697
+ stats = await promises3.lstat(absolutePath);
17557
17698
  } catch (err) {
17558
17699
  if (err.code !== "ENOENT" && err.code !== "ENOTDIR") {
17559
17700
  throw err;
@@ -17609,7 +17750,7 @@ var serveHandler = async (request, response, config) => {
17609
17750
  let stats = null;
17610
17751
  if (path19.extname(relativePath) !== "") {
17611
17752
  try {
17612
- stats = await promises2.lstat(absolutePath);
17753
+ stats = await promises3.lstat(absolutePath);
17613
17754
  } catch (err) {
17614
17755
  if (err.code !== "ENOENT" && err.code !== "ENOTDIR") {
17615
17756
  return internalError(absolutePath, response);
@@ -17629,7 +17770,7 @@ var serveHandler = async (request, response, config) => {
17629
17770
  }
17630
17771
  }
17631
17772
  try {
17632
- stats = await promises2.lstat(absolutePath);
17773
+ stats = await promises3.lstat(absolutePath);
17633
17774
  } catch (err) {
17634
17775
  if (err.code !== "ENOENT" && err.code !== "ENOTDIR") {
17635
17776
  return internalError(absolutePath, response);
@@ -18901,11 +19042,12 @@ var getFramesToRender = (frameRange, everyNthFrame) => {
18901
19042
  if (everyNthFrame === 0) {
18902
19043
  throw new Error("everyNthFrame cannot be 0");
18903
19044
  }
18904
- return new Array(frameRange[1] - frameRange[0] + 1).fill(true).map((_, index) => {
18905
- return index + frameRange[0];
19045
+ const ranges = Array.isArray(frameRange[0]) ? frameRange : [frameRange];
19046
+ return ranges.flatMap((range) => new Array(range[1] - range[0] + 1).fill(true).map((_, index) => {
19047
+ return index + range[0];
18906
19048
  }).filter((frame) => {
18907
- return (frame - frameRange[0]) % everyNthFrame === 0;
18908
- });
19049
+ return (frame - range[0]) % everyNthFrame === 0;
19050
+ }));
18909
19051
  };
18910
19052
 
18911
19053
  // src/get-extension-from-codec.ts
@@ -19080,10 +19222,7 @@ var getExtensionOfFilename = (filename) => {
19080
19222
  };
19081
19223
 
19082
19224
  // src/get-frame-to-render.ts
19083
- var getRealFrameRange = (durationInFrames, frameRange) => {
19084
- if (frameRange === null) {
19085
- return [0, durationInFrames - 1];
19086
- }
19225
+ var resolveFrameRange = (durationInFrames, frameRange) => {
19087
19226
  if (typeof frameRange === "number") {
19088
19227
  if (frameRange < 0 || frameRange >= durationInFrames) {
19089
19228
  throw new Error(`Frame number is out of range, must be between 0 and ${durationInFrames - 1} but got ${frameRange}`);
@@ -19099,6 +19238,23 @@ var getRealFrameRange = (durationInFrames, frameRange) => {
19099
19238
  }
19100
19239
  return resolved;
19101
19240
  };
19241
+ var getRealFrameRanges = (durationInFrames, frameRange) => {
19242
+ if (frameRange === null) {
19243
+ return [[0, durationInFrames - 1]];
19244
+ }
19245
+ validateFrameRange(frameRange);
19246
+ if (isMultipleFrameRanges(frameRange)) {
19247
+ return frameRange.map((range) => resolveFrameRange(durationInFrames, range));
19248
+ }
19249
+ return [resolveFrameRange(durationInFrames, frameRange)];
19250
+ };
19251
+ var getRealFrameRange = (durationInFrames, frameRange) => {
19252
+ const ranges = getRealFrameRanges(durationInFrames, frameRange);
19253
+ if (ranges.length !== 1) {
19254
+ throw new Error("Expected a single frame range, but received multiple frame ranges.");
19255
+ }
19256
+ return ranges[0];
19257
+ };
19102
19258
 
19103
19259
  // src/image-format.ts
19104
19260
  var validVideoImageFormats = ["png", "jpeg", "none"];
@@ -19221,7 +19377,7 @@ var validateSelectedPixelFormatAndCodecCombination = (pixelFormat, codec) => {
19221
19377
  };
19222
19378
 
19223
19379
  // src/render-frames.ts
19224
- import fs16 from "node:fs";
19380
+ import fs17 from "node:fs";
19225
19381
  import path22 from "node:path";
19226
19382
  import { NoReactInternals as NoReactInternals14 } from "remotion/no-react";
19227
19383
 
@@ -20154,7 +20310,7 @@ var onlyInlineAudio = (assets) => {
20154
20310
  import * as assert2 from "node:assert";
20155
20311
 
20156
20312
  // src/screenshot-task.ts
20157
- import fs15 from "node:fs";
20313
+ import fs16 from "node:fs";
20158
20314
  var screenshotTask = async ({
20159
20315
  format: format2,
20160
20316
  height,
@@ -20215,7 +20371,7 @@ var screenshotTask = async ({
20215
20371
  }
20216
20372
  const buffer2 = Buffer.from(result.data, "base64");
20217
20373
  if (path21) {
20218
- await fs15.promises.writeFile(path21, buffer2);
20374
+ await fs16.promises.writeFile(path21, buffer2);
20219
20375
  }
20220
20376
  return buffer2;
20221
20377
  } catch (err) {
@@ -20422,6 +20578,7 @@ var renderFrameWithOptionToReject = async ({
20422
20578
  return compressAsset(previousAudioRenderAssets, asset);
20423
20579
  });
20424
20580
  const inlineAudioAssets = onlyInlineAudio(collectedAssets);
20581
+ const outputFrame = allFramesAndExtraFrames[0] + allFramesAndExtraFrames.indexOf(frame);
20425
20582
  assets.push({
20426
20583
  audioAndVideoAssets: compressedAssets,
20427
20584
  frame,
@@ -20450,7 +20607,7 @@ var renderFrameWithOptionToReject = async ({
20450
20607
  }
20451
20608
  for (const renderAsset of inlineAudioAssets) {
20452
20609
  downloadMap.inlineAudioMixing.addAsset({
20453
- asset: renderAsset,
20610
+ asset: { ...renderAsset, frame: outputFrame },
20454
20611
  fps,
20455
20612
  totalNumberOfFrames: allFramesAndExtraFrames.length,
20456
20613
  firstFrame: allFramesAndExtraFrames[0],
@@ -20810,6 +20967,41 @@ var validateScale = (scale) => {
20810
20967
  }
20811
20968
  };
20812
20969
 
20970
+ // src/validate-selected-frames.ts
20971
+ var validateSelectedFrames = ({
20972
+ frames,
20973
+ durationInFrames
20974
+ }) => {
20975
+ if (!Array.isArray(frames)) {
20976
+ throw new TypeError("The `frames` option must be an array of frame numbers.");
20977
+ }
20978
+ if (frames.length === 0) {
20979
+ throw new TypeError("The `frames` option must contain at least one frame.");
20980
+ }
20981
+ for (const frame of frames) {
20982
+ if (typeof frame !== "number") {
20983
+ throw new TypeError(`The \`frames\` option must contain only numbers, but got ${typeof frame}.`);
20984
+ }
20985
+ if (!Number.isFinite(frame)) {
20986
+ throw new TypeError(`The \`frames\` option must contain only finite numbers, but got ${frame}.`);
20987
+ }
20988
+ if (!Number.isInteger(frame)) {
20989
+ throw new TypeError(`The \`frames\` option must contain only integers, but got ${frame}.`);
20990
+ }
20991
+ if (frame < 0) {
20992
+ throw new TypeError(`The \`frames\` option must contain only non-negative numbers, but got ${frame}.`);
20993
+ }
20994
+ if (durationInFrames !== null && frame >= durationInFrames) {
20995
+ throw new RangeError(`Frame ${frame} is out of range, must be between 0 and ${durationInFrames - 1}.`);
20996
+ }
20997
+ }
20998
+ const uniqueFrames = new Set(frames);
20999
+ if (uniqueFrames.size !== frames.length) {
21000
+ throw new TypeError("The `frames` option must not contain duplicate frames.");
21001
+ }
21002
+ return [...frames].sort((a, b) => a - b);
21003
+ };
21004
+
20813
21005
  // src/render-frames.ts
20814
21006
  var MAX_RETRIES_PER_FRAME = 1;
20815
21007
  var innerRenderFrames = async ({
@@ -20821,6 +21013,8 @@ var innerRenderFrames = async ({
20821
21013
  jpegQuality,
20822
21014
  imageFormat,
20823
21015
  frameRange,
21016
+ frames,
21017
+ outputFramesInSequence,
20824
21018
  onError,
20825
21019
  envVariables,
20826
21020
  onBrowserLog,
@@ -20854,18 +21048,25 @@ var innerRenderFrames = async ({
20854
21048
  sampleRate
20855
21049
  }) => {
20856
21050
  if (outputDir) {
20857
- if (!fs16.existsSync(outputDir)) {
20858
- fs16.mkdirSync(outputDir, {
21051
+ if (!fs17.existsSync(outputDir)) {
21052
+ fs17.mkdirSync(outputDir, {
20859
21053
  recursive: true
20860
21054
  });
20861
21055
  }
20862
21056
  }
20863
21057
  const downloadPromises = [];
20864
- const realFrameRange = getRealFrameRange(composition.durationInFrames, frameRange);
21058
+ const framesToRender = frames ? validateSelectedFrames({
21059
+ frames,
21060
+ durationInFrames: composition.durationInFrames
21061
+ }) : getFramesToRender(getRealFrameRanges(composition.durationInFrames, frameRange), everyNthFrame);
21062
+ const realFrameRange = [
21063
+ framesToRender[0],
21064
+ framesToRender[framesToRender.length - 1]
21065
+ ];
20865
21066
  const {
20866
21067
  extraFramesToCaptureAssetsBackend,
20867
21068
  extraFramesToCaptureAssetsFrontend,
20868
- chunkLengthInSeconds,
21069
+ chunkLengthInSeconds: seamlessChunkLengthInSeconds,
20869
21070
  trimLeftOffset,
20870
21071
  trimRightOffset
20871
21072
  } = getExtraFramesToCapture({
@@ -20875,7 +21076,7 @@ var innerRenderFrames = async ({
20875
21076
  forSeamlessAacConcatenation,
20876
21077
  sampleRate
20877
21078
  });
20878
- const framesToRender = getFramesToRender(realFrameRange, everyNthFrame);
21079
+ const chunkLengthInSeconds = forSeamlessAacConcatenation ? seamlessChunkLengthInSeconds : framesToRender.length / composition.fps;
20879
21080
  const lastFrame = framesToRender[framesToRender.length - 1];
20880
21081
  const concurrencyOrFramesToRender = Math.min(framesToRender.length, resolvedConcurrency);
20881
21082
  const makeNewPage = (frame, pageIndex) => {
@@ -20911,7 +21112,7 @@ var innerRenderFrames = async ({
20911
21112
  const pool = new Pool(puppeteerPages);
20912
21113
  return pool;
20913
21114
  };
20914
- const countType = everyNthFrame === 1 ? "actual-frames" : "from-zero";
21115
+ const countType = outputFramesInSequence ? "from-zero" : everyNthFrame === 1 ? "actual-frames" : "from-zero";
20915
21116
  const filePadLength = getFilePadLength({
20916
21117
  lastFrame,
20917
21118
  totalFrames: framesToRender.length,
@@ -21016,6 +21217,8 @@ var internalRenderFramesRaw = ({
21016
21217
  envVariables,
21017
21218
  everyNthFrame,
21018
21219
  frameRange,
21220
+ frames,
21221
+ outputFramesInSequence,
21019
21222
  imageFormat,
21020
21223
  indent,
21021
21224
  jpegQuality,
@@ -21129,6 +21332,8 @@ var internalRenderFramesRaw = ({
21129
21332
  envVariables,
21130
21333
  everyNthFrame,
21131
21334
  frameRange,
21335
+ frames,
21336
+ outputFramesInSequence,
21132
21337
  imageFormat,
21133
21338
  jpegQuality,
21134
21339
  muted,
@@ -21205,6 +21410,7 @@ var renderFrames = (options2) => {
21205
21410
  envVariables,
21206
21411
  everyNthFrame,
21207
21412
  frameRange,
21413
+ frames,
21208
21414
  imageFormat,
21209
21415
  jpegQuality,
21210
21416
  muted,
@@ -21231,6 +21437,16 @@ var renderFrames = (options2) => {
21231
21437
  if (!composition) {
21232
21438
  throw new Error("No `composition` option has been specified for renderFrames()");
21233
21439
  }
21440
+ if (frames !== undefined && frameRange !== undefined && frameRange !== null) {
21441
+ throw new Error("The `frames` and `frameRange` options are mutually exclusive.");
21442
+ }
21443
+ if (frames !== undefined && everyNthFrame !== undefined) {
21444
+ throw new Error("The `frames` and `everyNthFrame` options are mutually exclusive.");
21445
+ }
21446
+ const validatedFrames = frames === undefined ? null : validateSelectedFrames({
21447
+ frames,
21448
+ durationInFrames: composition.durationInFrames
21449
+ });
21234
21450
  if (typeof jpegQuality !== "undefined" && imageFormat !== "jpeg") {
21235
21451
  throw new Error("You can only pass the `quality` option if `imageFormat` is 'jpeg'.");
21236
21452
  }
@@ -21248,6 +21464,8 @@ var renderFrames = (options2) => {
21248
21464
  envVariables: envVariables ?? {},
21249
21465
  everyNthFrame: everyNthFrame ?? 1,
21250
21466
  frameRange: frameRange ?? null,
21467
+ frames: validatedFrames,
21468
+ outputFramesInSequence: frameRange !== undefined && frameRange !== null && isMultipleFrameRanges(frameRange),
21251
21469
  imageFormat: imageFormat ?? "jpeg",
21252
21470
  indent,
21253
21471
  jpegQuality: jpegQuality ?? DEFAULT_JPEG_QUALITY,
@@ -21292,8 +21510,8 @@ var renderFrames = (options2) => {
21292
21510
  };
21293
21511
 
21294
21512
  // src/render-media.ts
21295
- import fs18 from "node:fs";
21296
- import os9 from "node:os";
21513
+ import fs19 from "node:fs";
21514
+ import os10 from "node:os";
21297
21515
  import path28 from "node:path";
21298
21516
  import { LicensingInternals } from "@remotion/licensing";
21299
21517
  import { NoReactInternals as NoReactInternals17 } from "remotion/no-react";
@@ -22062,7 +22280,7 @@ var validateSelectedCodecAndProResCombination = ({
22062
22280
  };
22063
22281
 
22064
22282
  // src/stitch-frames-to-video.ts
22065
- import { cpSync as cpSync2, promises as promises3, rmSync as rmSync4 } from "node:fs";
22283
+ import { cpSync as cpSync2, promises as promises4, rmSync as rmSync4 } from "node:fs";
22066
22284
  import path27 from "node:path";
22067
22285
 
22068
22286
  // src/convert-number-of-gif-loops-to-ffmpeg.ts
@@ -22166,12 +22384,18 @@ var indexUncompressedAssets = (renderAssets) => {
22166
22384
  }
22167
22385
  return assetsByReference;
22168
22386
  };
22169
- var calculateAssetPositions = (frames) => {
22387
+ var calculateAssetPositions = (frames, sourceFrames = frames.map((_, index) => index)) => {
22170
22388
  const assets = [];
22171
22389
  const openAssets = new Map;
22172
22390
  const flattened = frames.flat(1);
22173
22391
  const uncompressedAssets = indexUncompressedAssets(flattened);
22174
22392
  for (let frame = 0;frame < frames.length; frame++) {
22393
+ if (frame > 0 && sourceFrames[frame] !== sourceFrames[frame - 1] + 1) {
22394
+ for (const openAsset of openAssets.values()) {
22395
+ openAsset.duration = frame - openAsset.startInVideo;
22396
+ }
22397
+ openAssets.clear();
22398
+ }
22175
22399
  const current = deduplicateAssets(frames[frame]);
22176
22400
  const currentIds = new Set(current.map((a) => a.id));
22177
22401
  for (const [id, openAsset] of openAssets) {
@@ -22331,7 +22555,7 @@ var createFfmpegMergeFilter = ({
22331
22555
  };
22332
22556
 
22333
22557
  // src/ffmpeg-filter-file.ts
22334
- import fs17, { existsSync as existsSync5 } from "node:fs";
22558
+ import fs18, { existsSync as existsSync5 } from "node:fs";
22335
22559
  import path24 from "node:path";
22336
22560
  var makeFfmpegFilterFile = (complexFilter, downloadMap) => {
22337
22561
  if (complexFilter.filter === null) {
@@ -22348,13 +22572,13 @@ var makeFfmpegFilterFileStr = async (complexFilter, downloadMap) => {
22348
22572
  const random2 = Math.random().toString().replace(".", "");
22349
22573
  const filterFile = path24.join(downloadMap.complexFilter, "complex-filter-" + random2 + ".txt");
22350
22574
  if (!existsSync5(downloadMap.complexFilter)) {
22351
- fs17.mkdirSync(downloadMap.complexFilter, { recursive: true });
22575
+ fs18.mkdirSync(downloadMap.complexFilter, { recursive: true });
22352
22576
  }
22353
- await fs17.promises.writeFile(filterFile, complexFilter);
22577
+ await fs18.promises.writeFile(filterFile, complexFilter);
22354
22578
  return {
22355
22579
  file: filterFile,
22356
22580
  cleanup: () => {
22357
- fs17.unlinkSync(filterFile);
22581
+ fs18.unlinkSync(filterFile);
22358
22582
  }
22359
22583
  };
22360
22584
  };
@@ -22907,7 +23131,7 @@ var createAudio = async ({
22907
23131
  binariesDirectory
22908
23132
  });
22909
23133
  markAllAssetsAsDownloaded(downloadMap);
22910
- const assetPositions = calculateAssetPositions(fileUrlAssets);
23134
+ const assetPositions = calculateAssetPositions(fileUrlAssets, assets.map((asset) => asset.frame));
22911
23135
  Log.verbose({ indent, logLevel, tag: "audio" }, "asset positions", JSON.stringify(assetPositions));
22912
23136
  const preprocessProgress = new Array(assetPositions.length).fill(0);
22913
23137
  let mergeProgress = 0;
@@ -23223,7 +23447,7 @@ var innerStitchFramesToVideo = async ({
23223
23447
  deleteDirectory(path27.dirname(audio));
23224
23448
  const file = await new Promise((resolve2, reject) => {
23225
23449
  if (tempFile) {
23226
- promises3.readFile(tempFile).then((f) => {
23450
+ promises4.readFile(tempFile).then((f) => {
23227
23451
  return resolve2(f);
23228
23452
  }).catch((e) => reject(e));
23229
23453
  } else {
@@ -23331,7 +23555,7 @@ var innerStitchFramesToVideo = async ({
23331
23555
  cleanDownloadMap(assetsInfo.downloadMap);
23332
23556
  return resolve2(null);
23333
23557
  }
23334
- promises3.readFile(tempFile).then((f) => {
23558
+ promises4.readFile(tempFile).then((f) => {
23335
23559
  resolve2(f);
23336
23560
  }).catch((e) => {
23337
23561
  reject(e);
@@ -23720,7 +23944,7 @@ var internalRenderMediaRaw = ({
23720
23944
  }
23721
23945
  const imageFormat = isAudioCodec(codec) ? "none" : provisionalImageFormat ?? compositionWithPossibleUnevenDimensions.defaultVideoImageFormat ?? DEFAULT_VIDEO_IMAGE_FORMAT;
23722
23946
  validateSelectedPixelFormatAndImageFormatCombination(pixelFormat, imageFormat);
23723
- const workingDir = fs18.mkdtempSync(path28.join(os9.tmpdir(), "react-motion-render"));
23947
+ const workingDir = fs19.mkdtempSync(path28.join(os10.tmpdir(), "react-motion-render"));
23724
23948
  const preEncodedFileLocation = parallelEncoding ? path28.join(workingDir, "pre-encode." + getFileExtensionFromCodec(codec, audioCodec)) : null;
23725
23949
  if (onCtrlCExit && workingDir) {
23726
23950
  onCtrlCExit(`Delete ${workingDir}`, () => deleteDirectory(workingDir));
@@ -23741,9 +23965,11 @@ var internalRenderMediaRaw = ({
23741
23965
  height: heightEvenDimensions,
23742
23966
  width: widthEvenDimensions
23743
23967
  };
23744
- const realFrameRange = getRealFrameRange(composition.durationInFrames, frameRange);
23745
- const totalFramesToRender = getFramesToRender(realFrameRange, everyNthFrame).length;
23746
- Log.verbose({ indent, logLevel, tag: "renderMedia()" }, `Rendering frames ${realFrameRange.join("-")}`);
23968
+ const realFrameRanges = getRealFrameRanges(composition.durationInFrames, frameRange);
23969
+ const framesToRender = getFramesToRender(realFrameRanges, everyNthFrame);
23970
+ const totalFramesToRender = framesToRender.length;
23971
+ const lastFrameToRender = framesToRender[framesToRender.length - 1];
23972
+ Log.verbose({ indent, logLevel, tag: "renderMedia()" }, `Rendering frames ${realFrameRanges.map((range) => range.join("-")).join(", ")}`);
23747
23973
  const callUpdate = () => {
23748
23974
  const encoded = Math.round(0.8 * encodedFrames + 0.2 * muxedFrames);
23749
23975
  onProgress?.({
@@ -23762,7 +23988,7 @@ var internalRenderMediaRaw = ({
23762
23988
  cancelSignal?.(() => {
23763
23989
  cancelRenderFrames.cancel();
23764
23990
  });
23765
- const { waitForRightTimeOfFrameToBeInserted, setFrameToStitch, waitForFinish } = ensureFramesInOrder(realFrameRange);
23991
+ const { waitForRightTimeOfFrameToBeInserted, setFrameToStitch, waitForFinish } = ensureFramesInOrder(framesToRender);
23766
23992
  const fps = composition.fps / everyNthFrame;
23767
23993
  validateFps(fps, 'in "renderMedia()"', codec === "gif");
23768
23994
  const createPrestitcherIfNecessary = () => {
@@ -23881,6 +24107,8 @@ var internalRenderMediaRaw = ({
23881
24107
  imageFormat,
23882
24108
  jpegQuality,
23883
24109
  frameRange,
24110
+ frames: null,
24111
+ outputFramesInSequence: true,
23884
24112
  puppeteerInstance,
23885
24113
  everyNthFrame,
23886
24114
  onFrameBuffer: parallelEncoding ? async (buffer2, frame) => {
@@ -23898,7 +24126,8 @@ var internalRenderMediaRaw = ({
23898
24126
  }
23899
24127
  stitcherFfmpeg?.stdin?.write(buffer2);
23900
24128
  stopPerfMeasure(id);
23901
- setFrameToStitch(Math.min(realFrameRange[1] + 1, frame + everyNthFrame));
24129
+ const frameIndex = framesToRender.indexOf(frame);
24130
+ setFrameToStitch(framesToRender[frameIndex + 1] ?? lastFrameToRender + 1);
23902
24131
  } : null,
23903
24132
  webpackBundleOrServeUrl: serveUrl,
23904
24133
  onBrowserLog,
@@ -24051,10 +24280,10 @@ var internalRenderMediaRaw = ({
24051
24280
  }
24052
24281
  reject(err);
24053
24282
  }).finally(() => {
24054
- if (preEncodedFileLocation !== null && fs18.existsSync(preEncodedFileLocation)) {
24283
+ if (preEncodedFileLocation !== null && fs19.existsSync(preEncodedFileLocation)) {
24055
24284
  deleteDirectory(path28.dirname(preEncodedFileLocation));
24056
24285
  }
24057
- if (workingDir && fs18.existsSync(workingDir)) {
24286
+ if (workingDir && fs19.existsSync(workingDir)) {
24058
24287
  deleteDirectory(workingDir);
24059
24288
  }
24060
24289
  cleanupServerFn?.(false).catch((err) => {
@@ -24225,7 +24454,7 @@ var renderMedia = ({
24225
24454
  };
24226
24455
 
24227
24456
  // src/render-still.ts
24228
- import fs19, { statSync as statSync2 } from "node:fs";
24457
+ import fs20, { statSync as statSync2 } from "node:fs";
24229
24458
  import path29 from "node:path";
24230
24459
  import { LicensingInternals as LicensingInternals2 } from "@remotion/licensing";
24231
24460
  import { NoReactInternals as NoReactInternals18 } from "remotion/no-react";
@@ -24280,7 +24509,7 @@ var innerRenderStill = async ({
24280
24509
  output = typeof output === "string" ? path29.resolve(process.cwd(), output) : null;
24281
24510
  validateJpegQuality(jpegQuality);
24282
24511
  if (output) {
24283
- if (fs19.existsSync(output)) {
24512
+ if (fs20.existsSync(output)) {
24284
24513
  if (!overwrite) {
24285
24514
  throw new Error(`Cannot render still - "overwrite" option was set to false, but the output destination ${output} already exists.`);
24286
24515
  }
@@ -25154,8 +25383,8 @@ var internalCombineChunks = async ({
25154
25383
  });
25155
25384
  const shouldCreateAudio = resolvedAudioCodec !== null && audioFiles.length > 0;
25156
25385
  const seamlessVideo = canConcatVideoSeamlessly(codec);
25157
- const seamlessAudio = canConcatAudioSeamlessly(resolvedAudioCodec, framesPerChunk);
25158
25386
  const realFrameRange = getRealFrameRange(compositionDurationInFrames, frameRange);
25387
+ const seamlessAudio = canConcatAudioSeamlessly(resolvedAudioCodec, framesPerChunk);
25159
25388
  const numberOfFrames = getFramesToRender(realFrameRange, everyNthFrame).length;
25160
25389
  const videoOutput = shouldCreateVideo ? join6(filelistDir, `video.${getFileExtensionFromCodec(codec, resolvedAudioCodec)}`) : null;
25161
25390
  const audioOutput = shouldCreateAudio ? join6(filelistDir, `audio.${getExtensionFromAudioCodec(resolvedAudioCodec)}`) : null;
@@ -25379,10 +25608,24 @@ var getVideoMetadata = async (videoSource, options2) => {
25379
25608
  };
25380
25609
  // src/options/chrome-mode.tsx
25381
25610
  import { jsx as jsx7, jsxs as jsxs7, Fragment as Fragment7 } from "react/jsx-runtime";
25382
- // src/options/number-of-gif-loops.tsx
25611
+ // src/options/default-editor.tsx
25383
25612
  import { jsx as jsx8, jsxs as jsxs8, Fragment as Fragment8 } from "react/jsx-runtime";
25384
- // src/options/on-browser-download.tsx
25613
+ var defaultEditorIds = [
25614
+ "vscode",
25615
+ "cursor",
25616
+ "windsurf",
25617
+ "zed",
25618
+ "vscodium",
25619
+ "webstorm",
25620
+ "sublime-text"
25621
+ ];
25622
+ var customEditorTargetPathPlaceholder = "%TARGET_PATH%";
25623
+ var customEditorLineNumberPlaceholder = "%LINE_NUMBER%";
25624
+ var customEditorColumnNumberPlaceholder = "%COLUMN_NUMBER%";
25625
+ // src/options/number-of-gif-loops.tsx
25385
25626
  import { jsx as jsx9, jsxs as jsxs9, Fragment as Fragment9 } from "react/jsx-runtime";
25627
+ // src/options/on-browser-download.tsx
25628
+ import { jsx as jsx10, jsxs as jsxs10, Fragment as Fragment10 } from "react/jsx-runtime";
25386
25629
  // src/index.ts
25387
25630
  var RenderInternals = {
25388
25631
  resolveConcurrency,
@@ -25394,6 +25637,7 @@ var RenderInternals = {
25394
25637
  isServeUrl,
25395
25638
  ensureOutputDirectory,
25396
25639
  getRealFrameRange,
25640
+ getRealFrameRanges,
25397
25641
  validatePuppeteerTimeout,
25398
25642
  downloadFile,
25399
25643
  parseStack,
@@ -25416,6 +25660,7 @@ var RenderInternals = {
25416
25660
  validPixelFormats,
25417
25661
  DEFAULT_BROWSER,
25418
25662
  validateFrameRange,
25663
+ validateSelectedFrames,
25419
25664
  DEFAULT_OPENGL_RENDERER,
25420
25665
  validateOpenGlRenderer,
25421
25666
  validCodecs,
@@ -25492,6 +25737,10 @@ export {
25492
25737
  getCompositions,
25493
25738
  extractAudio,
25494
25739
  ensureBrowser,
25740
+ defaultEditorIds,
25741
+ customEditorTargetPathPlaceholder,
25742
+ customEditorLineNumberPlaceholder,
25743
+ customEditorColumnNumberPlaceholder,
25495
25744
  combineChunks,
25496
25745
  RenderInternals,
25497
25746
  ErrorWithStackFrame