41 lines · 1.5 KB

Video

Adaptive HLS video served from R2 through the colo cache, with sprite-sheet scrubbing.

Prepare

Needs ffmpeg/ffprobe (libx264, AAC) and cwebp.

bun run video:prepare --source /path/to/video.mov --id intro --version v1 [--captions subs.srt --lang en]

Writes .artifacts/videos/<id>/<version>/: master.m3u8, one fMP4 rendition per rung (<height>/index.m3u8, init.mp4, NNNNN.m4s, 4 s segments, 2 s keyframes, no upscaling), poster.jpg, optional captions/<lang>.vtt (converted from SRT or VTT), and scrub/ (256x144 tiles, 10x10 per WebP sheet, one tile per second, at most 1000 tiles).

Upload

bun run video:upload --directory .artifacts/videos/intro/v1 [--bucket gitfor] [--local]

Objects land in the BUCKET R2 binding under videos/<id>/<version>/. Media goes first and master.m3u8 last; an already-published version is refused. Versions are immutable, so ship changes as a new version.

Delivery

src/server/videos.ts serves /videos/<id>/<version>/<asset>, only for the prepared layout. Responses are immutable and stored in the Workers cache on the first full fetch; later requests (including Range and If-None-Match) are answered from cache without touching R2.

Player

<VideoPlayer id="intro" version="v1" title="Intro" />

src/components/VideoPlayer.tsx uses hls.js (lazy-loaded) or native HLS on Safari. Hovering the timeline shows a sprite thumbnail; dragging fills the frame with sprite frames and seeks on release.