Keep verified FFmpeg downloads available in Recordly 2.0 part 8

This commit is contained in:
webadderall committed 2026-11-08 20:31:48 +11:00
commit 375e8dcff6
2 files changed
+193 -4

No files matched your search

+148 -1
View File
@@ -2,10 +2,12 @@ import { createHash } from "node:crypto";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { gzipSync } from "node:zlib";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
APPROVED_FFMPEG_BINARIES,
assertRedistributableFfmpegOutput,
ensureRedistributableFfmpeg,
verifyFfmpegDistribution,
} from "../scripts/ffmpeg-distribution.mjs";
@@ -29,6 +31,8 @@ function fixture(overrides = {}) {
return binary;
}
afterEach(() => {
vi.unstubAllGlobals();
vi.restoreAllMocks();
for (const directory of fixtures.splice(0)) rmSync(directory, { recursive: true, force: true });
});
@@ -98,3 +102,146 @@ describe("FFmpeg redistribution checks", () => {
).toThrow("pinned redistributable sources");
});
});
const directRelease = "https://github.com/eugeneware/ffmpeg-static/releases/download/b6.1.1";
const invalidArtifact = gzipSync(Buffer.from("unapproved executable fixture"));
function installTarget(platform = "linux", arch = "x64") {
const root = mkdtempSync(path.join(os.tmpdir(), "recordly-ffmpeg-download-"));
fixtures.push(root);
return ensureRedistributableFfmpeg({ root, platform, arch });
}
function artifactResponse() {
return new Response(invalidArtifact);
}
describe("FFmpeg release transport", () => {
it("keeps a successful direct download and still rejects unapproved bytes", async () => {
const network = vi.fn().mockResolvedValue(artifactResponse());
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toThrow("pinned SHA-256 check");
expect(network).toHaveBeenCalledOnce();
expect(network.mock.calls[0][0]).toBe(`${directRelease}/ffmpeg-linux-x64.gz`);
expect(network.mock.calls[0][1]).toEqual({
signal: expect.any(AbortSignal),
credentials: "omit",
});
});
it.each([
500, 502, 503, 504,
])("falls back once for retryable HTTP %i without accepting changed bytes", async (status) => {
const failed = new Response("temporarily unavailable", { status });
const cancel = vi.spyOn(failed.body, "cancel");
const network = vi
.fn()
.mockResolvedValueOnce(failed)
.mockResolvedValueOnce(artifactResponse());
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toThrow("pinned SHA-256 check");
expect(cancel).toHaveBeenCalledOnce();
expect(network).toHaveBeenCalledTimes(2);
expect(network.mock.calls[1]).toEqual([
"https://api.github.com/repos/eugeneware/ffmpeg-static/releases/assets/316528626",
{
signal: expect.any(AbortSignal),
credentials: "omit",
headers: { Accept: "application/octet-stream" },
},
]);
});
it.each([
["linux", "arm64", 316528458],
["win32", "x64", 316528798],
])("uses the same pinned release asset for %s/%s", async (platform, arch, assetId) => {
const network = vi
.fn()
.mockResolvedValueOnce(new Response(null, { status: 504 }))
.mockResolvedValueOnce(artifactResponse());
vi.stubGlobal("fetch", network);
await expect(installTarget(platform, arch)).rejects.toThrow("pinned SHA-256 check");
expect(network.mock.calls.map(([url]) => url)).toEqual([
`${directRelease}/ffmpeg-${platform}-${arch}.gz`,
`https://api.github.com/repos/eugeneware/ffmpeg-static/releases/assets/${assetId}`,
]);
});
it.each([
400, 401, 403, 404, 429,
])("fails closed without fallback for permanent HTTP %i", async (status) => {
const network = vi.fn().mockResolvedValue(new Response(null, { status }));
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toThrow(`download failed (${status})`);
expect(network).toHaveBeenCalledOnce();
});
it("recovers a network exception through the exact same-provider asset API", async () => {
const network = vi
.fn()
.mockRejectedValueOnce(new TypeError("fetch failed"))
.mockResolvedValueOnce(artifactResponse());
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toThrow("pinned SHA-256 check");
expect(network).toHaveBeenCalledTimes(2);
expect(network.mock.calls[1][0]).toBe(
"https://api.github.com/repos/eugeneware/ffmpeg-static/releases/assets/316528626",
);
});
it("does not retry programming failures as network outages", async () => {
const network = vi.fn().mockRejectedValue(new Error("unexpected failure"));
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toThrow("unexpected failure");
expect(network).toHaveBeenCalledOnce();
});
it("stops after a failed API fallback rather than looping", async () => {
const network = vi
.fn()
.mockResolvedValueOnce(new Response(null, { status: 504 }))
.mockResolvedValueOnce(new Response(null, { status: 503 }));
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toThrow("download failed (503)");
expect(network).toHaveBeenCalledTimes(2);
});
it("bounds both stalled requests with real abort signals", async () => {
const realTimeout = AbortSignal.timeout.bind(AbortSignal);
const deadlines = vi.spyOn(AbortSignal, "timeout").mockImplementation(() => realTimeout(1));
const network = vi.fn(
(_url, { signal }) =>
new Promise((_resolve, reject) => {
if (signal.aborted) reject(signal.reason);
else
signal.addEventListener("abort", () => reject(signal.reason), {
once: true,
});
}),
);
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toMatchObject({ name: "TimeoutError" });
expect(network).toHaveBeenCalledTimes(2);
expect(deadlines.mock.calls).toEqual([[180000], [180000]]);
expect(network.mock.calls[0][1].signal).not.toBe(network.mock.calls[1][1].signal);
});
it("includes response-body stalls in the same two-request bound", async () => {
const realTimeout = AbortSignal.timeout.bind(AbortSignal);
const deadlines = vi.spyOn(AbortSignal, "timeout").mockImplementation(() => realTimeout(1));
const network = vi.fn(async (_url, { signal }) => ({
ok: true,
arrayBuffer: () =>
new Promise((_resolve, reject) => {
if (signal.aborted) reject(signal.reason);
else
signal.addEventListener("abort", () => reject(signal.reason), {
once: true,
});
}),
}));
vi.stubGlobal("fetch", network);
await expect(installTarget()).rejects.toMatchObject({ name: "TimeoutError" });
expect(network).toHaveBeenCalledTimes(2);
expect(deadlines.mock.calls).toEqual([[180000], [180000]]);
});
});
+45 -3
View File
@@ -13,6 +13,16 @@ import { machoContentHash } from "./macho-content-hash.mjs";
import { peContentHash } from "./pe-content-hash.mjs";
const upstreamRelease = "https://github.com/eugeneware/ffmpeg-static/releases/download/b6.1.1";
// Exact assets from that same pinned release. The API route avoids release-page outages.
const upstreamAssetIds = Object.freeze({
"ffmpeg-linux-arm64.gz": 316528458,
"ffmpeg-linux-x64.gz": 316528626,
"ffmpeg-win32-x64.gz": 316528798,
"ffprobe-win32-x64.gz": 316529830,
"linux-arm64.README": 316529906,
"linux-x64.README": 316529927,
"win32-x64.README": 316529965,
});
// Verified raw release digests. The upstream darwin-arm64 artifact enables nonfree.
export const APPROVED_FFMPEG_BINARIES = Object.freeze({
"win32-x64": "04e1307997530f9cf2fe35cba2ca7e8875ca91da02f89d6c7243df819c94ad00",
@@ -151,9 +161,41 @@ export function verifyFfprobeDistribution(
}
async function download(url) {
const response = await fetch(url, { signal: AbortSignal.timeout(180000) });
if (!response.ok) throw new Error(`FFmpeg download failed (${response.status}): ${url}`);
return Buffer.from(await response.arrayBuffer());
const assetName = url.startsWith(`${upstreamRelease}/`)
? url.slice(upstreamRelease.length + 1)
: undefined;
const assetId = upstreamAssetIds[assetName];
if (!Number.isSafeInteger(assetId)) throw new Error("Unapproved FFmpeg asset URL.");
for (let attempt = 0; attempt < 2; attempt += 1) {
let response;
try {
response = await fetch(
attempt === 0
? url
: `https://api.github.com/repos/eugeneware/ffmpeg-static/releases/assets/${assetId}`,
{
signal: AbortSignal.timeout(180000),
credentials: "omit",
...(attempt === 0 ? {} : { headers: { Accept: "application/octet-stream" } }),
},
);
if (!response.ok) {
await response.body?.cancel().catch(() => {
// Preserve the HTTP failure if releasing its body also fails.
});
throw new Error(`FFmpeg download failed (${response.status}): ${url}`);
}
return Buffer.from(await response.arrayBuffer());
} catch (error) {
const retryable =
response && !response.ok
? response.status >= 500 && response.status <= 599
: error instanceof TypeError ||
error?.name === "TimeoutError" ||
error?.name === "AbortError";
if (attempt !== 0 || !retryable) throw error;
}
}
}
async function ensureWindowsFfprobe(root) {