fix: ffmpeg-Binary im Container fehlte (ENOENT bei jedem Video-Upload)
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 3m50s

Live-Log zeigte: spawn .../ffmpeg-static/ffmpeg ENOENT bei jedem
Transcode-Versuch, unabhaengig vom Videoformat (.mov war nur zufaellig
der zuletzt getestete Fall). Die ffmpeg-static-Binary aus der
migrator-Docker-Stage kam nicht zuverlaessig im finalen Image an.

Root-Fix: ffmpeg jetzt per "apk add ffmpeg" als System-Paket in der
runner-Stage installiert - liegt fest unter /usr/bin auf PATH,
unabhaengig von jeder Node/pnpm-Kopiermechanik. lib/video-transcode.ts
versucht zuerst "ffmpeg" ueber PATH und faellt nur bei ENOENT (kein
System-ffmpeg vorhanden, typischerweise lokale Windows-/Mac-Entwicklung)
auf die per npm mitgelieferte ffmpeg-static-Binary zurueck, damit
"npm run dev" weiterhin ohne manuelle Einrichtung funktioniert. Beide
Pfade lokal gegen ein synthetisches Testvideo verifiziert.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Sonnet 5 committed 2026-09-12 03:38:06 +02:00
1 parent a366ffb544
commit 902f818c6e
2 files changed
+51 -43

No files matched your search

+41 -28
View File
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import ffmpegPath from "ffmpeg-static";
import ffmpegStaticPath from "ffmpeg-static";
/**
* Harte Obergrenze fuer einen einzelnen ffmpeg-Lauf: verhindert, dass ein
@@ -40,39 +40,52 @@ const TRANSCODE_TIMEOUT_MS = 5 * 60 * 1000;
* Magic-Bytes am Dateianfang).
*/
export async function transcodeVideoForWeb(buffer: Buffer): Promise<Buffer> {
if (!ffmpegPath) {
throw new Error("ffmpeg-Binary (ffmpeg-static) nicht gefunden.");
}
const dir = await mkdtemp(path.join(tmpdir(), "wabenhain-video-"));
const inputPath = path.join(dir, "input");
const outputPath = path.join(dir, "output.mp4");
const args = [
"-y",
"-i",
inputPath,
"-an",
"-vf",
"scale='min(1920,iw)':-2",
"-c:v",
"libx264",
"-preset",
"medium",
"-crf",
"26",
"-maxrate",
"3M",
"-bufsize",
"6M",
"-pix_fmt",
"yuv420p",
"-movflags",
"+faststart",
outputPath,
];
try {
await writeFile(inputPath, buffer);
await runFfmpeg(ffmpegPath, [
"-y",
"-i",
inputPath,
"-an",
"-vf",
"scale='min(1920,iw)':-2",
"-c:v",
"libx264",
"-preset",
"medium",
"-crf",
"26",
"-maxrate",
"3M",
"-bufsize",
"6M",
"-pix_fmt",
"yuv420p",
"-movflags",
"+faststart",
outputPath,
]);
try {
// Bevorzugt "ffmpeg" ueber PATH — im Produktions-Image per "apk add
// ffmpeg" installiert (siehe Dockerfile "runner"-Stage), fest unter
// /usr/bin und unabhaengig von jeder Node/pnpm-Verzeichnisstruktur.
await runFfmpeg("ffmpeg", args);
} catch (error: unknown) {
// ENOENT = Binary ueber PATH nicht gefunden (typischerweise ein
// lokaler Windows-/Mac-Rechner ohne System-ffmpeg) — dann auf die per
// npm mitgelieferte ffmpeg-static-Binary ausweichen, damit "npm run
// dev" ohne manuelle Einrichtung funktioniert. Jeder andere Fehler
// (z.B. eine tatsaechlich nicht dekodierbare Datei) soll dagegen
// unveraendert durchschlagen, nicht stillschweigend erneut versucht
// werden.
const isMissingBinary = error instanceof Error && "code" in error && error.code === "ENOENT";
if (!isMissingBinary || !ffmpegStaticPath) throw error;
await runFfmpeg(ffmpegStaticPath, args);
}
return await readFile(outputPath);
} finally {
await rm(dir, { recursive: true, force: true });