Captions & Subtitles

Add subtitles to videos — upload VTT/SRT files manually or auto-generate with AI. Captions appear in the Plyr player with styled rendering and language selection.

Two ways to add captions

  • Upload — Upload .vtt or .srt files (SRT auto-converted to VTT)
  • AI Generate — AI creates timed captions from video metadata + chapters

1. Upload Captions

API Endpoint

bash
POST /api/mediakit/videos/:id/captions
Content-Type: multipart/form-data
Authorization: Bearer <jwt_token>

# Form fields:
file          = subtitles.vtt    # .vtt or .srt file (required)
language      = English          # Display name (default: "English")
language_code = en               # ISO 639-1 code (default: "en")
is_default    = true             # Show by default in player

cURL Example

bash
# Upload English subtitles
curl -X POST https://mediakitapi.gritcms.com/api/mediakit/videos/8/captions \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@subtitles.vtt" \
  -F "language=English" \
  -F "language_code=en" \
  -F "is_default=true"

# Upload Spanish subtitles
curl -X POST https://mediakitapi.gritcms.com/api/mediakit/videos/8/captions \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@subtitulos.srt" \
  -F "language=Español" \
  -F "language_code=es"

JavaScript / Next.js

Upload captions from your app
async function uploadCaptions(videoId: number, file: File, language: string, code: string) {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('language', language)
  formData.append('language_code', code)
  formData.append('is_default', 'true')

  const res = await fetch(
    `${MEDIAKIT_URL}/api/mediakit/videos/${videoId}/captions`,
    {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${token}` },
      body: formData,
    }
  )

  const data = await res.json()
  console.log('Caption created:', data.data.id)
}

SRT Format (auto-converted)

Upload .srt files and they're automatically converted to WebVTT. Both formats are stored.

subtitles.srt
1
00:00:00,000 --> 00:00:03,500
Welcome to this tutorial

2
00:00:03,500 --> 00:00:07,000
Today we'll cover video streaming

3
00:00:07,000 --> 00:00:11,500
Let's start with the upload pipeline

WebVTT Format (native)

subtitles.vtt
WEBVTT

00:00:00.000 --> 00:00:03.500
Welcome to this tutorial

00:00:03.500 --> 00:00:07.000
Today we'll cover video streaming

00:00:07.000 --> 00:00:11.500
Let's start with the upload pipeline

2. AI-Generated Captions

Generate captions automatically using AI. The model creates timed WebVTT content based on the video's title, description, duration, and chapter markers (if available).

bash
POST /api/ai/videos/:id/analyse
Content-Type: application/json
Authorization: Bearer <jwt_token>

{ "type": "captions" }
Trigger AI caption generation
// From your backend or admin panel
const res = await fetch(
  `${MEDIAKIT_URL}/api/ai/videos/${videoId}/analyse`,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
    },
    body: JSON.stringify({ type: 'captions' }),
  }
)
// Captions are generated, saved as VTT, and linked to the video
// They'll appear in the player automatically on next load

Note on AI captions

AI-generated captions are based on video metadata (title, description, chapters) — not actual speech transcription. For real speech-to-text from audio, integrate OpenAI Whisper or AssemblyAI and upload the resulting VTT file.

3. Player Integration

Captions are automatically loaded from the playback API and rendered in the Plyr player. No extra code needed — just upload captions and they appear.

Captions just work
import { PlyrPlayer } from '@mediakit-dev/react'

// Captions load automatically from the playback API
<PlyrPlayer videoId="8" captions={true} />

// The player shows:
// - CC button in controls bar
// - Settings → Captions → language selector
// - Styled subtitle overlay on the video

Caption Styling

Captions are styled with a modern, readable appearance:

  • Dark semi-transparent background (rgba(0,0,0,0.8))
  • System sans-serif font, weight 500
  • Max width 80%, auto-centered
  • Responsive sizing — larger in fullscreen
  • No text shadow (cleaner than default)
  • 4px border radius on caption boxes

Override styles with CSS targeting .plyr__caption:

Custom caption styling
/* Yellow captions like Netflix */
.mk-plyr-wrapper .plyr__caption {
  background: transparent !important;
  color: #ffd700 !important;
  text-shadow: 2px 2px 4px rgba(0,0,0,0.9) !important;
  font-size: 1.2rem !important;
}

/* Larger font for accessibility */
.mk-plyr-wrapper .plyr__caption {
  font-size: 1.3rem !important;
  line-height: 1.6 !important;
}

/* Karaoke-style highlighting */
.mk-plyr-wrapper video::cue {
  background: rgba(108,92,231,0.9);
  color: #fff;
}

4. API Reference

MethodEndpointAuthDescription
POST/api/mediakit/videos/:id/captionsJWTUpload VTT/SRT file
GET/api/mediakit/videos/:id/captionsPublicList captions for a video
DELETE/api/mediakit/captions/:idJWTDelete a caption
POST/api/ai/videos/:id/analyseJWTAI generate (type=captions)

5. Multiple Languages

Upload captions in multiple languages
// English (default)
await uploadCaptions(videoId, enFile, 'English', 'en')

// Spanish
await uploadCaptions(videoId, esFile, 'Español', 'es')

// French
await uploadCaptions(videoId, frFile, 'Français', 'fr')

// Japanese
await uploadCaptions(videoId, jaFile, '日本語', 'ja')

// In the player: Settings → Captions → select language
// The player automatically shows all available tracks

Playback API Response

json
{
  "data": {
    "id": 8,
    "title": "Getting Started Tutorial",
    "hls_url": "https://mediakitapi.gritcms.com/api/storage/videos/8/hls/index.m3u8",
    "captions": [
      {
        "id": 1,
        "language": "English",
        "language_code": "en",
        "vtt_url": "https://mediakitapi.gritcms.com/api/storage/videos/8/captions/en.vtt",
        "is_default": true,
        "ai_generated": false,
        "source": "upload"
      },
      {
        "id": 2,
        "language": "Español",
        "language_code": "es",
        "vtt_url": "https://mediakitapi.gritcms.com/api/storage/videos/8/captions/es.vtt",
        "is_default": false,
        "ai_generated": false,
        "source": "upload"
      }
    ]
  }
}