"use client" import * as React from "react" import { MediaPlayer, MediaProvider, Poster, Track, type MediaPlayerInstance, } from "@vidstack/react" import { DefaultAudioLayout, DefaultVideoLayout, defaultLayoutIcons, } from "@vidstack/react/player/layouts/default" import "@vidstack/react/player/styles/default/theme.css" import "@vidstack/react/player/styles/default/layouts/audio.css" import "@vidstack/react/player/styles/default/layouts/video.css" import { cn } from "@/lib/utils" export type MediaTextTrack = Pick< React.ComponentProps, "src" | "kind" | "language" | "label" | "default" > & { id?: React.Key } export type MediaPlayerStateContent = { loading?: React.ReactNode error?: React.ReactNode empty?: React.ReactNode } type MediaPlayerRootProps = Omit< React.ComponentPropsWithoutRef, "children" | "viewType" > type MediaPlayerCompositionProps = { tracks?: MediaTextTrack[] providerChildren?: React.ReactNode beforeLayout?: React.ReactNode afterLayout?: React.ReactNode layout?: React.ReactNode | false overlay?: React.ReactNode overlayClassName?: string showStateOverlay?: boolean stateContent?: MediaPlayerStateContent providerClassName?: string } export type VideoPlayerProps = MediaPlayerRootProps & MediaPlayerCompositionProps & { poster?: string posterAlt?: string thumbnails?: string aspectRatio?: React.CSSProperties["aspectRatio"] layoutProps?: React.ComponentProps posterClassName?: string } export type AudioPlayerProps = MediaPlayerRootProps & MediaPlayerCompositionProps & { layoutProps?: React.ComponentProps } function hasMediaSource(src: MediaPlayerRootProps["src"]) { if (src == null) return false if (typeof src === "string") return src.trim().length > 0 if (Array.isArray(src)) return src.length > 0 return true } function renderTracks(tracks: MediaTextTrack[]) { return tracks.map((track) => ( )) } function DefaultLoadingState({ tone }: { tone: "video" | "audio" }) { return (
) } function DefaultErrorState({ tone }: { tone: "video" | "audio" }) { return (

Media could not be loaded

Check the source, network connection, provider configuration, and CORS headers.

) } function DefaultEmptyState({ tone }: { tone: "video" | "audio" }) { return (

No media source

Pass a valid source or source collection to render the player.

) } function PlayerStateOverlay({ hasSource, tone, content, showLoading, }: { hasSource: boolean tone: "video" | "audio" content?: MediaPlayerStateContent showLoading: boolean }) { const surfaceClass = tone === "video" ? "bg-black/55 text-white" : "bg-background/95 text-foreground" if (!hasSource) { return (
{content?.empty ?? }
) } return ( <> {showLoading ? (
{content?.loading ?? }
) : null} ) } const VideoPlayer = React.forwardRef(function VideoPlayer( { poster, posterAlt = "Video poster", thumbnails, tracks = [], layoutProps, providerChildren, beforeLayout, afterLayout, layout, overlay, overlayClassName, showStateOverlay = true, stateContent, className, providerClassName, posterClassName, aspectRatio = "16 / 9", playsInline = true, load = "visible", src, title, style, "aria-label": ariaLabel, ...props }, ref ) { const hasSource = hasMediaSource(src) const { icons = defaultLayoutIcons, thumbnails: layoutThumbnails, ...resolvedLayoutProps } = layoutProps ?? {} return ( {poster ? ( ) : null} {renderTracks(tracks)} {providerChildren} {overlay ? (
{overlay}
) : null} {showStateOverlay ? ( ) : null} {beforeLayout} {layout === false ? null : layout ?? ( )} {afterLayout}
) }) VideoPlayer.displayName = "VideoPlayer" const AudioPlayer = React.forwardRef(function AudioPlayer( { tracks = [], layoutProps, providerChildren, beforeLayout, afterLayout, layout, overlay, overlayClassName, showStateOverlay = true, stateContent, className, providerClassName, load = "visible", src, title, "aria-label": ariaLabel, ...props }, ref ) { const hasSource = hasMediaSource(src) const { icons = defaultLayoutIcons, ...resolvedLayoutProps } = layoutProps ?? {} return ( {renderTracks(tracks)} {providerChildren} {overlay ? (
{overlay}
) : null} {showStateOverlay ? ( ) : null} {beforeLayout} {layout === false ? null : layout ?? } {afterLayout}
) }) AudioPlayer.displayName = "AudioPlayer" export { AudioPlayer, VideoPlayer } export type { MediaPlayerInstance }