import type { BaseItemDto, MediaSourceInfo, } from "@jellyfin/sdk/lib/generated-client"; import React, { createContext, type MutableRefObject, type ReactNode, useContext, useMemo, } from "react"; import type { MpvPlayerViewRef } from "@/modules"; interface PlayerContextProps { playerRef: MutableRefObject; item: BaseItemDto; mediaSource: MediaSourceInfo | null | undefined; isVideoLoaded: boolean; tracksReady: boolean; } const PlayerContext = createContext(undefined); interface PlayerProviderProps { children: ReactNode; playerRef: MutableRefObject; item: BaseItemDto; mediaSource: MediaSourceInfo | null | undefined; isVideoLoaded: boolean; tracksReady: boolean; } export const PlayerProvider: React.FC = ({ children, playerRef, item, mediaSource, isVideoLoaded, tracksReady, }) => { const value = useMemo( () => ({ playerRef, item, mediaSource, isVideoLoaded, tracksReady }), [playerRef, item, mediaSource, isVideoLoaded, tracksReady], ); return ( {children} ); }; // Core context hook export const usePlayerContext = () => { const context = useContext(PlayerContext); if (!context) throw new Error("usePlayerContext must be used within PlayerProvider"); return context; }; // Player controls hook export const usePlayerControls = () => { const { playerRef } = usePlayerContext(); return { // Subtitle controls getSubtitleTracks: async () => { return playerRef.current?.getSubtitleTracks() ?? null; }, setSubtitleTrack: (trackId: number) => { playerRef.current?.setSubtitleTrack(trackId); }, disableSubtitles: () => { playerRef.current?.disableSubtitles(); }, addSubtitleFile: (url: string, select = true) => { playerRef.current?.addSubtitleFile(url, select); }, // Audio controls getAudioTracks: async () => { return playerRef.current?.getAudioTracks() ?? null; }, setAudioTrack: (trackId: number) => { playerRef.current?.setAudioTrack(trackId); }, // Playback controls play: () => playerRef.current?.play(), pause: () => playerRef.current?.pause(), seekTo: (position: number) => playerRef.current?.seekTo(position), seekBy: (offset: number) => playerRef.current?.seekBy(offset), setSpeed: (speed: number) => playerRef.current?.setSpeed(speed), // Subtitle positioning setSubtitleScale: (scale: number) => playerRef.current?.setSubtitleScale(scale), setSubtitlePosition: (position: number) => playerRef.current?.setSubtitlePosition(position), setSubtitleMarginY: (margin: number) => playerRef.current?.setSubtitleMarginY(margin), setSubtitleFontSize: (size: number) => playerRef.current?.setSubtitleFontSize(size), // PiP startPictureInPicture: () => playerRef.current?.startPictureInPicture(), stopPictureInPicture: () => playerRef.current?.stopPictureInPicture(), }; };