Files
player/src/components/VideoPlayer.tsx
T
hibna 183cc65455 .
2025-10-30 04:02:31 +03:00

175 lines
4.8 KiB
TypeScript

import React, { useEffect, useState, useCallback } from 'react'
import { PlayerProvider, usePlayerContext } from '../contexts/PlayerContext'
import { VideoElement } from './VideoElement'
import { ControlsLayer } from './ControlsLayer'
import type { VideoPlayerProps, AudioTrack, VideoQuality } from '../types'
import '../styles/variables.css'
import './VideoPlayer.css'
// Lazy load polyfills only if needed
let polyfillsInitialized = false
const initializePolyfillsIfNeeded = async () => {
if (polyfillsInitialized) return
// Check if polyfills are needed
const needsFullscreenPolyfill = !document.fullscreenEnabled && !(document as any).webkitFullscreenEnabled
const needsPIPPolyfill = !('pictureInPictureEnabled' in document)
if (needsFullscreenPolyfill || needsPIPPolyfill) {
const { initializePolyfills } = await import('../utils/polyfills')
initializePolyfills()
polyfillsInitialized = true
}
}
// Initialize polyfills asynchronously
initializePolyfillsIfNeeded()
const VideoPlayerContent: React.FC<
VideoPlayerProps & {
audioTracks: AudioTrack[]
onAudioTracksLoadedInternal: (tracks: AudioTrack[]) => void
qualities: VideoQuality[]
onQualityLevelsLoadedInternal: (qualities: VideoQuality[]) => void
}
> = ({
src,
poster,
autoplay = false,
loop = false,
muted = false,
controls = true,
subtitles = [],
keyboardShortcuts = true,
pictureInPicture = true,
className = '',
style,
onPlay,
onPause,
onEnded,
onTimeUpdate,
onVolumeChange,
onError,
onLoadedMetadata,
onSeeking,
onSeeked,
audioTracks,
onAudioTracksLoadedInternal,
qualities,
onQualityLevelsLoadedInternal,
}) => {
const { containerRef, uiState } = usePlayerContext()
const controlsHiddenClass = !uiState.controlsVisible ? 'controls-hidden' : ''
return (
<div ref={containerRef} className={`video-player ${controlsHiddenClass} ${className}`} style={style}>
<VideoElement
src={src}
poster={poster}
autoplay={autoplay}
loop={loop}
muted={muted}
subtitles={subtitles}
onPlay={onPlay}
onPause={onPause}
onEnded={onEnded}
onTimeUpdate={onTimeUpdate}
onVolumeChange={onVolumeChange}
onError={onError}
onLoadedMetadata={onLoadedMetadata}
onSeeking={onSeeking}
onSeeked={onSeeked}
onAudioTracksLoaded={onAudioTracksLoadedInternal}
onQualityLevelsLoaded={onQualityLevelsLoadedInternal}
/>
{controls && (
<ControlsLayer
keyboardShortcuts={keyboardShortcuts}
pictureInPicture={pictureInPicture}
subtitles={subtitles}
audioTracks={audioTracks}
qualities={qualities}
/>
)}
</div>
)
}
export const VideoPlayer: React.FC<VideoPlayerProps> = ({
src,
poster,
autoplay = false,
loop = false,
muted = false,
controls = true,
subtitles = [],
theme,
language,
keyboardShortcuts = true,
pictureInPicture = true,
className = '',
style,
onPlay,
onPause,
onEnded,
onTimeUpdate,
onVolumeChange,
onError,
onLoadedMetadata,
onSeeking,
onSeeked,
}) => {
const [audioTracks, setAudioTracks] = useState<AudioTrack[]>([])
const [qualities, setQualities] = useState<VideoQuality[]>([])
// Apply theme CSS variables
useEffect(() => {
if (theme) {
const root = document.documentElement
if (theme.primaryColor) root.style.setProperty('--player-primary', theme.primaryColor)
if (theme.accentColor) root.style.setProperty('--player-primary-hover', theme.accentColor)
if (theme.backgroundColor) root.style.setProperty('--player-bg', theme.backgroundColor)
if (theme.textColor) root.style.setProperty('--player-text', theme.textColor)
}
}, [theme])
const handleAudioTracksLoaded = useCallback((tracks: AudioTrack[]) => {
setAudioTracks(tracks)
}, [])
const handleQualityLevelsLoaded = useCallback((levels: VideoQuality[]) => {
setQualities(levels)
}, [])
return (
<PlayerProvider initialMuted={muted} language={language}>
<VideoPlayerContent
src={src}
poster={poster}
autoplay={autoplay}
loop={loop}
muted={muted}
controls={controls}
subtitles={subtitles}
keyboardShortcuts={keyboardShortcuts}
pictureInPicture={pictureInPicture}
className={className}
style={style}
onPlay={onPlay}
onPause={onPause}
onEnded={onEnded}
onTimeUpdate={onTimeUpdate}
onVolumeChange={onVolumeChange}
onError={onError}
onLoadedMetadata={onLoadedMetadata}
onSeeking={onSeeking}
onSeeked={onSeeked}
audioTracks={audioTracks}
onAudioTracksLoadedInternal={handleAudioTracksLoaded}
qualities={qualities}
onQualityLevelsLoadedInternal={handleQualityLevelsLoaded}
/>
</PlayerProvider>
)
}