7.1 KiB
Mux Video Integration
This project integrates Mux for professional video upload, processing, and streaming capabilities.
Overview
Mux provides:
- Video Upload: Drag & drop or click to upload video files
- Automatic Processing: Videos are automatically transcoded and optimized
- HLS Streaming: Adaptive bitrate streaming for smooth playback
- Thumbnail Generation: Automatic thumbnails and poster images
- Analytics: Track video views and engagement (optional)
Architecture
Flow
- Client requests upload URL → Frontend calls our Supabase Edge Function
- Edge Function creates upload → Calls Mux API to generate signed upload URL
- User uploads video → Mux Uploader handles the upload with progress tracking
- Mux processes video → Transcodes video, creates HLS stream, generates thumbnails
- Get playback ID → Poll for asset creation, retrieve playback ID
- Play video → Use Vidstack player with Mux HLS stream URL
Components
- MuxUploader: React component for uploading videos (
@mux/mux-uploader-react) - VideoCard: Component for displaying videos with Vidstack player
- mux-proxy: Supabase Edge Function that interfaces with Mux API
Setup
1. Get Mux Credentials
- Sign up at mux.com
- Navigate to Settings → Access Tokens
- Create a new access token with permissions:
Mux Video- Read and Write
- Copy the Token ID and Token Secret
2. Configure Environment Variables
Add these to your Supabase Edge Function environment variables:
MUX_TOKEN_ID=your_token_id_here
MUX_TOKEN_SECRET=your_token_secret_here
To set them in Supabase:
# Using Supabase CLI
supabase secrets set MUX_TOKEN_ID=your_token_id
supabase secrets set MUX_TOKEN_SECRET=your_token_secret
# Or via Supabase Dashboard
# Project Settings → Edge Functions → Secrets
3. Deploy Edge Function
supabase functions deploy mux-proxy
Usage
Upload Video
import MuxUploader from "@mux/mux-uploader-react";
import { supabase } from "@/integrations/supabase/client";
const fetchUploadUrl = async () => {
const response = await fetch(
`${supabase.supabaseUrl}/functions/v1/mux-proxy`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${session.access_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ action: 'create-upload' }),
}
);
const { data } = await response.json();
return data.url;
};
function VideoUpload() {
return (
<MuxUploader
endpoint={fetchUploadUrl}
onSuccess={(event) => {
console.log('Upload complete!', event.detail);
}}
/>
);
}
Play Video
Once you have the playback ID from Mux, you can play the video:
import VideoCard from "@/components/VideoCard";
function VideoPlayer({ playbackId }: { playbackId: string }) {
const videoUrl = `https://stream.mux.com/${playbackId}.m3u8`;
const thumbnailUrl = `https://image.mux.com/${playbackId}/thumbnail.jpg`;
return (
<VideoCard
videoId="123"
videoUrl={videoUrl}
thumbnailUrl={thumbnailUrl}
title="My Video"
author="User"
authorId="user-id"
likes={0}
comments={0}
/>
);
}
Mux API Actions
create-upload
Creates a new direct upload URL.
Request:
{
"action": "create-upload"
}
Response:
{
"success": true,
"data": {
"id": "upload_abc123",
"url": "https://storage.googleapis.com/...",
"status": "waiting"
}
}
get-upload
Get the status of an upload and check if asset was created.
Request:
{
"action": "get-upload",
"uploadId": "upload_abc123"
}
Response:
{
"success": true,
"data": {
"id": "upload_abc123",
"status": "asset_created",
"asset_id": "asset_xyz789"
}
}
get-asset
Get asset details including playback IDs.
Request:
{
"action": "get-asset",
"assetId": "asset_xyz789"
}
Response:
{
"success": true,
"data": {
"id": "asset_xyz789",
"status": "ready",
"playback_ids": [
{
"id": "playback_def456",
"policy": "public"
}
]
}
}
Database Schema
Store Mux video data in your videos table:
CREATE TABLE videos (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL,
title TEXT NOT NULL,
description TEXT,
video_url TEXT NOT NULL, -- https://stream.mux.com/{playback_id}.m3u8
thumbnail_url TEXT, -- https://image.mux.com/{playback_id}/thumbnail.jpg
meta JSONB, -- { mux_asset_id, mux_playback_id }
created_at TIMESTAMP DEFAULT NOW()
);
Store in meta:
mux_asset_id: For managing the asset via Mux APImux_playback_id: For generating stream/thumbnail URLs
Mux URLs
Stream URL (HLS)
https://stream.mux.com/{PLAYBACK_ID}.m3u8
This is an HLS stream that works with Vidstack, Mux Player, and most video players.
Thumbnail URL
https://image.mux.com/{PLAYBACK_ID}/thumbnail.jpg
Query parameters:
?width=1280- Set width?height=720- Set height?time=10- Thumbnail at 10 seconds
MP4 URL (if enabled)
https://stream.mux.com/{PLAYBACK_ID}/high.mp4
Available qualities: low.mp4, medium.mp4, high.mp4
Webhooks (Optional)
For production, set up Mux webhooks to get notified when:
- Upload completes (
video.upload.asset_created) - Video is ready (
video.asset.ready) - Errors occur (
video.asset.errored)
This is more efficient than polling. See Mux Webhooks Docs.
Playground
Test the integration at /playground/video-player:
- Upload tab: Upload videos using Mux
- Test with URL tab: Test Vidstack player with any video URL
Pricing
Mux charges based on:
- Encoding: Minutes of video processed
- Streaming: Minutes of video delivered
- Storage: GB-months of video stored
See Mux Pricing for current rates.
Free tier includes:
- $20/month in free credits
- Enough for ~40 minutes of encoding + 100 hours of streaming
Troubleshooting
Upload fails immediately
- Check that MUX_TOKEN_ID and MUX_TOKEN_SECRET are set in Supabase
- Verify the edge function is deployed
- Check browser console for CORS errors
Video stuck in "processing"
- Large videos can take several minutes to process
- Check Mux dashboard for asset status
- Verify the upload completed successfully
Video won't play
- Check that playback policy is set to "public"
- Verify the HLS URL format is correct
- Check browser console for player errors