File preview and streaming
Last updated: May 19, 2026
File preview and streaming
Preview modal opens on file click — supports images (full-size view) and videos (streaming with seek). Backed by GET /api/v1/creative-hub/media/:fileId/thumbnail (verified apps/backend/src/routes/api/creative-hub-media.route.ts, 189 LOC). 3-tier fetch: in-memory cache → S3 cache → FFmpeg generation on-demand. HTTP Range requests supported for video seeking. No auth required on thumbnail endpoint (read-only; fileId acts as access token).
Who is this for
Anyone reviewing what they uploaded or generated. Also: anyone using the picker in ch-106.
How preview works
For images
Click file in grid → preview modal opens
Loads full-resolution image inline (or higher-res variant of thumbnail)
Pan / zoom via mouse / touch
Action buttons in modal: Download, Share, Use in Campaign, Delete
For videos
Click video → preview modal opens with player
Video streams progressively via HTTP Range requests (browser fetches byte ranges)
Seek to any timestamp without full download
Standard player controls: play / pause / mute / fullscreen
Thumbnail (poster frame) shown before play
The 3-tier thumbnail cache
To make preview fast for huge libraries, thumbnails (and video poster frames) flow through 3 cache layers:
Tier | Latency | What |
|---|---|---|
In-memory cache | < 10 ms | Hot recent thumbnails kept in app memory |
S3 cache | 50-200 ms | Persisted thumbnails for cold files |
FFmpeg generation | 5-30 sec | Generated on first request if cache miss |
Cache miss = first preview of a file may show "Generating thumbnail..." for a few seconds.
HTTP Range request support
The thumbnail endpoint accepts HTTP Range headers. Browsers use this to:
Request the first byte range (video header) for player initialization
Request progressive byte ranges as you scrub the timeline
Avoid downloading the full video file
For very large videos: scrubbing is responsive after initial 1-2 second buffer.
No auth on thumbnail endpoint
The thumbnail endpoint deliberately requires no auth header:
fileIditself is a unguessable UUID — knowing it is implicit accessLets the image / video tag fetch directly without Bearer token logic
Wevion's main file metadata endpoints (list / detail / delete) DO require auth — only the thumbnail render endpoint is open
This trade-off: rapid serving + simpler client code, accepted because file IDs are unguessable + the render is read-only.
Endpoint
GET /api/v1/creative-hub/media/:fileId/thumbnail (verified):
No auth header required
Supports
RangeheaderReturns image/jpeg or video/mp4 (depending on file type) with proper Content-Type + Content-Length + Accept-Ranges headers
Preview modal actions
Per file type:
Action | Image | Video |
|---|---|---|
Download | ✅ | ✅ |
Share | ✅ (via ch-105) | ✅ |
Use in Campaign | ✅ (via picker) | ✅ |
Delete | ✅ | ✅ |
Generate variant | ✅ (re-run AI) | ✅ (re-run AI) |
Rotate / crop | ⚠ (basic) | ❌ |
Quality + resolution
Image thumbnails: 320px / 640px variants (lower = smaller download for grid; higher = sharper for preview)
Image full preview: original resolution
Video poster: extracted from frame at t=1s by FFmpeg
Video stream: original encoding (MP4 H.264 typical; may not work in some browsers for exotic codecs)
For best playback compatibility: upload H.264 MP4. Other codecs may need transcode.
Common issues
"Loading thumbnail..." stays > 30 sec: FFmpeg failed (corrupted file, unsupported codec); re-upload or convert format
Video won't play: codec not supported by browser; convert to H.264 MP4
Image appears low-resolution in preview: hitting 640px thumbnail instead of full file; click "Download original" or check browser cache
Preview modal blank: file deleted on Drive but Wevion metadata still references it; refresh page; if persists, file is broken — re-upload
Slow scrubbing on long video: large file, slow network; expected behavior on first scrub
Related
Thumbnails — how generated — FFmpeg + cache details
Upload files — what to upload for best preview
Generate videos — videos created by AI also preview here