File preview and streaming

Preview modal images + videos. Thumbnail: GET /api/v1/creative-hub/thumbnail/:fileId (image bytes only). Video content via /files/:fileId/content. In-memory + S3 cache. No auth on thumbnail.

Written By Salvatore Sinigaglia

Last updated About 4 hours ago

Preview modal images + videos. Thumbnail: GET /api/v1/creative-hub/thumbnail/:fileId (image bytes only). Video content via /files/:fileId/content. In-memory + S3 cache. No auth on thumbnail.

File preview and streaming

Preview modal opens on file click — supports images and videos. The thumbnail/poster endpoint is GET /api/v1/creative-hub/thumbnail/:fileId (verified apps/backend/src/routes/api/creative-hub-media.route.ts), which returns raw image bytes only (image/jpeg or image/png) — it does not stream video or support HTTP Range. Video bytes are served separately by the file-content endpoint (GET /api/v1/creative-hub/files/:fileId/content). Thumbnail fetch: in-memory cache → S3 cache (videos) → Drive thumbnail link (images), with an FFmpeg-generated poster cached in S3 for videos. No auth required on the 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

  1. Click file in grid → preview modal opens
  2. Loads full-resolution image inline (or higher-res variant of thumbnail)
  3. Pan / zoom via mouse / touch
  4. Action buttons in modal: Download, Share, Use in Campaign, Delete

For videos

  1. Click video → preview modal opens with player
  2. The <video> tag streams the file from the content endpoint (/files/:fileId/content)
  3. Seek/scrub via the browser player
  4. Standard player controls: play / pause / mute / fullscreen
  5. A poster frame (FFmpeg still at t=0.5s, from the thumbnail endpoint) is shown before play

The thumbnail cache

To make preview fast for huge libraries, thumbnails (and video poster frames) flow through cache layers:

LayerLatencyWhat
In-memory cache< 10 msHot recent thumbnails kept in app memory
S3 cache (videos)50-200 msPersisted video poster frames
SourcevariesImages: proxied from the Drive thumbnail link. Videos with no cached poster: FFmpeg generates one (then cached in S3)

Cache miss on a video = first preview may take a few seconds while the poster is generated.

Video streaming (separate endpoint)

The thumbnail endpoint returns only a still image — it does not stream video or support HTTP Range. Video playback in the preview modal streams the file bytes from the separate content endpoint (GET /api/v1/creative-hub/files/:fileId/content). The browser's <video> tag handles seeking/scrubbing against that endpoint.

No auth on thumbnail endpoint

The thumbnail endpoint deliberately requires no auth header:

  • fileId itself is a unguessable UUID — knowing it is implicit access
  • Lets 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/thumbnail/:fileId (verified):

  • No auth header required
  • Returns image bytes only (image/jpeg or image/png) — no video, no Range support
  • Sets Cache-Control: public, max-age=86400 and a cross-origin resource policy header

Video bytes are served by a different endpoint: GET /api/v1/creative-hub/files/:fileId/content.

Preview modal actions

Per file type:

ActionImageVideo
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: proxied from Drive's thumbnail link (up to ~800px)
  • Image full preview: original resolution
  • Video poster: a single frame extracted by FFmpeg at t=0.5s, scaled to 400px wide, cached in S3
  • Video stream: original encoding served from the content endpoint (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

FAQ

Why does my first preview show "Generating thumbnail..." for a few seconds?

That is a cache miss on the first preview of a video. Wevion serves thumbnails from an in-memory cache (under 10 ms) and, for videos, an S3 cache (50-200 ms); on a miss it generates the video poster with FFmpeg (a few seconds) and stores it to S3. Images are proxied straight from Drive's thumbnail link. The first video request generates and stores the poster, so subsequent previews are fast. If it stays stuck, FFmpeg likely failed on a corrupted file or unsupported codec.

How can I preview a long video without downloading the whole file?

Video playback uses the browser <video> tag against the content endpoint (/files/:fileId/content), which handles seeking/scrubbing. Note the separate thumbnail endpoint only returns a still poster image — it does not stream video or support HTTP Range. Standard player controls — play, pause, mute, fullscreen — are available in the preview modal.

Why does the thumbnail endpoint not require authentication?

The GET /api/v1/creative-hub/thumbnail/:fileId endpoint is deliberately open so image tags can fetch directly without Bearer-token logic. The fileId acts as an implicit access token, and the render is read-only (image bytes only). Wevion's file metadata endpoints — list, detail, delete — still require auth; only the thumbnail render endpoint is open.

My video won't play in the preview modal — what's wrong?

The video's codec is likely not supported by your browser. Wevion streams the original encoding (typically MP4 H.264), and exotic codecs may not play. Convert the file to H.264 MP4 for best compatibility. Uploading H.264 MP4 in the first place gives the most reliable playback across browsers.

FAQ

Why does my first preview show "Generating thumbnail..." for a few seconds?

That is a cache miss on the first preview of a video. Wevion serves thumbnails from an in-memory cache (under 10 ms) and, for videos, an S3 cache (50-200 ms); on a miss it generates the video poster with FFmpeg (a few seconds) and stores it to S3. Images are proxied straight from Drive's thumbnail link. The first video request generates and stores the poster, so subsequent previews are fast. If it stays stuck, FFmpeg likely failed on a corrupted file or unsupported codec.

How can I preview a long video without downloading the whole file?

Video playback uses the browser <video> tag against the content endpoint (/files/:fileId/content), which handles seeking/scrubbing. Note the separate thumbnail endpoint only returns a still poster image — it does not stream video or support HTTP Range. Standard player controls — play, pause, mute, fullscreen — are available in the preview modal.

Why does the thumbnail endpoint not require authentication?

The GET /api/v1/creative-hub/thumbnail/:fileId endpoint is deliberately open so image tags can fetch directly without Bearer-token logic. The fileId acts as an implicit access token, and the render is read-only (image bytes only). Wevion's file metadata endpoints — list, detail, delete — still require auth; only the thumbnail render endpoint is open.

My video won't play in the preview modal — what's wrong?

The video's codec is likely not supported by your browser. Wevion streams the original encoding (typically MP4 H.264), and exotic codecs may not play. Convert the file to H.264 MP4 for best compatibility. Uploading H.264 MP4 in the first place gives the most reliable playback across browsers.

Last updated: 2026-05-17