126 lines
3.6 KiB
TypeScript
Executable File
126 lines
3.6 KiB
TypeScript
Executable File
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<HTMLMediaElement> {
|
|
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,
|
|
]);
|
|
}
|