Video Upload & Streaming
Overview
Exercise videos are stored on a CDN and delivered via adaptive bitrate streaming (HLS). The platform uses a provider-agnostic design to support multiple CDN providers without schema changes.
Supported Providers
Bunny Stream (Recommended)
Bunny Stream is an EU-first video hosting service with built-in HLS transcoding.
Flow:
- Admin uploads video via
POST /v1/exercises/{id}/video - Backend uploads to Bunny Stream via their Upload API
- Bunny automatically transcodes to multiple resolutions (360p, 480p, 720p, 1080p)
- Bunny generates HLS manifest (
.m3u8) + thumbnail - Backend stores the HLS URL, thumbnail URL, and duration
Advantages:
- EU-first (GDPR-friendly data residency)
- Automatic HLS transcoding (no pipeline to build)
- Built-in CDN delivery
- DRM support available
- Webhook for transcoding completion
Configuration:
BUNNY_STREAM_API_KEY=...
BUNNY_STREAM_LIBRARY_ID=...
BUNNY_STREAM_CDN_HOSTNAME=...S3 + CloudFront (Alternative)
For organizations preferring AWS infrastructure:
Flow:
- Admin uploads video via
POST /v1/exercises/{id}/video - Backend uploads original to S3
- AWS MediaConvert transcodes to HLS (triggered by S3 event)
- Transcoded files stored in S3, served via CloudFront
- Backend stores the CloudFront HLS URL
Configuration:
AWS_S3_BUCKET=...
AWS_MEDIACONVERT_ROLE=...
AWS_CLOUDFRONT_DOMAIN=...Upload Flow
Step 1: Client Upload
Client Core API CDN Provider
│ │ │
│ POST /exercises/{id}/video │
│ Content-Type: multipart │ │
│─────────────────────────>│ │
│ │ │
│ │ Upload video file │
│ │───────────────────────────>│
│ │ │
│ │ Upload ID / Video ID │
│ │<───────────────────────────│
│ │ │
│ 202 Accepted │ │
│ { "status": "processing" } │
│<─────────────────────────│ │Step 2: Transcoding (Async)
CDN Provider Core API (Webhook)
│ │
│ Transcoding complete │
│ POST /webhooks/video-ready │
│───────────────────────────>│
│ │
│ │ Update exercise:
│ │ video_url = HLS URL
│ │ video_thumbnail_url = thumb URL
│ │ video_duration_seconds = duration
│ │
│ 200 OK │
│<───────────────────────────│Step 3: Client Playback
The frontend uses the HLS URL for adaptive bitrate playback:
// Using hls.js (browser) or native HLS (Safari/iOS)
const video = document.getElementById('exercise-video');
const hls = new Hls();
hls.loadSource(exercise.video_url); // .m3u8 URL
hls.attachMedia(video);The player automatically selects the best quality based on the patient's network bandwidth.
S3 Key Structure
Org-Scoped Exercises
s3://{bucket}/{org_id}/exercises/{exercise_id}/
├── video/
│ └── original.mp4 # uploaded file (archived)
├── thumbnail/
│ └── poster.jpg # auto-generated or uploaded
└── instructions/
├── 0/image.jpg # instruction step 0 image
├── 1/image.jpg # instruction step 1 image
└── 2/image.jpg # instruction step 2 imageGlobal Exercises (Superadmin)
s3://{bucket}/global/exercises/{exercise_id}/
├── video/
│ └── original.mp4
├── thumbnail/
│ └── poster.jpg
└── instructions/
└── ...Video Processing Status
The exercise tracks video processing state via the API response:
{
"video_url": null,
"video_status": "processing",
"video_provider": "bunny_stream"
}Possible states:
null— No video uploadedprocessing— Upload complete, transcoding in progress- Ready —
video_urlis populated with the HLS manifest URL
The frontend polls or receives a push notification when transcoding completes.
File Size Limits
| Type | Max Size | Formats |
|---|---|---|
| Exercise video | 500 MB | MP4, MOV, WebM |
| Video thumbnail | 5 MB | JPG, PNG, WebP |
| Instruction image | 10 MB | JPG, PNG, WebP, SVG |
Bandwidth Considerations for Patients
Exercise videos are watched during telerehab sessions, often on mobile networks. The HLS adaptive streaming ensures:
- Low bandwidth: 360p stream (~500 Kbps)
- Medium bandwidth: 720p stream (~2 Mbps)
- High bandwidth: 1080p stream (~5 Mbps)
The Telemetry service monitors bandwidth during sessions via GET /v1/media/bandwidth/stats to detect quality issues and alert the patient or specialist.
Security
- Signed URLs: Videos are served via signed CDN URLs with expiration (1 hour)
- Token-based access: For Bunny Stream, pull zone token authentication ensures only authenticated users can access videos
- No public URLs: Video URLs are never exposed publicly; they require an authenticated API call to obtain
- CORS: CDN configured to allow requests only from the platform's frontend domains