Text tracks & captions
Selecting tracks
player.getTextTracks(); // TextTrack[] (subtitle / caption tracks)
player.setTextTrack(id, 'showing'); // 'disabled' | 'hidden' | 'showing'
player.on('texttrackchange', ({ track }) => console.log(track.language));
The subtitles/CC control-bar button opens a native Video.js menu with the available languages plus "captions off".
Side-loaded WebVTT
SourceDescription.textTracks attaches WebVTT tracks that are not embedded
in the HLS/DASH manifest (subtitles, or a kind: 'chapters' cue file):
player.setSource({
sources: [{ src: 'https://example.com/video.m3u8', type: 'application/x-mpegURL' }],
textTracks: [
{ kind: 'chapters', src: 'https://example.com/chapters.vtt', srclang: 'en' },
{ kind: 'subtitles', src: 'https://example.com/subs.it.vtt', srclang: 'it', label: 'Italiano' },
],
});
Side-loaded tracks are cleared automatically on the next setSource(): no
manual cleanup when switching sources.
Validation
textTracks is checked before anything reaches Video.js. It must be an
array, and every entry must be an object with a non-empty src and a
kind from the TextTrackKind union (subtitles, captions,
descriptions, chapters, or metadata). Entries that fail (a bare URL string
passed instead of an array, a missing or unrecognised kind, a missing src)
are dropped with a console.warn and ignored. This matters because Video.js
silently rewrites an unknown kind to subtitles, which would otherwise show a
phantom language entry in the CC menu for a track that never loads.
The caption-settings dialog
By default the subs/CC menu also carries a "captions settings" entry that opens Video.js's text-track styling dialog (font, colour, background, edge style, opacity). The wrapper re-skins that dialog to match the EVE settings flyout (translucent dark card, flat controls, accent-filled Done, ghost Reset) and lays its six field groups out as aligned rows.
To remove the entry and the dialog entirely, pass captionSettings: false:
new EvePlayer(container, { captionSettings: false });
Under the hood this passes textTrackSettings: false to the Video.js
constructor, which drops the textTrackSettings component. Video.js only adds
the menu entry when that component exists, so it disappears with it.
Chapters
Chapter cues are read from a kind: 'chapters' text track on the current
source (embedded in the manifest or side-loaded above). They are kept
separate from getTextTracks() / setTextTrack() on purpose: chapters are
not a language choice, and mixing them in would leak a non-language entry into
any subtitle picker built on top.
player.getChapterCues(); // ChapterCue[] (all cues from the 'chapters' track)
player.on('chapterentercue', ({ cue }) => console.log(cue.text));
Whenever the current source has a chapters track with cues, Video.js's chapters control-bar button (the list icon) shows up on its own, next to the audio and subtitles menus. Its popup is hoisted to the player's bottom-right corner and skinned to match those menus (click-only, translucent dark panel). Clicking a row seeks to that chapter; the row for the chapter currently playing carries the accent checkmark.
Pass chapters: false to the constructor to remove the button even when a
chapters track is present; getChapterCues() and the chapterentercue event
are unaffected.
new EvePlayer(container, { chapters: false });
Chapters on the timeline
The same cues also mark up the progress bar. It is split into one segment per chapter, with a small gap at each boundary, and while the mouse hovers the bar the title of the chapter under it shows right above the time. When the source also has thumbnails, the thumbnail sits above the title.
This is on by default (chapterMarkers: true) and skipped on live streams. It
is independent of the chapters button, so either can be turned off alone:
// Chapters menu, but a plain progress bar:
new EvePlayer(container, { chapterMarkers: false });
The gap width is the --eve-chapter-gap CSS variable (default 2px, see
Theming).