Skip to main content

Overview

Video Actions provide server-side functions for handling video uploads to S3, processing callbacks, and generating secure playback URLs. These are Convex actions that integrate with external services. All video action functions are defined in convex/videoActions.ts.

Upload Functions

getUploadUrl

Generates a presigned S3 upload URL for direct client-to-S3 uploads.
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

function VideoUploader({ videoId }) {
  const getUploadUrl = useAction(api.videoActions.getUploadUrl);
  
  const handleUpload = async (file: File) => {
    const { uploadUrl, s3Key } = await getUploadUrl({ videoId });
    
    // Upload directly to S3
    await fetch(uploadUrl, {
      method: 'PUT',
      body: file,
      headers: { 'Content-Type': file.type },
    });
    
    console.log('Uploaded to S3:', s3Key);
  };
  
  return <input type="file" onChange={(e) => handleUpload(e.target.files[0])} />;
}
videoId
Id<'videos'>
required
The video record to generate upload URL for
uploadUrl
string
Presigned S3 URL for PUT upload
s3Key
string
The S3 object key where the file will be stored
Permissions: Requires member role in the video’s team Implementation: convex/videoActions.ts:15

markUploadComplete

Marks a video upload as complete and triggers Mux processing.
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

async function finalizeUpload(videoId, s3Key) {
  const markComplete = useAction(api.videoActions.markUploadComplete);
  
  await markComplete({
    videoId,
    s3Key,
  });
  
  console.log('Video processing started');
}
videoId
Id<'videos'>
required
The video to mark as complete
s3Key
string
required
The S3 key where the video was uploaded
Permissions: Requires member role in the video’s team Implementation: convex/videoActions.ts:56

markUploadFailed

Marks a video upload as failed with an error message.
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

async function handleUploadError(videoId, error) {
  const markFailed = useAction(api.videoActions.markUploadFailed);
  
  await markFailed({
    videoId,
    error: error.message,
  });
}
videoId
Id<'videos'>
required
The video to mark as failed
error
string
required
Error message describing what went wrong
Permissions: Requires member role in the video’s team Implementation: convex/videoActions.ts:90

Playback Functions

getPlaybackUrl

Generates a signed Mux playback URL for team members.
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

function SecureVideoPlayer({ videoId }) {
  const getPlaybackUrl = useAction(api.videoActions.getPlaybackUrl);
  
  const loadVideo = async () => {
    const { playbackUrl, duration } = await getPlaybackUrl({ videoId });
    // Use playbackUrl with video player
  };
  
  return <button onClick={loadVideo}>Load Video</button>;
}
videoId
Id<'videos'>
required
The video to get playback URL for
playbackUrl
string
Signed Mux playback URL
duration
number | undefined
Video duration in seconds
Permissions: Requires viewer role in the video’s team Implementation: convex/videoActions.ts:122

getPlaybackSession

Generates a Mux playback session token for authenticated playback.
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

async function startPlayback(videoId) {
  const getSession = useAction(api.videoActions.getPlaybackSession);
  
  const { playbackId, token } = await getSession({ videoId });
  
  // Use with Mux Player
  return { playbackId, token };
}
videoId
Id<'videos'>
required
The video to create session for
playbackId
string
Mux playback ID
token
string
JWT token for signed playback
duration
number | undefined
Video duration in seconds
Permissions: Requires viewer role in the video’s team Implementation: convex/videoActions.ts:145

getPublicPlaybackSession

Generates a playback session for public videos (no authentication required).
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

function PublicPlayer({ publicId }) {
  const getSession = useAction(api.videoActions.getPublicPlaybackSession);
  
  const loadVideo = async () => {
    const session = await getSession({ publicId });
    if (!session) return; // Video not public
    
    // Use session.playbackId and session.token
  };
  
  return <button onClick={loadVideo}>Play</button>;
}
publicId
string
required
The public ID of the video
playbackId
string | null
Mux playback ID, or null if not found/not public
token
string | undefined
JWT token for signed playback
Permissions: Public - no authentication required Implementation: convex/videoActions.ts:185

