The VideoPlayer component provides a full-featured media player with video/audio mode switching, playlist support, and keyboard controls.
VideoPlayer
The main video player component with playback controls and context.
Usage
import { VideoPlayer } from "@/components/video-player/VideoPlayer";
import { VideoPlayerOverlay } from "@/components/video-player/VideoPlayerOverlay";
import type { WatchListItem } from "@/context/playerContext";
interface Props {
watchListItem: WatchListItem;
background: boolean;
}
export function VideoPage({ watchListItem, background }: Props) {
return (
<VideoPlayer
watchListItem={watchListItem}
background={background}
overlay={<VideoPlayerOverlay {...props} />}
>
{/* Sidebar content */}
</VideoPlayer>
);
}
Props
Contains video, entry, and theme data:interface WatchListItem {
watchListId: string;
video: VideoSummaryCardVideoFragment;
entry: VideoSummaryCardEntryFragment;
}
Whether player is in background/mini mode
Sidebar content displayed next to video
Overlay content (typically VideoPlayerOverlay)
VideoPlayerContext
The component provides a context with player controls:
interface VideoPlayerContextValue {
video: VideoSummaryCardVideoFragment;
entry: VideoSummaryCardEntryFragment;
background: boolean;
videoPagePath: string;
playerRef: RefObject<HTMLVideoElement | HTMLAudioElement | null>;
progressRef: RefObject<HTMLDivElement | null>;
bufferedRef: RefObject<HTMLDivElement | null>;
previousVideoPath: string | null;
playPreviousTrack(navigate: boolean): void;
nextVideoPath: string | null;
playNextTrack(navigate: boolean): void;
isPlaying: boolean;
togglePlay(): void;
videoUrl: string;
audioUrl: string;
updateAudioMode(audioMode: string): void;
togglePip(): void;
}
Accessing Context
From /src/components/video-player/VideoPlayerBar.tsx:90:
import { VideoPlayerContext } from "@/components/video-player/VideoPlayer";
export function VideoPlayerBar() {
const context = useContext(VideoPlayerContext);
if (!context) {
throw new Error("VideoPlayerBar needs to be inside VideoPlayer!");
}
const {
isPlaying,
togglePlay,
playNextTrack,
playPreviousTrack
} = context;
// Use context values
}
Features
- Dual Mode: Switches between video and audio-only mode
- Playlist Support: Navigate between tracks in watch list
- Auto-play: Configurable auto-play for next track
- Keyboard Shortcuts: Full keyboard control
- Picture-in-Picture: PiP support for video mode
- Frame-accurate Seeking: Step through individual frames
- Media Session API: System media controls integration
Keyboard Shortcuts
From /src/components/video-player/VideoPlayer.tsx:323:
Seek forward 1 frame (when paused)
Seek backward 1 frame (when paused)
Toggle picture-in-picture
Source
Defined in /src/components/video-player/VideoPlayer.tsx:58
VideoPlayerBar
The control bar with playback controls and track information.
Usage
import { VideoPlayerBar } from "@/components/video-player/VideoPlayerBar";
// Automatically included in VideoPlayer
<VideoPlayer {...props}>
{/* VideoPlayerBar is rendered internally */}
</VideoPlayer>
Features
- Track Information: Song title, artist, anime name
- Playback Controls: Previous, play/pause, next buttons
- Volume Control: Slider and mute button
- Progress Bar: Seek through video
- Actions: Add to playlist, share, close player
Layout
From /src/components/video-player/VideoPlayerBar.tsx:23:
const StyledPlayerBar = styled(Solid)`
display: grid;
grid-template-columns: 1fr auto 1fr;
grid-gap: 16px;
align-items: center;
// Responsive: 2 columns on tablet
@media (max-width: ${theme.breakpoints.tabletMax}) {
grid-template-columns: 1fr auto;
}
`;
Source
Defined in /src/components/video-player/VideoPlayerBar.tsx:89
VideoPlayerOverlay
Overlay controls for settings and video options.
Usage
import { VideoPlayerOverlay } from "@/components/video-player/VideoPlayerOverlay";
import type { VideoPageProps } from "@/pages/anime/[animeSlug]/[videoSlug]";
export function VideoPage(props: VideoPageProps) {
return (
<VideoPlayer
overlay={<VideoPlayerOverlay {...props} />}
{...otherProps}
/>
);
}
Props
Features
- Add to Playlist: Quick playlist addition
- Keyboard Shortcuts Dialog: Shows all keyboard shortcuts
- Share Menu: Share video or audio
- Settings Menu: Switch between video/audio mode
- Version Selector: Choose different video versions
- Picture-in-Picture: Toggle PiP mode
- Fullscreen: Toggle fullscreen mode
Example from Overlay
From /src/components/video-player/VideoPlayerOverlay.tsx:124:
<StyledOverlay>
<Row style={{ "--gap": "16px" }}>
<PlaylistTrackAddDialog
video={video}
entry={{ ...entry, theme }}
trigger={<StyledOverlayButton icon={faPlus} isCircle />}
/>
<Dialog>
<DialogTrigger asChild>
<StyledOverlayButton icon={faKeyboard} isCircle />
</DialogTrigger>
<DialogContent title="Keyboard Shortcuts">
{/* Shortcuts list */}
</DialogContent>
</Dialog>
<ShareMenu {...props} />
<Menu>{/* Settings */}</Menu>
{isPIPSupported && <PiPButton />}
<FullscreenButton />
</Row>
</StyledOverlay>
Source
Defined in /src/components/video-player/VideoPlayerOverlay.tsx:90
VolumeControl
Volume slider with mute button.
Usage
import { VolumeControl } from "@/components/video-player/VolumeControl";
<VolumeControl />
Features
- Dynamic Icon: Changes based on volume level (high, low, off, muted)
- Hover Slider: Slider appears on hover
- Mute Toggle: Click icon to mute/unmute
- Global State: Syncs volume across all players using settings
Implementation
From /src/components/video-player/VolumeControl.tsx:29:
export function VolumeControl(props: ComponentPropsWithoutRef<typeof StyledRow>) {
const [volume, setVolume] = useSetting(GlobalVolume);
const [muted, setMuted] = useSetting(Muted);
let icon;
if (muted) {
icon = faVolumeXmark;
} else if (volume > 0.5) {
icon = faVolumeHigh;
} else if (volume > 0) {
icon = faVolumeLow;
} else {
icon = faVolumeOff;
}
return (
<StyledRow style={{ "--gap": "8px" }} {...props}>
<IconTextButton
icon={icon}
isCircle
onClick={() => setMuted(!muted)}
/>
<StyledSlider
value={[volume]}
onValueChange={([volume]) => {
setVolume(volume);
setMuted(false);
}}
min={0}
max={1}
step={0.01}
/>
</StyledRow>
);
}
Source
Defined in /src/components/video-player/VolumeControl.tsx:29
ProgressBar
Seekable progress bar for video playback (component available in /src/components/video-player/ProgressBar.tsx).
Complete Example
Full video player implementation:
import { VideoPlayer } from "@/components/video-player/VideoPlayer";
import { VideoPlayerOverlay } from "@/components/video-player/VideoPlayerOverlay";
import { Column } from "@/components/box/Flex";
import { ThemeDetailCard } from "@/components/card/ThemeDetailCard";
export default function VideoPage(props: VideoPageProps) {
const { anime, themeIndex, entryIndex, videoIndex } = props;
const theme = anime.themes[themeIndex];
const entry = theme.entries[entryIndex];
const video = entry.videos[videoIndex];
const watchListItem = {
watchListId: video.basename,
video,
entry: { ...entry, theme }
};
return (
<VideoPlayer
watchListItem={watchListItem}
background={false}
overlay={<VideoPlayerOverlay {...props} />}
>
<Column style={{ "--gap": "24px" }}>
{anime.themes.map((theme) => (
<ThemeDetailCard key={theme.slug} theme={theme} />
))}
</Column>
</VideoPlayer>
);
}