# Subtitles

> **TL;DR** — Every ticket lists the episode's subtitles: one WebVTT file per language, in `lang` order. An empty list means none yet, not an error. Native apps hand the URLs to the Player SDK or render the cues themselves; web players download the file and add the cues with `addTextTrack()`. Subtitle URLs outlive the ticket; when one stops working, take the current URL from the next ticket.

## Where they come from
| Field | Description |
|---|---|
| `subtitles[].lang` | Language code as delivered, for example `en`, `vi`, `pt-BR`. |
| `subtitles[].url` | Where to download the WebVTT file. |
| `subtitles[].format` | `webvtt` today. Skip a format you do not know. |

- Subtitles are not in the catalog; they come with each ticket. The languages present across your films are listed in `GET /config` (`subtitles.languages`).
- A subtitle attached or replaced on Wandura's side reaches tickets within 5 minutes.
- A replaced file gets a **new URL**, and the old one stops working. If a stored URL fails, take the current one from a fresh ticket.
- Subtitle URLs are public and long-lived: fine to cache per episode, not to publish.
- A subtitle that fails to load must never stop playback.

## Native apps
Two options, both covered in the portal's Player SDK guide: give the URLs to the SDK's external-subtitle mode, or download the `.vtt` and draw the cues over the player yourself. The [iOS](https://docs.wandura.ai/developers/player-sdk/ios) and [Android](https://docs.wandura.ai/developers/player-sdk/android) examples pass `ticket.subtitles` to one `addSubtitles` call.

## Web players
Do **not** add the `crossorigin` attribute to the `<video>` element so you can use a `<track>`: it changes how the video itself is requested and can stop it from playing. Download the file with `fetch` and add the cues yourself:

```ts
/** One text track per subtitle of a ticket. A file that fails is skipped: subtitles never block playback. */
export async function addSubtitles(video: HTMLVideoElement, subtitles: Subtitle[], show = 'en'): Promise<void> {
  for (const s of subtitles) {
    if (s.format !== 'webvtt') continue; // unknown format: skip it
    try {
      const res = await fetch(s.url);
      if (!res.ok) continue; // replaced since: the next ticket has the current URL
      const track = video.addTextTrack('subtitles', s.lang, s.lang);
      for (const c of parseVtt(await res.text())) track.addCue(new VTTCue(c.start, c.end, c.text));
      track.mode = s.lang === show ? 'showing' : 'hidden';
    } catch {
      // network error: play without this language
    }
  }
}
```

## Done when
- [ ] Subtitles appear, and switching language works.
- [ ] An episode without subtitles plays without an error.
- [ ] A subtitle URL that answers `404` is skipped, and playback continues.

## Related
- [Playback and tickets](https://docs.wandura.ai/developers/concepts/playback#what-the-ticket-contains)
