import { useEffect, useEffectEvent, useState } from "react"; import { ProviderFactory, usePlayer } from "./provider"; import type { OmniPlayer } from "./specs/omni-player.nitro"; import type { CastLoadRequest, OmniEvents } from "./types/events"; import type { OmniPlayerState } from "./types/player"; function capitalize(str: T): Capitalize { return (str.charAt(0).toUpperCase() + str.slice(1)) as Capitalize; } export const useEvent = ( event: Event, callback: OmniEvents[Event], ) => { const player = usePlayer() as OmniPlayer; useEffect(() => { return player.eventMap[`addOn${capitalize(event)}Listener`]( callback as any, ); }, [player, event, callback]); }; // cast connect (android tv): called when a sender casts to this app. throw to // refuse the cast, senders show an error. export const useCastLoad = ( callback: (request: CastLoadRequest) => void | Promise, ) => { const onLoad = useEffectEvent( async (request: CastLoadRequest) => await callback(request), ); useEffect(() => ProviderFactory.addCastLoadListener(onLoad), []); }; export function usePlayerState( key: Key, ): OmniPlayerState[Key]; export function usePlayerState( key: "currentTime", refresh?: number, ): OmniPlayerState["currentTime"]; export function usePlayerState( key: Key, refresh?: number, ): OmniPlayerState[Key] { const player = usePlayer() as OmniPlayer; // don't call `player[key]` (native call on main thread) every re-render // only do it when needed (init) const [ret, setState] = useState(() => player[key]); useEffect(() => { const em = player.eventMap; switch (key) { case "currentTime": case "buffered": case "duration": case "playbackRate": case "volume": return em.addStateListener(key, setState); case "isPlaying": case "muted": case "isAutoQuality": return em.addStateBoolListener(key, setState); case "status": return em.addPlayerStatusListener(setState); case "castStatus": return em.addCastStatusListener(setState); case "source": return em.addSourceListener(setState); case "videos": case "audios": case "subtitles": return em.addTracksListener(key, setState); case "renditions": return em.addRenditionsListener(setState); } }, [player, key]); if (key === "currentTime") refresh ??= 1; useEffect(() => { if (!refresh || refresh <= 0) return; const int = setInterval(() => { setState(player[key]); }, refresh * 1000); return () => clearInterval(int); }, [refresh, key, player]); return ret; }