getSharedPlaybackSession

Generates a playback session for videos accessed via share link.
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

function SharedPlayer({ grantToken }) {
  const getSession = useAction(api.videoActions.getSharedPlaybackSession);
  
  const loadVideo = async () => {
    const session = await getSession({ grantToken });
    if (!session) return; // Invalid grant
    
    return { playbackId: session.playbackId, token: session.token };
  };
  
  return <button onClick={loadVideo}>Play</button>;
}
grantToken
string
required
The share grant token
playbackId
string | null
Mux playback ID, or null if grant is invalid
token
string | undefined
JWT token for signed playback
Permissions: Public - requires valid share grant token Implementation: convex/videoActions.ts:215

getOriginalPlaybackUrl

Gets the original uploaded video URL (not Mux-processed).
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

async function getOriginal(videoId) {
  const getOriginalUrl = useAction(api.videoActions.getOriginalPlaybackUrl);
  
  const { url } = await getOriginalUrl({ videoId });
  
  return url; // S3 presigned download URL
}
videoId
Id<'videos'>
required
The video to get original URL for
url
string
Presigned S3 URL for downloading the original file
Permissions: Requires member role in the video’s team Implementation: convex/videoActions.ts:170

getDownloadUrl

Generates a download URL for videos (respects share link permissions).
import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';

function DownloadButton({ videoId }) {
  const getDownload = useAction(api.videoActions.getDownloadUrl);
  
  const handleDownload = async () => {
    const { url } = await getDownload({ videoId });
    window.location.href = url;
  };
  
  return <button onClick={handleDownload}>Download Video</button>;
}
videoId
Id<'videos'>
required
The video to generate download URL for
url
string
Presigned download URL
Permissions: Requires viewer role in the video’s team, or valid share grant with allowDownload Implementation: convex/videoActions.ts:245

Usage Patterns

Complete Upload Flow

import { useAction, useMutation } from 'convex/react';
import { api } from '../convex/_generated/api';

function CompleteUploadFlow({ projectId }) {
  const createVideo = useMutation(api.videos.create);
  const getUploadUrl = useAction(api.videoActions.getUploadUrl);
  const markComplete = useAction(api.videoActions.markUploadComplete);
  const markFailed = useAction(api.videoActions.markUploadFailed);
  
  const uploadVideo = async (file: File) => {
    try {
      // 1. Create video record
      const videoId = await createVideo({
        projectId,
        title: file.name,
        fileSize: file.size,
        contentType: file.type,
      });
      
      // 2. Get upload URL
      const { uploadUrl, s3Key } = await getUploadUrl({ videoId });
      
      // 3. Upload to S3
      const response = await fetch(uploadUrl, {
        method: 'PUT',
        body: file,
        headers: { 'Content-Type': file.type },
      });
      
      if (!response.ok) throw new Error('Upload failed');
      
      // 4. Mark complete to trigger Mux processing
      await markComplete({ videoId, s3Key });
      
      return videoId;
    } catch (error) {
      if (videoId) {
        await markFailed({ videoId, error: error.message });
      }
      throw error;
    }
  };
  
  return <input type="file" onChange={(e) => uploadVideo(e.target.files[0])} />;
}

Secure Playback with Session Tokens

import { useAction } from 'convex/react';
import { api } from '../convex/_generated/api';
import MuxPlayer from '@mux/mux-player-react';

function SecurePlayer({ videoId }) {
  const [session, setSession] = useState(null);
  const getSession = useAction(api.videoActions.getPlaybackSession);
  
  useEffect(() => {
    getSession({ videoId }).then(setSession);
  }, [videoId]);
  
  if (!session) return <div>Loading...</div>;
  
  return (
    <MuxPlayer
      playbackId={session.playbackId}
      tokens={{ playback: session.token }}
      metadata={{ video_title: 'Team Video' }}
    />
  );
}

Build docs developers (and LLMs) love