Upload once, get back an Instagram Reel, a YouTube Short, HLS segments, a blur-shot thumbnail, and a cover — all queued, all on R2.

A professional video creator publishing across Instagram and YouTube re-exports the same footage multiple times — different resolutions, aspect ratios, and codec settings per platform. That is twenty minutes of manual FFmpeg or Premiere work per video, before any editing happens. For someone who publishes daily, it compounds into hours a week.
The real requirement was not a transcoder — it was disappearing the decision. Upload a source, get back every derivative you need, stored and ready, with no flags to remember and no export queue to babysit.
The same core pattern as AmarV4 — signed upload, queue, transcode, R2 — but the fan-out step is more complex: one source becomes five distinct outputs, each with different FFmpeg filter chains and path conventions.
Each output format is a separate BullMQ job. If the Instagram render fails, the HLS segments still complete. The client sees per-format progress and only broken outputs need retrying — not the whole upload.
Every output lands at a deterministic path: uploads/{id}/hls/master.m3u8, uploads/{id}/reel.mp4, and so on. The frontend never asks the backend “where did the files go?” — it constructs the URL from the job ID. No extra database round-trip.
The blurred-background thumbnail — source frame centred on a blurred full-bleed version of itself — is a single FFmpeg filtergraph. It saves the client from opening any image editor and produces a consistent look across every video automatically.
The blurred background + centred source composite sounds simple and is not. The filtergraph has to scale the source to two different sizes, blur one, overlay the other with correct gravity and padding — and do all of it in a single pass to keep encode time reasonable.
Instagram and YouTube Shorts both want 9:16, but a landscape source cropped naively loses the subject. A centre-crop is the safe default, but getting the crop coordinates right across arbitrary source resolutions without distortion took more geometry than expected.
Five jobs per upload means five status states the UI has to reflect simultaneously. BullMQ has per-job event emitters; wiring them into a coherent “this upload is 3/5 done” view over SSE was the most fiddly part of the frontend.
“The client's job is to make good video. PIPE's job is to make sure the format question never enters the room.”— Parm, on PIPE