Video records store url = /uploads/videos/<id>.mp4, but nothing served
that path: Next.js only serves static files from public/, so the
player's MP4 fallback 404'd and videos were unplayable until HLS
transcoding finished. (Thumbnails hit the same problem historically —
see scripts/update-thumbnail-urls.ts.)
Add a route handler at the exact stored URL so no DB migration is
needed. Unlike the HLS route it streams from disk via createReadStream
instead of buffering the file, and implements HTTP Range requests
(206/416, open-ended and suffix ranges) — browsers require ranges for
<video> seeking, and files can be multi-GB.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Cast the transcoder upload body to node:stream/web ReadableStream;
the DOM ReadableStream type lacks the async-iterator members
Readable.fromWeb expects
- Exclude transcoder/ and transcoder-remote/ from the root tsconfig:
they are standalone sub-projects with their own tsconfig and
dependencies (e.g. archiver) that aren't installed in the root
node_modules, so next build's typecheck can never resolve them
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
req.formData() parsed the entire multipart body into RAM, so large
uploads (1.6GB+) OOM-killed the server. Bridge the web Request body to
a Node stream and parse it with formidable, which writes the file to
disk as it arrives:
- Temp dir is /uploads/tmp (same filesystem as the destination) so the
post-parse move is an atomic rename, not a second multi-GB write
- Mark the bridged request as chunked when a proxy strips
content-length, otherwise formidable assumes an empty body
- Track formidable temp files via fileBegin so aborted uploads get
their partial files cleaned up
- Remove the dead formidable branch gated on (req as any).req, which
is always null in the App Router
Also fix the nginx example in VPS_UPLOAD_CONFIG.md: client_max_body_size
was 1024M, below the app's 2.5GB limit, and would 413 large uploads at
the proxy before they reached the app.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>