import { HTMLAttributes, useCallback, useContext, useEffect, useMemo, } from 'react'; import {PlayerStoreContext} from '@common/player/player-context'; import {HtmlMediaInternalStateReturn} from '@common/player/providers/html-media/use-html-media-internal-state'; const defaultPlaybackRates = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]; export function useHtmlMediaEvents({ ref, updateCurrentTime, updateBuffered, internalState, }: HtmlMediaInternalStateReturn): HTMLAttributes { const store = useContext(PlayerStoreContext); const onTextTracksChange = useCallback(() => { if (!ref.current) return; const tracks = Array.from(ref.current.textTracks).filter( t => t.label && (t.kind === 'subtitles' || t.kind === 'captions') ); let trackId = -1; for (let id = 0; id < tracks.length; id += 1) { if (tracks[id].mode === 'hidden') { // Do not break in case there is a following track with showing. trackId = id; } else if (tracks[id].mode === 'showing') { trackId = id; break; } } const isVisible = trackId !== -1 && tracks[trackId].mode === 'showing'; store.getState().emit('currentTextTrackChange', {trackId}); store.getState().emit('textTrackVisibilityChange', {isVisible}); store.getState().emit('textTracks', {tracks}); }, [ref, store]); useEffect(() => { const el = ref.current; return () => { el?.textTracks.removeEventListener('change', onTextTracksChange); }; }, [ref, onTextTracksChange]); return useMemo(() => { const emit = store.getState().emit; return { // set some common props used on audio/video/hls/dash providers autoPlay: false, onContextMenu: e => e.preventDefault(), controlsList: 'nodownload', preload: 'metadata', 'x-webkit-airplay': 'allow', onEnded: () => { emit('playbackEnd'); updateCurrentTime(); internalState.current.timeRafLoop.stop(); }, onStalled: e => { if (e.currentTarget.readyState < 3) { emit('buffering', {isBuffering: true}); } }, onWaiting: () => { emit('buffering', {isBuffering: true}); }, onPlaying: () => { emit('play'); emit('buffering', {isBuffering: false}); }, onPause: e => { emit('pause'); emit('buffering', {isBuffering: false}); internalState.current.timeRafLoop.stop(); }, onSuspend: () => { emit('buffering', {isBuffering: false}); }, onSeeking: () => { updateCurrentTime(); }, onSeeked: () => { updateCurrentTime(); }, onTimeUpdate: () => { updateCurrentTime(); }, onError: e => { emit('error', {sourceEvent: e}); }, onDurationChange: e => { updateCurrentTime(); emit('durationChange', {duration: e.currentTarget.duration}); }, onRateChange: e => { emit('playbackRateChange', {rate: e.currentTarget.playbackRate}); }, onLoadedMetadata: e => { if (!internalState.current.playbackReady) { emit('providerReady', {el: e.currentTarget}); internalState.current.playbackReady = true; updateBuffered(); onTextTracksChange(); e.currentTarget.textTracks.addEventListener('change', () => { onTextTracksChange(); }); } emit('cued'); emit('playbackRates', {rates: defaultPlaybackRates}); }, }; }, [ internalState, store, updateCurrentTime, onTextTracksChange, updateBuffered, ]); }