# Preflight a Reference Video Budget with ffprobe and Node.js

A reference clip can be short enough for a video remake and still exceed the size budget. It can also fit the byte limit while running a fraction of a second too long. This Node.js script reads an ffprobe report and tells you which limit to review before you queue an upload.

## 1\. Set a budget in bytes and seconds

This example uses a local budget of 90,000,000 bytes and 58 seconds, leaving room beneath the published Video Remake limits. Pick values for your destination and keep the byte count explicit. A file exactly at either local limit passes that check.

## 2\. Export just the metadata you need

With ffprobe available in your terminal, run this command from the directory that contains the reference file. Stop if the probe command fails. Save its successful JSON output as UTF-8 before passing it to Node.js.

```shell
ffprobe -v error -show_entries format=duration,size -of json "reference.mp4" > probe.json
```

The report needs the container duration in seconds and its size in bytes. Keep the decimal strings as emitted. Missing values or placeholders such as N/A need manual investigation before the budget comparison can run.

## 3\. Compare exact units

Save this module as check-budget.mjs. It compares byte counts as integers and converts decimal seconds to integer microseconds. This keeps a value one microsecond over the limit distinct from an exact match.

```javascript
const defaults = { bytes: 90_000_000n, seconds: "58" };

function parseBytes(value) {
  if (typeof value !== "string" || !/^\d+$/.test(value) || BigInt(value) === 0n) {
    throw new Error("Expected a positive size in bytes");
  }
  return BigInt(value);
}

function parseMicroseconds(value) {
  if (typeof value !== "string" || !/^\d+(\.\d{1,6})?$/.test(value)) {
    throw new Error("Expected duration as decimal seconds with up to six places");
  }
  const [whole, fraction = ""] = value.split(".");
  const micros = BigInt(whole) * 1_000_000n + BigInt(fraction.padEnd(6, "0"));
  if (micros === 0n) throw new Error("Expected a positive duration");
  return micros;
}

export function checkBudget(probe, { bytes, seconds } = defaults) {
  if (typeof bytes !== "bigint" || bytes <= 0n) {
    throw new Error("Expected a positive byte limit");
  }
  const size = parseBytes(probe?.format?.size);
  const duration = parseMicroseconds(probe?.format?.duration);
  const limit = parseMicroseconds(seconds);
  const exceeded = [];
  if (size > bytes) exceeded.push("size");
  if (duration > limit) exceeded.push("duration");

  return {
    sizeBytes: size.toString(),
    durationSeconds: probe.format.duration,
    limitBytes: bytes.toString(),
    limitSeconds: seconds,
    exceeded,
  };
}
```

The parser accepts positive decimal seconds with up to six fractional digits. It reports missing or malformed fields as errors. Byte counts are returned as strings so the result can be written as JSON without rounding them.

## 4\. Wire it into a local command

Save this wrapper as preflight.mjs. Run node preflight.mjs probe.json to print the report. Exit code 0 means the file fits this local budget. Exit code 1 means the report names an exceeded limit. Exit code 2 means the input could not be read or checked.

```javascript
import { readFileSync } from "node:fs";
import { checkBudget } from "./check-budget.mjs";

try {
  const filename = process.argv[2];
  if (!filename) throw new Error("Usage: node preflight.mjs probe.json");
  const json = readFileSync(filename, "utf8").replace(/^\uFEFF/, "");
  const result = checkBudget(JSON.parse(json));
  console.log(JSON.stringify(result, null, 2));
  process.exitCode = result.exceeded.length ? 1 : 0;
} catch (error) {
  console.error(error.message);
  process.exitCode = 2;
}
```

For a manual example, give the format object a size of 89000000 and a duration of 58.000001, both as strings. The result lists duration as the only exceeded limit. Changing it to 58.000000 makes the same fixture pass.

## 5\. Test both sides of each boundary

Save the assertions as test.mjs and run node test.mjs. The fixtures cover exact limits, one byte or one microsecond over, missing metadata, and a custom budget.

```javascript
import assert from "node:assert/strict";
import { checkBudget } from "./check-budget.mjs";

const probe = (size, duration) => ({ format: { size, duration } });

const cases = [
  ["89999999", "57.999999", []],
  ["90000000", "58.000000", []],
  ["90000001", "58.000000", ["size"]],
  ["89000000", "58.000001", ["duration"]],
  ["90000001", "58.000001", ["size", "duration"]],
  ["9007199254740993", "58", ["size"]],
];
for (const [size, duration, expected] of cases) {
  assert.deepEqual(checkBudget(probe(size, duration)).exceeded, expected);
}
assert.throws(() => checkBudget({}), /size/);
assert.throws(() => checkBudget(probe("0", "10")), /size/);
assert.throws(() => checkBudget(probe("100", "N/A")), /decimal/);
assert.throws(() => checkBudget(probe("100", "1.0000001")), /decimal/);
assert.throws(() => checkBudget(probe("100", "0.000000")), /positive duration/);
assert.deepEqual(
  checkBudget(probe("1001", "0.1"), { bytes: 1000n, seconds: "0.2" }).exceeded,
  ["size"],
);
console.log("12 checks passed");
```

## 6\. Review the actual clip

Check the destination’s current input rules before upload. The budget report measures container metadata. Verify playback and the intended opening and ending of the clip separately. For a new export, generate a fresh probe report so you compare the version you intend to send.

The JavaScript and wrapper were tested in Node.js 24.20.0 with synthetic JSON. The ffprobe command follows the official documentation. It was not run against a media file for this article.

AI assistance was used in drafting and editing this article.

Source workflow. [How to Use AI Video Remake](https://www.pixmind.io/posts/how-to-remake-video-with-ai)

Technical reference. [ffprobe documentation](https://ffmpeg.org/ffprobe.html)
