Video Player

A player with a scrubber, quality and subtitle menus. Controls are real buttons on a real timeline, so the keyboard reaches every one of them — a div-based scrubber never does.

src/components/ui/video-player.tsx

About

From the component's own source, where it was written.

A video player with a real scrubber, quality and subtitle menus.

The scrubber is the kit's Slider — a range input underneath, not a div with a drag handler. That one decision buys arrow-key seeking, Home and End, screen-reader announcement of the position and a working focus ring, all of which a hand-rolled track has to reimplement and usually does not. It was a bare range input until recently, which meant the OS accent colour showed through on the one black bar in the product; the colour variables below point the shared component at the chrome's own ink instead.

Time is formatted from the media's own duration, so an hour-long video shows 1:02:15 and a clip shows 0:42 rather than 00:00:42. Padding every clip to hours is the giveaway of a player that formats blindly.

Subtitles default to off but the menu is always present. A player that hides the menu when only one track exists makes people believe there are none.

Install

npx astralyx-ui add video-player

Usage

import { VideoPlayer } from '@/components/ui/video-player'

<VideoPlayer src={src} poster={poster} qualities={qualities} subtitles={tracks} />

Composer

Change the props and the source below follows.

0:00 / 0:00Big Buck Bunny
Props
tsx
<VideoPlayer src={src} qualities={qualities} subtitles={tracks} />

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

VideoPlayer

Extends Omit<ComponentProps<'div'>, 'title'>.

PropTypeDefaultDescription
srcstringrequiredPassed to the underlying <video>. The poster is what fills the frame before play, instead of a black rectangle.
posterstring—Passed to the underlying <video>. The poster is what fills the frame before play, instead of a black rectangle.
qualitiesVideoTrack[]—Controlled: switching a rendition means changing the source, which is yours to do. Omit to hide the menu.
qualitystring—Controlled: switching a rendition means changing the source, which is yours to do. Omit to hide the menu.
onQualityChange(id: string) => void—Controlled: switching a rendition means changing the source, which is yours to do. Omit to hide the menu.
subtitlesVideoTrack[]—null is "off", which is a real selection and appears in the menu as one.
subtitlestring | null—null is "off", which is a real selection and appears in the menu as one.
onSubtitleChange(id: string | null) => void——
titleReactNode——
playLabelstring'Play'—
pauseLabelstring'Pause'—
muteLabelstring'Mute'—
unmuteLabelstring'Unmute'—
seekLabelstring'Seek'Accessible name for the scrubber.
settingsLabelstring'Playback settings'—
fullscreenLabelstring'Full screen'—
qualityLabelReactNode'Quality'Heading over the quality menu.
subtitlesLabelReactNode'Subtitles'Heading over the subtitle menu.
subtitlesOffLabelReactNode'Off'The "no subtitles" choice, which is a real selection.
progressLabel(at: string, total: string) => stringDEFAULT_PROGRESS_LABELSpoken position, given the formatted current time and duration.
displayhours when needed—A 40-minute video shows 12:04, a 2-hour one shows 1:12:04. Padding everything to hours wastes the width.

Functions

FunctionSignatureDescription
formatTime(seconds: number, showHours: boolean) => string—

Types

VideoTrack

A video player with a real scrubber, quality and subtitle menus.

FieldTypeDescription
idstring—
labelReactNode—

Behaviour

TopicDetailDescription
keyboardspace, arrows, m, fPlay/pause, seek, mute, fullscreen. The scrubber is a slider, so arrow keys seek from it too.