You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
spa_instat_tv/src/features/MultiSourcePlayer/hooks/index.tsx

191 lines
4.4 KiB

import type { MouseEvent } from 'react'
import {
useCallback,
useEffect,
useRef,
} from 'react'
import size from 'lodash/size'
import type { LastPlayPosition } from 'requests'
import { useFullscreen } from 'features/StreamPlayer/hooks/useFullscreen'
import { useVolume } from 'features/VideoPlayer/hooks/useVolume'
import { useObjectState } from 'hooks'
import { useProgressChangeHandler } from './useProgressChangeHandler'
import { usePlayingHandlers } from './usePlayingHandlers'
import { useVideoQuality } from './useVideoQuality'
import { useDuration } from './useDuration'
import type { Chapters } from '../types'
export type PlayerState = {
activeChapterIndex: number,
loadedProgress: number,
playedProgress: number,
playing: boolean,
ready: boolean,
seek: number,
seeking: boolean,
}
const initialState: PlayerState = {
activeChapterIndex: 0,
loadedProgress: 0,
playedProgress: 0,
playing: false,
ready: false,
seek: 0,
seeking: false,
}
export type Props = {
chapters: Chapters,
onError?: () => void,
onPlayingChange: (playing: boolean) => void,
onProgressChange: (seconds: number, period: number) => void,
resumeFrom: LastPlayPosition,
}
export const useMultiSourcePlayer = ({
chapters,
onError,
onPlayingChange,
onProgressChange: onProgressChangeCallback,
resumeFrom,
}: Props) => {
const [
{
activeChapterIndex,
loadedProgress,
playedProgress,
playing,
seek,
seeking,
},
setPlayerState,
] = useObjectState({ ...initialState, activeChapterIndex: resumeFrom.half })
const videoRef = useRef<HTMLVideoElement>(null)
const {
onReady,
startPlaying,
stopPlaying,
togglePlaying,
} = usePlayingHandlers(setPlayerState)
const {
selectedQuality,
setSelectedQuality,
videoQualities,
} = useVideoQuality(chapters)
const duration = useDuration(chapters)
const handleError = useCallback(() => {
stopPlaying()
onError?.()
}, [onError, stopPlaying])
const onProgressChange = useProgressChangeHandler({
activeChapterIndex,
chapters,
duration,
setPlayerState,
})
const getActiveChapterUrl = useCallback((quality: string = selectedQuality) => (
chapters[activeChapterIndex].urls[quality]
), [selectedQuality, chapters, activeChapterIndex])
const onQualitySelect = (quality: string) => {
setPlayerState({
seek: videoRef.current?.currentTime ?? 0,
})
setSelectedQuality(quality)
}
const playNextChapter = useCallback(() => {
const nextIndex = (activeChapterIndex + 1) % size(chapters)
setPlayerState({
activeChapterIndex: nextIndex,
loadedProgress: 0,
playedProgress: 0,
playing: nextIndex !== 0,
})
}, [activeChapterIndex, chapters, setPlayerState])
const onPlayerClick = (e: MouseEvent<HTMLDivElement>) => {
if (e.target === videoRef.current) {
togglePlaying()
}
}
const onLoadedProgress = (loadedMs: number) => {
const chapter = chapters[activeChapterIndex]
const value = loadedMs - chapter.startOffsetMs
setPlayerState({ loadedProgress: value })
}
const onPlayedProgress = (playedMs: number) => {
const chapter = chapters[activeChapterIndex]
const value = Math.max(playedMs - chapter.startOffsetMs, 0)
setPlayerState({ playedProgress: value })
}
useEffect(() => {
onPlayingChange(playing)
}, [playing, onPlayingChange])
useEffect(() => {
const progressSeconds = playedProgress / 1000
const { period } = chapters[activeChapterIndex]
onProgressChangeCallback(progressSeconds, Number(period))
}, [
playedProgress,
chapters,
onProgressChangeCallback,
activeChapterIndex,
])
useEffect(() => {
const { duration: chapterDuration } = chapters[activeChapterIndex]
if (playedProgress >= chapterDuration && !seeking) {
playNextChapter()
}
}, [
activeChapterIndex,
playedProgress,
seeking,
playNextChapter,
chapters,
])
return {
activeChapterIndex,
activeSrc: getActiveChapterUrl(),
chapters,
duration,
loadedProgress,
onError: handleError,
onLoadedProgress,
onPlayedProgress,
onPlayerClick,
onProgressChange,
onQualitySelect,
onReady,
playNextChapter,
playedProgress,
playing,
seek,
selectedQuality,
startPlaying,
togglePlaying,
videoQualities,
videoRef,
...useFullscreen(),
...useVolume(),
}
}