Replaces single-MP4 + range-request streaming with HLS multi-bitrate
segments to fix video stutter through the Newt tunnel. Range-request
bursts were the root cause; HLS chunks are small and tunnel-friendly,
plus the player adapts bitrate to bandwidth.
Backend
- New BullMQ `hls-transcode` queue (in-process worker, concurrency 1)
- FFmpeg single-pass transcode → 360p/720p/1080p variants with aligned
keyframes; output at /media/local/hls/{id}/master.m3u8
- New /api/{videos|public}/{id}/hls/* routes serving signed manifests
and segments (URLs emitted as /media/* so nginx rewrites to media-api)
- Prisma: HlsStatus enum + 6 fields on Video + index, migration
- Upload + yt-dlp fetch paths enqueue transcode jobs
- ENABLE_HLS_TRANSCODE flag (default off; gates enqueue only)
- Backfill script: `npm run backfill:hls`
- media-api bumped to 4 CPU / 2G for FFmpeg headroom
Frontend
- New useHls hook: lazy-imports hls.js (kept out of main bundle),
native HLS on Safari/iOS, gives up after 2 NETWORK_ERRORs so MP4
fallback engages cleanly
- VideoPlayer, VideoViewerModal, ShortsPage, ProductDetailPage now
prefer HLS when ready; MP4 fallback is automatic
- ShortsPage prefetches next-3 master manifests via <link rel="prefetch">
- PublicVideoCard hover preview stays MP4 (avoids hls.js init latency)
Bunker Admin
Documents are a separate media type from Video/Photo because the
Photo pipeline assumes raster images (sharp metadata, EXIF, variant
generation). The new routes mirror the photo upload pattern but
target the Document Prisma model and serve files with
Content-Disposition: attachment so browsers download instead of
inline-rendering. Tag-based categorization (e.g. 'volunteer-resource')
lets the volunteer dashboard surface curated downloads alongside
videos and photos.
Admin Library page gets a Documents tab for upload/list/edit/delete
with the same affordances as the existing photo and video tabs.
Bunker Admin
Major additions: onboarding tour system, correlation-id middleware, media
error handler, restore script, env validation script, Dockerignore files.
Updates across 70+ admin components for improved UX and error handling.
Bunker Admin
- NotFoundPage component with Go Back, Go Home (role-aware), and Report to Admin buttons
- Catch-all routes inside AppLayout, VolunteerLayout, and top-level PublicLayout
- POST /api/public/error-report endpoint sends 404 notification emails to super admins
- Express API 404 handler returns consistent JSON error envelope for /api/* routes
- Fastify media API 404 handler via setNotFoundHandler
- Rate-limited error reports (5/hour per IP)
Bunker Admin