307 lines
7.1 KiB
Markdown
307 lines
7.1 KiB
Markdown
# Mux Video Integration
|
|
|
|
This project integrates [Mux](https://www.mux.com) 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
|
|
|
|
1. **Client requests upload URL** → Frontend calls our Supabase Edge Function
|
|
2. **Edge Function creates upload** → Calls Mux API to generate signed upload URL
|
|
3. **User uploads video** → Mux Uploader handles the upload with progress tracking
|
|
4. **Mux processes video** → Transcodes video, creates HLS stream, generates thumbnails
|
|
5. **Get playback ID** → Poll for asset creation, retrieve playback ID
|
|
6. **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
|
|
|
|
1. Sign up at [mux.com](https://www.mux.com)
|
|
2. Navigate to **Settings** → **Access Tokens**
|
|
3. Create a new access token with permissions:
|
|
- `Mux Video` - Read and Write
|
|
4. Copy the **Token ID** and **Token Secret**
|
|
|
|
### 2. Configure Environment Variables
|
|
|
|
Add these to your Supabase Edge Function environment variables:
|
|
|
|
```bash
|
|
MUX_TOKEN_ID=your_token_id_here
|
|
MUX_TOKEN_SECRET=your_token_secret_here
|
|
```
|
|
|
|
To set them in Supabase:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
supabase functions deploy mux-proxy
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Upload Video
|
|
|
|
```tsx
|
|
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:
|
|
|
|
```tsx
|
|
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:**
|
|
```json
|
|
{
|
|
"action": "create-upload"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"action": "get-upload",
|
|
"uploadId": "upload_abc123"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"id": "upload_abc123",
|
|
"status": "asset_created",
|
|
"asset_id": "asset_xyz789"
|
|
}
|
|
}
|
|
```
|
|
|
|
### get-asset
|
|
|
|
Get asset details including playback IDs.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"action": "get-asset",
|
|
"assetId": "asset_xyz789"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```sql
|
|
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 API
|
|
- `mux_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](https://docs.mux.com/guides/listen-for-webhooks).
|
|
|
|
## 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](https://www.mux.com/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
|
|
|
|
## Resources
|
|
|
|
- [Mux Documentation](https://docs.mux.com)
|
|
- [Mux Uploader Docs](https://www.mux.com/docs/guides/mux-uploader)
|
|
- [Vidstack Player Docs](https://vidstack.io)
|
|
- [Mux Dashboard](https://dashboard.mux.com)
|
|
|