import React, { createContext, useContext, useRef, useState, useCallback } from 'react' import type { PlayerContextValue, VideoState, UIState, PlayerSettings, AudioTrack } from '../types' import type { Translations } from '../i18n' import { getTranslations, detectBrowserLanguage } from '../i18n' type SelectedQuality = PlayerSettings['quality'] type SelectedSubtitle = PlayerSettings['subtitle'] interface PlayerContextType extends PlayerContextValue { setVideoState: React.Dispatch> setUIState: React.Dispatch> translations: Translations } const PlayerContext = createContext(null) // eslint-disable-next-line react-refresh/only-export-components export const usePlayerContext = () => { const context = useContext(PlayerContext) if (!context) { throw new Error('usePlayerContext must be used within a PlayerProvider') } return context } interface PlayerProviderProps { children: React.ReactNode initialVolume?: number initialMuted?: boolean initialPlaybackRate?: number language?: string } export const PlayerProvider: React.FC = ({ children, initialVolume = 1, initialMuted = false, initialPlaybackRate = 1, language, }) => { const videoRef = useRef(null) const containerRef = useRef(null) // Get translations based on language prop or browser language const translations = getTranslations(language || detectBrowserLanguage()) const [videoState, setVideoState] = useState({ playing: false, currentTime: 0, duration: 0, buffered: 0, volume: initialVolume, muted: initialMuted, playbackRate: initialPlaybackRate, fullscreen: false, pictureInPicture: false, loading: false, error: null, seeking: false, isLiveBroadcast: false, }) const [uiState, setUIState] = useState({ controlsVisible: true, settingsOpen: false, volumeControlOpen: false, qualityMenuOpen: false, subtitleMenuOpen: false, }) const [settings, setSettings] = useState({ quality: null, subtitle: null, audioTrack: null, playbackRate: initialPlaybackRate, }) // Video controls const play = useCallback(() => { videoRef.current?.play() }, []) const pause = useCallback(() => { videoRef.current?.pause() }, []) const togglePlay = useCallback(() => { if (videoState.playing) { pause() } else { play() } }, [videoState.playing, play, pause]) const seek = useCallback((time: number) => { if (videoRef.current) { videoRef.current.currentTime = time } }, []) const setVolume = useCallback((volume: number) => { if (videoRef.current) { const clampedVolume = Math.max(0, Math.min(1, volume)) videoRef.current.volume = clampedVolume setVideoState((prev) => ({ ...prev, volume: clampedVolume })) } }, []) const toggleMute = useCallback(() => { if (videoRef.current) { videoRef.current.muted = !videoRef.current.muted setVideoState((prev) => ({ ...prev, muted: !prev.muted })) } }, []) const setPlaybackRate = useCallback((rate: number) => { if (videoRef.current) { videoRef.current.playbackRate = rate setVideoState((prev) => ({ ...prev, playbackRate: rate })) setSettings((prev) => ({ ...prev, playbackRate: rate })) } }, []) // Fullscreen & PIP const toggleFullscreen = useCallback(() => { if (!document.fullscreenElement) { containerRef.current?.requestFullscreen() } else { document.exitFullscreen() } }, []) const togglePictureInPicture = useCallback(async () => { if (!document.pictureInPictureElement) { try { await videoRef.current?.requestPictureInPicture() } catch (error) { if (error instanceof Error) { setVideoState((prev) => ({ ...prev, error })) } } } else { await document.exitPictureInPicture() } }, []) // UI controls const showControls = useCallback(() => { setUIState((prev) => ({ ...prev, controlsVisible: true })) }, []) const hideControls = useCallback(() => { setUIState((prev) => ({ ...prev, controlsVisible: false })) }, []) const toggleSettings = useCallback(() => { setUIState((prev) => ({ ...prev, settingsOpen: !prev.settingsOpen })) }, []) // Settings const setQuality = useCallback((quality: SelectedQuality) => { setSettings((prev) => ({ ...prev, quality })) }, []) const setSubtitle = useCallback((subtitle: SelectedSubtitle) => { setSettings((prev) => ({ ...prev, subtitle })) }, []) const setAudioTrack = useCallback((audioTrack: AudioTrack | null) => { setSettings((prev) => ({ ...prev, audioTrack })) }, []) const value: PlayerContextType = { videoState, uiState, settings, videoRef, containerRef, setVideoState, setUIState, translations, play, pause, togglePlay, seek, setVolume, toggleMute, setPlaybackRate, toggleFullscreen, togglePictureInPicture, showControls, hideControls, toggleSettings, setQuality, setSubtitle, setAudioTrack, } return {children} }