# 🎬 Wasabi HLS Streaming Architecture Complete guide to how HLS video streaming works with Wasabi storage and proxy CDN architecture. --- ## πŸ“‹ Table of Contents 1. [Overview](#overview) 2. [Upload & Conversion Flow](#upload--conversion-flow) 3. [Streaming Flow](#streaming-flow) 4. [URL Rewriting Flow](#url-rewriting-flow) 5. [Why Local Testing is Slow](#why-local-testing-is-slow) 6. [Production Performance](#production-performance) --- ## 🎯 Overview **Problem:** Wasabi free trial doesn't allow public bucket access **Solution:** Server acts as proxy CDN - streams files from Wasabi to clients **Benefit:** No direct Wasabi URLs exposed, full control over streaming --- ## πŸ“€ Upload & Conversion Flow ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ UPLOAD & HLS CONVERSION FLOW β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Step 1: User uploads video file ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ POST /wasabi/upload-video-hls β”‚ (Browser)β”‚ Content-Type: multipart/form-data β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ { video: video.mp4 } β”‚ β”‚ HTTP Upload β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageController β”‚ β”‚ @Post('upload-video-hls') β”‚ β”‚ β”‚ β”‚ β€’ Receives file via multer β”‚ β”‚ β€’ Saves to: ./resources/temp/ β”‚ β”‚ β€’ Generates uploadId β”‚ β”‚ (e.g., 1766933325669-42b7k6) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Call service β–Ό Step 2: Async HLS Conversion (FFmpeg) ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ startVideoProcessing() β”‚ β”‚ β”‚ β”‚ Returns uploadId immediately β”‚ β”‚ (non-blocking, async processing) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Async processing in background β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HlsConversionService β”‚ β”‚ convertVideoToAdaptiveHLS() β”‚ β”‚ β”‚ β”‚ Converts video to HLS format: β”‚ β”‚ β€’ 1080p (1920x1080, 5500k bitrate) β”‚ β”‚ β€’ 720p (1280x720, 3000k bitrate) β”‚ β”‚ β€’ 480p (854x480, 1200k bitrate) β”‚ β”‚ β€’ Audio-only (128k bitrate) β”‚ β”‚ β”‚ β”‚ Generates files: β”‚ β”‚ β€’ master.m3u8 (main playlist) β”‚ β”‚ β€’ index_1080p.m3u8 β”‚ β”‚ β€’ index_720p.m3u8 β”‚ β”‚ β€’ index_480p.m3u8 β”‚ β”‚ β€’ index_audio.m3u8 β”‚ β”‚ β€’ segment_1080p_000.ts to XXX.ts β”‚ β”‚ β€’ segment_720p_000.ts to XXX.ts β”‚ β”‚ β€’ segment_480p_000.ts to XXX.ts β”‚ β”‚ β€’ segment_audio_000.ts to XXX.ts β”‚ β”‚ β”‚ β”‚ Saved to: /tmp/streamflix-hls/ β”‚ β”‚ {uploadId}/ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Conversion complete β–Ό Step 3: Upload all files to Wasabi ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ uploadHLSFilesToWasabi() β”‚ β”‚ β”‚ β”‚ Uploads to Wasabi S3: β”‚ β”‚ β€’ hls/{uploadId}/master.m3u8 β”‚ β”‚ β€’ hls/{uploadId}/index_1080p.m3u8 β”‚ β”‚ β€’ hls/{uploadId}/index_720p.m3u8 β”‚ β”‚ β€’ hls/{uploadId}/index_480p.m3u8 β”‚ β”‚ β€’ hls/{uploadId}/index_audio.m3u8 β”‚ β”‚ β€’ hls/{uploadId}/segment_*.ts β”‚ β”‚ (all segments for all qualities) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Upload complete β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Wasabi S3 Storage β”‚ β”‚ Bucket: streamflix-backed β”‚ β”‚ Region: us-west-1 β”‚ β”‚ β”‚ β”‚ Storage structure: β”‚ β”‚ hls/ β”‚ β”‚ └── 1766933325669-42b7k6/ β”‚ β”‚ β”œβ”€β”€ master.m3u8 β”‚ β”‚ β”œβ”€β”€ index_1080p.m3u8 β”‚ β”‚ β”œβ”€β”€ index_720p.m3u8 β”‚ β”‚ β”œβ”€β”€ index_480p.m3u8 β”‚ β”‚ β”œβ”€β”€ index_audio.m3u8 β”‚ β”‚ β”œβ”€β”€ segment_1080p_000.ts β”‚ β”‚ β”œβ”€β”€ segment_1080p_001.ts β”‚ β”‚ β”œβ”€β”€ ... (hundreds of segments) β”‚ β”‚ β”œβ”€β”€ segment_720p_000.ts β”‚ β”‚ β”œβ”€β”€ segment_480p_000.ts β”‚ β”‚ └── segment_audio_000.ts β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Step 4: Cleanup local files ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HlsConversionService β”‚ β”‚ cleanupFiles(uploadId) β”‚ β”‚ β”‚ β”‚ Deletes from local disk: β”‚ β”‚ β€’ /tmp/streamflix-hls/{uploadId}/ β”‚ β”‚ β€’ ./resources/temp/video-*.mp4 β”‚ β”‚ β”‚ β”‚ Result: Files only in Wasabi β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ“₯ Streaming Flow (The Proxy CDN Magic) ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ HLS STREAMING FLOW (PROXY CDN) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Step 1: User requests master playlist ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ GET /wasabi/hls?uploadId=1766933325669-42b7k6 β”‚ (HLS β”‚ β”‚ Player) β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ β”‚ HTTP Request β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageController β”‚ β”‚ @Get('hls') β”‚ β”‚ @SkipApiToken() (public) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Call service β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ getRewrittenM3U8(uploadId) β”‚ β”‚ β”‚ β”‚ 1. Fetch from Wasabi: β”‚ β”‚ hls/{uploadId}/master.m3u8 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Internal S3 request (presigned URL) β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Wasabi S3 Storage β”‚ β”‚ Returns master.m3u8 content: β”‚ β”‚ β”‚ β”‚ #EXTM3U β”‚ β”‚ #EXT-X-MEDIA:URI="index_audio.m3u8"β”‚ β”‚ #EXT-X-STREAM-INF:BANDWIDTH=1328000β”‚ β”‚ index_480p.m3u8 β”‚ β”‚ #EXT-X-STREAM-INF:BANDWIDTH=3128000β”‚ β”‚ index_720p.m3u8 β”‚ β”‚ #EXT-X-STREAM-INF:BANDWIDTH=5628000β”‚ β”‚ index_1080p.m3u8 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Stream master.m3u8 content β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ getRewrittenM3U8() β”‚ β”‚ β”‚ β”‚ 2. Rewrite URLs (URL MAGIC!) β”‚ β”‚ β”‚ β”‚ Before: β”‚ β”‚ URI="index_audio.m3u8" β”‚ β”‚ index_480p.m3u8 β”‚ β”‚ index_720p.m3u8 β”‚ β”‚ β”‚ β”‚ After: β”‚ β”‚ URI="https://domain/wasabi/ β”‚ β”‚ file?path=hls%2F...%2F β”‚ β”‚ index_audio.m3u8" β”‚ β”‚ https://domain/wasabi/ β”‚ β”‚ file?path=hls%2F...%2F β”‚ β”‚ index_480p.m3u8 β”‚ β”‚ https://domain/wasabi/ β”‚ β”‚ file?path=hls%2F...%2F β”‚ β”‚ index_720p.m3u8 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Return rewritten playlist β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ Receives master.m3u8 with β”‚ (HLS β”‚ absolute URLs to server proxy β”‚ Player) β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ β”‚ Player parses, selects quality (e.g., 720p) β–Ό Step 2: Player requests quality playlist ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ GET /wasabi/file?path=hls%2F...%2Findex_720p.m3u8 β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ β”‚ HTTP Request β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageController β”‚ β”‚ @Get('file') β”‚ β”‚ @SkipApiToken() (public) β”‚ β”‚ β”‚ β”‚ Detects: .m3u8 file in hls/ folder β”‚ β”‚ Triggers: Automatic URL rewriting β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Call service β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ streamFile(path) β”‚ β”‚ β”‚ β”‚ 1. Fetch from Wasabi: β”‚ β”‚ hls/{uploadId}/index_720p.m3u8 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Internal S3 request β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Wasabi S3 Storage β”‚ β”‚ Returns index_720p.m3u8: β”‚ β”‚ β”‚ β”‚ #EXTM3U β”‚ β”‚ #EXTINF:6.000000, β”‚ β”‚ segment_720p_000.ts β”‚ β”‚ #EXTINF:6.000000, β”‚ β”‚ segment_720p_001.ts β”‚ β”‚ #EXTINF:6.000000, β”‚ β”‚ segment_720p_002.ts β”‚ β”‚ ... (more segments) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Stream playlist content β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ streamFile() - Auto Rewrite! β”‚ β”‚ β”‚ β”‚ 2. Rewrite segment URLs β”‚ β”‚ β”‚ β”‚ Before: β”‚ β”‚ segment_720p_000.ts β”‚ β”‚ segment_720p_001.ts β”‚ β”‚ segment_720p_002.ts β”‚ β”‚ β”‚ β”‚ After: β”‚ β”‚ https://domain/wasabi/ β”‚ β”‚ file?path=hls%2F...%2F β”‚ β”‚ segment_720p_000.ts β”‚ β”‚ https://domain/wasabi/ β”‚ β”‚ file?path=hls%2F...%2F β”‚ β”‚ segment_720p_001.ts β”‚ β”‚ https://domain/wasabi/ β”‚ β”‚ file?path=hls%2F...%2F β”‚ β”‚ segment_720p_002.ts β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Return rewritten playlist β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ Receives quality playlist with β”‚ (HLS β”‚ absolute URLs to server proxy β”‚ Player) β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ β”‚ Player starts downloading segments β–Ό Step 3: Player requests video segments (MANY PARALLEL REQUESTS) ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ GET /wasabi/file?path=hls%2F...%2Fsegment_720p_000.ts β”‚ (HLS β”‚ GET /wasabi/file?path=hls%2F...%2Fsegment_720p_001.ts β”‚ Player) β”‚ GET /wasabi/file?path=hls%2F...%2Fsegment_720p_002.ts β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ (Multiple parallel requests) β”‚ β”‚ HTTP Requests (parallel) β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageController β”‚ β”‚ @Get('file') β”‚ β”‚ @SkipApiToken() (public) β”‚ β”‚ β”‚ β”‚ Detects: .ts file (video segment) β”‚ β”‚ Action: Direct stream (no rewrite) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Call service β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ streamFile(path) β”‚ β”‚ β”‚ β”‚ Fetch from Wasabi: β”‚ β”‚ hls/{uploadId}/segment_720p_000.ts β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Internal S3 request β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Wasabi S3 Storage β”‚ β”‚ Returns video segment: β”‚ β”‚ Content-Type: video/mp2t β”‚ β”‚ Size: ~1-2 MB (6 seconds of video) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Stream segment (DIRECT PIPE) β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WasabiStorageService β”‚ β”‚ streamFile() β”‚ β”‚ β”‚ β”‚ ⚑ CRITICAL: Direct piping! β”‚ β”‚ result.stream.pipe(res) β”‚ β”‚ β”‚ β”‚ β€’ NO buffering on server β”‚ β”‚ β€’ Chunks stream immediately β”‚ β”‚ β€’ Server memory stays low β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Pipe stream chunks β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Client β”‚ Receives video chunks in real-time β”‚ (HLS β”‚ Player buffers and plays video β”‚ Player) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ πŸ”„ This repeats for EVERY segment (could be 100+ segments per quality) ``` --- ## πŸ”§ URL Rewriting Flow (Technical Details) ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ URL REWRITING LOGIC β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Problem: HLS playlists contain relative file paths Wasabi bucket is NOT public (free trial restriction) Direct Wasabi URLs won't work Solution: Rewrite ALL URLs to point to our proxy server ═══════════════════════════════════════════════════════════════════════════════ Original master.m3u8 from Wasabi: ───────────────────────────────────────────────────────────────────────────── #EXTM3U #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="Audio",DEFAULT=YES,URI="index_audio.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=1328000,RESOLUTION=854x480,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio" index_480p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=3128000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio" index_720p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=5628000,RESOLUTION=1920x1080,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio" index_1080p.m3u8 ───────────────────────────────────────────────────────────────────────────── Rewriting Process (2 patterns): ═══════════════════════════════════════════════════════════════════════════════ Pattern 1: URIs inside quotes (audio track) ──────────────────────────────────────────────────────────────────────────── Regex: /URI="([^"]+\.(m3u8|ts))"/g Before: URI="index_audio.m3u8" After: URI="https://domain/wasabi/file?path=hls%2F{uploadId}%2Findex_audio.m3u8" Pattern 2: Standalone filenames on their own line ──────────────────────────────────────────────────────────────────────────── Check: line.trim().endsWith('.m3u8') || line.trim().endsWith('.ts') Before: index_480p.m3u8 After: https://domain/wasabi/file?path=hls%2F{uploadId}%2Findex_480p.m3u8 Before: segment_720p_000.ts After: https://domain/wasabi/file?path=hls%2F{uploadId}%2Fsegment_720p_000.ts Rewritten master.m3u8 sent to client: ───────────────────────────────────────────────────────────────────────────── #EXTM3U #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="Audio",DEFAULT=YES,URI="https://domain/wasabi/file?path=hls%2F1766933325669-42b7k6%2Findex_audio.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=1328000,RESOLUTION=854x480,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio" https://domain/wasabi/file?path=hls%2F1766933325669-42b7k6%2Findex_480p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=3128000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio" https://domain/wasabi/file?path=hls%2F1766933325669-42b7k6%2Findex_720p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=5628000,RESOLUTION=1920x1080,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio" https://domain/wasabi/file?path=hls%2F1766933325669-42b7k6%2Findex_1080p.m3u8 ───────────────────────────────────────────────────────────────────────────── Result: HLS player sees fully qualified URLs All URLs point to our server (proxy CDN) Player fetches everything through our API ``` --- ## 🐌 Why Local Testing is Slow ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ LOCAL TESTING BOTTLENECK ANALYSIS β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Scenario: Testing on localhost with slow home internet ═══════════════════════════════════════════════════════════════════════════════ Your Network: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ [Your Computer] <───────────┐ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ Browser β”‚ β”‚ Loopback (instant) β”‚ β”‚ β”‚ HLS Player β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ HTTP Request β”‚ β”‚ β”‚ β”‚ (localhost:5119) β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ NestJS Serverβ”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ (localhost) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ⚠️ BOTTLENECK: Downloads from Wasabi β”‚ β”‚ β”‚ via your home internet (slow upload!) β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Home Internet β”‚ Upload: 5-20 Mbps (typical home internet) β”‚ β”‚ β”‚ Router/Modem β”‚ Download: 50-200 Mbps β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Internet (slow home upload speed) β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Wasabi S3 β”‚ Located in: us-west-1 (California) β”‚ Storage β”‚ Upload/Download: Very fast (datacenter speeds) β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ The Problem (Request Flow): ═══════════════════════════════════════════════════════════════════════════════ 1. Browser requests segment: GET /wasabi/file?path=segment_720p_000.ts 2. Server receives request (instant - localhost) 3. ⚠️ Server downloads from Wasabi: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ NestJS Server β”‚ β”‚ └─> Wasabi S3 Request β”‚ β”‚ β”‚ β”‚ Download speed LIMITED BY: β”‚ β”‚ β€’ Your home internet UPLOAD speed (5-20 Mbps typical) β”‚ β”‚ β€’ Wasabi is downloading TO your home (uses upload speed!) β”‚ β”‚ β€’ Segment size: ~1-2 MB β”‚ β”‚ β”‚ β”‚ Time: 1-2 MB Γ· 10 Mbps upload = 0.8-1.6 seconds per chunk β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 4. Server streams to browser (instant - localhost) 5. ⚠️ Player needs 3-5 segments buffered before playing Total time: 3 segments Γ— 1.5s each = 4.5 seconds initial load Why it's slow: ───────────────────────────────────────────────────────────────────────────── ❌ Home internet upload speed is slow (5-20 Mbps typical) ❌ Wasabi β†’ Your Server uses your UPLOAD bandwidth ❌ Multiple segments needed before playback starts ❌ Each segment takes 1-2 seconds to download from Wasabi Visual Timeline: ───────────────────────────────────────────────────────────────────────────── Time Browser Server Wasabi ──────────────────────────────────────────────────────────────────────────── 0.0s Request seg_000.ts ──> Waiting 0.1s Request to Wasabi ──────> Processing 0.2s Downloading.............. Sending 0.5s Downloading.............. Sending 1.0s Downloading.............. Sending 1.5s Download complete <────── Done 1.6s Receiving chunk <──── Streaming to browser 2.0s Request seg_001.ts ──> Waiting (repeat above) ``` --- ## πŸš€ Production Performance (Why Server Deployment is Fast) ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PRODUCTION DEPLOYMENT PERFORMANCE β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Scenario: Server deployed on cloud (AWS, DigitalOcean, Heroku, etc.) ═══════════════════════════════════════════════════════════════════════════════ Production Network: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CLIENT SIDE β”‚ β”‚ [User's Device] β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Browser β”‚ β”‚ β”‚ β”‚ HLS Player β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Internet (user's speed doesn't affect server-to-wasabi) β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ SERVER SIDE (CLOUD) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Cloud Datacenter (AWS/DigitalOcean/Heroku) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ NestJS Serverβ”‚ β”‚ β”‚ β”‚ β”‚ β”‚ (Production) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ⚑ FAST: Datacenter-to-datacenter connection β”‚ β”‚ β”‚ β”‚ β”‚ Speed: 1-10 Gbps (1000-10000 Mbps) β”‚ β”‚ β”‚ β”‚ β”‚ Latency: 5-20ms β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ High-Speed β”‚ Upload: 1-10 Gbps β”‚ β”‚ β”‚ β”‚ β”‚ Datacenter β”‚ Download: 1-10 Gbps β”‚ β”‚ β”‚ β”‚ β”‚ Network β”‚ Unlimited bandwidth β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ High-speed datacenter backbone β”‚ Speed: 1-10 Gbps β”‚ Latency: ~10ms β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Wasabi S3 β”‚ Located in: us-west-1 (California) β”‚ Storage β”‚ Datacenter: High-speed connections β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Performance Comparison: ═══════════════════════════════════════════════════════════════════════════════ Local Testing Production Server ──────────────────────────────────────────────────────────────────────────── Server Network Home Internet Cloud Datacenter Upload Speed 5-20 Mbps 1,000-10,000 Mbps Download Speed 50-200 Mbps 1,000-10,000 Mbps Serverβ†’Wasabi Uses upload (slow) Datacenter speed (fast) Latency 50-100ms 5-20ms Segment Download 1-2 seconds 0.05-0.2 seconds Initial Load 4-6 seconds 0.5-1 second Buffering Frequent Rare/Never Production Request Flow (FAST!): ═══════════════════════════════════════════════════════════════════════════════ 1. Browser requests segment: GET /wasabi/file?path=segment_720p_000.ts 2. Server receives request (fast - CDN) 3. βœ… Server downloads from Wasabi: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ NestJS Server (Cloud Datacenter) β”‚ β”‚ └─> Wasabi S3 Request β”‚ β”‚ β”‚ β”‚ Download speed: β”‚ β”‚ β€’ Datacenter connection: 1-10 Gbps β”‚ β”‚ β€’ Wasabi datacenter: 1-10 Gbps β”‚ β”‚ β€’ Segment size: ~1-2 MB β”‚ β”‚ β”‚ β”‚ Time: 1-2 MB Γ· 1 Gbps = 0.01-0.02 seconds per chunk β”‚ β”‚ (50-100x FASTER than home internet!) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 4. Server streams to browser (depends on user's internet) 5. βœ… Player starts playing almost immediately Total time: 3 segments Γ— 0.05s each = 0.15 seconds initial load Visual Timeline (Production): ───────────────────────────────────────────────────────────────────────────── Time Browser Server (Cloud) Wasabi ──────────────────────────────────────────────────────────────────────────── 0.00s Request seg_000.ts ──> Waiting 0.01s Request to Wasabi ──────> Processing 0.02s Download complete <────── Done (FAST!) 0.03s Receiving chunk <──── Streaming to browser 0.10s Request seg_001.ts ──> Waiting 0.11s Request to Wasabi ──────> Processing 0.12s Download complete <────── Done (FAST!) 0.13s Receiving chunk <──── Streaming to browser 0.20s 🎬 Video starts playing (smooth!) Why Production is Fast: ───────────────────────────────────────────────────────────────────────────── βœ… Datacenter network: 1-10 Gbps (100-1000x faster than home) βœ… Low latency: 5-20ms (vs 50-100ms home internet) βœ… Unlimited bandwidth: No ISP throttling βœ… Parallel downloads: Server can handle 100+ concurrent requests βœ… Geographic proximity: Server and Wasabi both in datacenters Optimization Techniques Applied: ═══════════════════════════════════════════════════════════════════════════════ 1. ⚑ Direct Piping (No Buffering) ───────────────────────────────────────────────────────────────────────── result.stream.pipe(res) β€’ Chunks stream immediately (no waiting for full download) β€’ Server memory stays low (no buffering in RAM) β€’ Start streaming to client while still downloading from Wasabi 2. ⚑ No Processing Overhead ───────────────────────────────────────────────────────────────────────── For .ts segments: Pure streaming (no URL rewriting) For .m3u8 files: Only rewrite text (very fast string operation) 3. ⚑ HTTP Range Support ───────────────────────────────────────────────────────────────────────── Accept-Ranges: bytes β€’ Allows video seeking (jump to any point) β€’ Browser only downloads needed chunks β€’ No need to download entire video 4. ⚑ CORS Enabled ───────────────────────────────────────────────────────────────────────── Access-Control-Allow-Origin: * β€’ Works with any HLS player β€’ No preflight request delays 5. ⚑ CDN-Ready Headers ───────────────────────────────────────────────────────────────────────── Cache-Control: public, max-age=2592000 β€’ Browser caches segments for 30 days β€’ Repeated views = instant playback β€’ Can add Cloudflare/CDN in front for global edge caching ``` --- ## πŸ“Š Performance Comparison Summary ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PERFORMANCE METRICS COMPARISON β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Metric Local Testing Production Server Improvement ═══════════════════════════════════════════════════════════════════════════════ Server Network Speed 10 Mbps 1,000 Mbps 100x faster Serverβ†’Wasabi Latency 50-100ms 5-20ms 5x faster Segment Download Time 1-2 seconds 0.05-0.2 seconds 10-20x faster Initial Video Load 4-6 seconds 0.5-1 second 5-10x faster Buffering Frequency Every 3-5 seg Rare/Never Smooth Concurrent Users 1-2 100-1000+ Scalable Memory Usage Low (piping) Low (piping) Efficient CPU Usage Low (no proc) Low (no proc) Efficient Bottleneck Analysis: ═══════════════════════════════════════════════════════════════════════════════ Local Testing: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Browser ←─── [INSTANT] ───→ Server ←─── [SLOW!] ───→ Wasabi β”‚ β”‚ (Fast) (Localhost) (Home Internet) β”‚ β”‚ ↑ β”‚ β”‚ └── BOTTLENECK HERE β”‚ β”‚ (5-20 Mbps upload) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Production: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Browser ←─ [User Speed] ─→ Server ←─── [FAST!] ───→ Wasabi β”‚ β”‚ (Varies) (Cloud) (Datacenter) β”‚ β”‚ ↑ β”‚ β”‚ └── NO BOTTLENECK β”‚ β”‚ (1-10 Gbps) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Key Takeaway: ═══════════════════════════════════════════════════════════════════════════════ πŸ“Œ Local testing is slow because your HOME INTERNET UPLOAD SPEED limits the server's ability to download from Wasabi. πŸ“Œ Production is fast because SERVER-TO-WASABI happens at datacenter speeds (1000x faster than home internet). πŸ“Œ Your slow internet does NOT affect the production server's performance! πŸ“Œ Deploy to cloud β†’ Experience true speed! πŸš€ ``` --- ## 🎯 Architecture Benefits ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ WHY THIS ARCHITECTURE? β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ βœ… Works with Wasabi Free Trial β€’ No need for public bucket access β€’ No need to pay for CDN features β€’ Full control over access βœ… Security β€’ All Wasabi credentials stay on server β€’ No direct Wasabi URLs exposed to clients β€’ Can add authentication/authorization later βœ… Flexibility β€’ Can add rate limiting per user β€’ Can track analytics (views, bandwidth) β€’ Can add watermarks or DRM later βœ… Cost Effective β€’ Wasabi storage: Very cheap ($6/TB/month) β€’ No egress fees (unlimited downloads) β€’ No need for separate CDN service βœ… Scalability β€’ Direct piping = Low memory usage β€’ Can handle 100+ concurrent streams β€’ Can add Cloudflare/CDN layer later for global reach βœ… Performance β€’ Direct piping = Fast streaming β€’ No buffering on server = Low latency β€’ Works with any HLS player ⚠️ Trade-off: Server bandwidth β€’ All video traffic goes through your server β€’ For high traffic, add CDN layer (Cloudflare) in front β€’ But for most apps, this is fine! ``` --- ## πŸ” Debugging Tips ``` When video is slow/not loading: ═══════════════════════════════════════════════════════════════════════════════ 1. Check server logs: β€’ Look for "Streaming file:" messages β€’ Check for errors in WasabiStorageService β€’ Verify uploadId exists in Wasabi 2. Check network tab in browser: β€’ Are requests returning 200 OK? β€’ Check Content-Type headers (should be video/mp2t for segments) β€’ Look at response times (should be fast in production) 3. Check Wasabi bucket: β€’ Verify files exist: hls/{uploadId}/master.m3u8 β€’ Check file sizes (master.m3u8 should be small, segments ~1-2 MB) 4. Test with curl: curl "http://localhost:5119/wasabi/hls?uploadId=1766933325669-42b7k6" # Should return rewritten m3u8 content 5. Check APP_URL in .env: β€’ Must match your actual domain β€’ Local: http://localhost:5119 β€’ Prod: https://yourdomain.com ``` --- ## πŸ“ Summary **Upload Flow:** Video β†’ FFmpeg HLS Conversion β†’ Upload to Wasabi β†’ Cleanup Local Files **Streaming Flow:** Browser β†’ Server (proxy) β†’ Wasabi β†’ Server β†’ Browser **URL Rewriting:** All playlists rewritten to point to server proxy endpoints **Performance:** - Local testing: Slow (limited by home internet upload speed) - Production: Fast (datacenter-to-datacenter speeds, 100x faster) **Key Insight:** Your slow local internet does NOT affect production performance! πŸš€