R2 StreamReady is a single Cloudflare Worker you deploy to your own account. When a video lands in an R2 bucket, the Worker hands it to Cloudflare Stream for transcoding. When Stream finishes, the Worker copies the whole HLS package (master playlist, every quality level, audio, init sections and segments) back into R2 and, by default, deletes the video from Stream. You get Stream’s encoding and keep the output as plain files you own, to serve from an R2 custom domain, mirror anywhere or archive.
Parts: Your app (uploads a video), R2 source (event notifications), Queue (retries · DLQ), Worker (idempotent steps), Stream (encodes the ladder), R2 HLS package (master.m3u8 last).
- Your app to R2 source (upload)
- R2 source to Queue (event)
- Queue to Worker
- Worker to Stream
- Worker to R2 HLS package (copies files)
Design
It uses R2 event notifications, Queues, the Stream Workers binding and a cron trigger, and no database: job state lives in R2 as a status.json next to the output. Every step is idempotent. Retries back off, failed messages go to a dead-letter queue, and a cron watchdog re-drives stuck jobs. The Worker holds no Stream API token; it uses the binding, and hands Stream an HMAC-signed link to the source. Copying is split into queue messages of a few dozen files so each invocation stays inside Workers limits, and master.m3u8 is written last, so a player never sees a half-copied package.
- Your app to R2: upload talks/intro.mp4
- R2 to Queue: object-created event
- Queue to Worker: deliver the event
- Worker to Stream: copy from an HMAC-signed link
- Stream to Worker: fetch the original
- Worker to R2: status.json: uploading
- Worker to Queue: poll later
- Stream to Worker: ready webhook (optional)
- Worker to Stream: read every playlist
- Worker to Queue: mirror slices of 40 files
- Worker to R2: copy segments + playlists
- Worker to R2: master.m3u8 last, then done
- Worker to Stream: delete the Stream copy
Re-uploading a file with the same name produces a new version folder and switches the master playlist over atomically. It comes with a one-command installer, a backfill CLI for existing buckets, an uninstall, and tests.
- hls/talks/intro/
- master.m3u8 point your player here
- status.json state, progress, errors
- a169…92e0/ this version, never modified
- 1080p/
- index.m3u8
- init.mp4
- seg_00001 … 00005.mp4
- 720p/ same files
- 480p/ same files
- 360p/ same files
- 240p/ same files
- audio-en/ AAC
- 1080p 1920×1080
- 720p 1280×720
- 480p 854×480
- 360p 640×360
- 240p 426×240
Players switch rungs by bandwidth while playing. Every rung shares the same audio track.