feat(703): preloading next episode (#277)

keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent d9467279db
commit 537a38a79a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 39
      src/features/MultiSourcePlayer/hooks/index.tsx
  2. 7
      src/features/MultiSourcePlayer/hooks/usePlayingHandlers.tsx
  3. 17
      src/features/MultiSourcePlayer/hooks/useProgressChangeHandler.tsx
  4. 37
      src/features/MultiSourcePlayer/index.tsx
  5. 5
      src/features/MultiSourcePlayer/types.tsx
  6. 3
      src/features/VideoPlayer/hooks/index.tsx
  7. 2
      src/features/VideoPlayer/index.tsx

@ -19,24 +19,30 @@ import { usePlayingHandlers } from './usePlayingHandlers'
import { useVideoQuality } from './useVideoQuality' import { useVideoQuality } from './useVideoQuality'
import { useDuration } from './useDuration' import { useDuration } from './useDuration'
import type { Chapters } from '../types' import type { Chapters } from '../types'
import { Players } from '../types'
export type PlayerState = { export type PlayerState = {
activeChapterIndex: number, activeChapterIndex: number,
activePlayer: Players,
loadedProgress: number, loadedProgress: number,
playedProgress: number, playedProgress: number,
playing: boolean, playing: boolean,
ready: boolean, ready: boolean,
seek: number, seek: Record<Players, number>,
seeking: boolean, seeking: boolean,
} }
const initialState: PlayerState = { const initialState: PlayerState = {
activeChapterIndex: 0, activeChapterIndex: 0,
activePlayer: 0,
loadedProgress: 0, loadedProgress: 0,
playedProgress: 0, playedProgress: 0,
playing: false, playing: false,
ready: false, ready: false,
seek: 0, seek: {
[Players.PLAYER1]: 0,
[Players.PLAYER2]: 0,
},
seeking: false, seeking: false,
} }
@ -59,6 +65,7 @@ export const useMultiSourcePlayer = ({
const [ const [
{ {
activeChapterIndex, activeChapterIndex,
activePlayer,
loadedProgress, loadedProgress,
playedProgress, playedProgress,
playing, playing,
@ -68,7 +75,8 @@ export const useMultiSourcePlayer = ({
}, },
setPlayerState, setPlayerState,
] = useObjectState({ ...initialState, activeChapterIndex: resumeFrom.half }) ] = useObjectState({ ...initialState, activeChapterIndex: resumeFrom.half })
const videoRef = useRef<HTMLVideoElement>(null) const video1Ref = useRef<HTMLVideoElement>(null)
const video2Ref = useRef<HTMLVideoElement>(null)
const { const {
onReady, onReady,
playNextChapter, playNextChapter,
@ -92,20 +100,24 @@ export const useMultiSourcePlayer = ({
}, [onError, stopPlaying]) }, [onError, stopPlaying])
const onProgressChange = useProgressChangeHandler({ const onProgressChange = useProgressChangeHandler({
activeChapterIndex,
chapters, chapters,
duration, duration,
setPlayerState, setPlayerState,
}) })
const getActiveChapterUrl = useCallback((quality: string = selectedQuality) => ( const getChapterUrl = useCallback((index: number, quality: string = selectedQuality) => (
chapters[activeChapterIndex].urls[quality] chapters[index]?.urls[quality]
), [selectedQuality, chapters, activeChapterIndex]) ), [selectedQuality, chapters])
const videoRef = [video1Ref, video2Ref][activePlayer]
const onQualitySelect = (quality: string) => { const onQualitySelect = (quality: string) => {
setPlayerState({ setPlayerState((state) => ({
seek: videoRef.current?.currentTime ?? 0, seek: {
}) ...state.seek,
[state.activePlayer]: videoRef.current?.currentTime ?? 0,
},
}))
setSelectedQuality(quality) setSelectedQuality(quality)
} }
@ -159,12 +171,14 @@ export const useMultiSourcePlayer = ({
return { return {
activeChapterIndex, activeChapterIndex,
activeSrc: getActiveChapterUrl(), activePlayer,
activeSrc: getChapterUrl(activeChapterIndex),
chapters, chapters,
duration, duration,
isFirstChapterPlaying: activeChapterIndex === 0, isFirstChapterPlaying: activeChapterIndex === 0,
isLastChapterPlaying: activeChapterIndex === numberOfChapters - 1, isLastChapterPlaying: activeChapterIndex === numberOfChapters - 1,
loadedProgress, loadedProgress,
nextSrc: getChapterUrl(activeChapterIndex + 1),
onError: handleError, onError: handleError,
onLoadedProgress, onLoadedProgress,
onPlayedProgress, onPlayedProgress,
@ -181,8 +195,9 @@ export const useMultiSourcePlayer = ({
selectedQuality, selectedQuality,
startPlaying, startPlaying,
togglePlaying, togglePlaying,
video1Ref,
video2Ref,
videoQualities, videoQualities,
videoRef,
...useFullscreen(), ...useFullscreen(),
...useVolume(), ...useVolume(),
} }

@ -2,7 +2,10 @@ import { useCallback } from 'react'
import { SetPartialState } from 'hooks' import { SetPartialState } from 'hooks'
import { PlayerState } from '.' import type { PlayerState } from '.'
import { Players } from '../types'
const getNextPlayer = (player: Players): Players => (player + 1) % 2
export const usePlayingHandlers = ( export const usePlayingHandlers = (
setPlayerState: SetPartialState<PlayerState>, setPlayerState: SetPartialState<PlayerState>,
@ -43,6 +46,7 @@ export const usePlayingHandlers = (
const isLastChapter = state.activeChapterIndex + 1 === numberOfChapters const isLastChapter = state.activeChapterIndex + 1 === numberOfChapters
return { return {
activeChapterIndex: isLastChapter ? 0 : state.activeChapterIndex + 1, activeChapterIndex: isLastChapter ? 0 : state.activeChapterIndex + 1,
activePlayer: getNextPlayer(state.activeChapterIndex),
loadedProgress: 0, loadedProgress: 0,
playedProgress: 0, playedProgress: 0,
playing: isLastChapter ? false : state.playing, playing: isLastChapter ? false : state.playing,
@ -55,6 +59,7 @@ export const usePlayingHandlers = (
if (!state.ready || state.activeChapterIndex === 0) return state if (!state.ready || state.activeChapterIndex === 0) return state
return { return {
activeChapterIndex: state.activeChapterIndex - 1, activeChapterIndex: state.activeChapterIndex - 1,
activePlayer: getNextPlayer(state.activeChapterIndex),
loadedProgress: 0, loadedProgress: 0,
playedProgress: 0, playedProgress: 0,
} }

@ -7,42 +7,45 @@ import type { PlayerState } from '.'
import { findChapterByProgress } from '../helpers' import { findChapterByProgress } from '../helpers'
type Args = { type Args = {
activeChapterIndex: number,
chapters: Chapters, chapters: Chapters,
duration: number, duration: number,
setPlayerState: SetPartialState<PlayerState>, setPlayerState: SetPartialState<PlayerState>,
} }
export const useProgressChangeHandler = ({ export const useProgressChangeHandler = ({
activeChapterIndex,
chapters, chapters,
duration, duration,
setPlayerState, setPlayerState,
}: Args) => { }: Args) => {
const onProgressChange = useCallback((progress: number, seeking: boolean) => { const onProgressChange = useCallback((progress: number, seeking: boolean) => {
setPlayerState((state) => {
// значение новой позиции ползунка в миллисекундах // значение новой позиции ползунка в миллисекундах
const progressMs = progress * duration const progressMs = progress * duration
const chapterIndex = findChapterByProgress(chapters, progressMs) const chapterIndex = findChapterByProgress(chapters, progressMs)
const chapter = chapters[chapterIndex] const chapter = chapters[chapterIndex]
const isProgressOnDifferentChapter = ( const isProgressOnDifferentChapter = (
chapterIndex !== -1 chapterIndex !== -1
&& chapterIndex !== activeChapterIndex && chapterIndex !== state.activeChapterIndex
) )
const nextChapter = isProgressOnDifferentChapter const nextChapter = isProgressOnDifferentChapter
? chapterIndex ? chapterIndex
: activeChapterIndex : state.activeChapterIndex
// отнимаем начало главы на котором остановились от общего прогресса // отнимаем начало главы на котором остановились от общего прогресса
// чтобы получить прогресс текущей главы // чтобы получить прогресс текущей главы
const chapterProgressMs = (progressMs - chapter.startMs) const chapterProgressMs = (progressMs - chapter.startMs)
const seekMs = chapterProgressMs + chapter.startOffsetMs const seekMs = chapterProgressMs + chapter.startOffsetMs
setPlayerState({ return {
activeChapterIndex: nextChapter, activeChapterIndex: nextChapter,
playedProgress: chapterProgressMs, playedProgress: chapterProgressMs,
seek: seekMs / 1000, seek: {
...state.seek,
[state.activePlayer]: seekMs / 1000,
},
seeking, seeking,
}
}) })
}, [duration, chapters, activeChapterIndex, setPlayerState]) }, [duration, chapters, setPlayerState])
return onProgressChange return onProgressChange
} }

@ -15,10 +15,12 @@ import { ProgressBar } from './components/ProgressBar'
import { Settings } from './components/Settings' import { Settings } from './components/Settings'
import type { Props } from './hooks' import type { Props } from './hooks'
import { useMultiSourcePlayer } from './hooks' import { useMultiSourcePlayer } from './hooks'
import { Players } from './types'
export const MultiSourcePlayer = (props: Props) => { export const MultiSourcePlayer = (props: Props) => {
const { const {
activeChapterIndex, activeChapterIndex,
activePlayer,
activeSrc, activeSrc,
chapters, chapters,
duration, duration,
@ -27,6 +29,7 @@ export const MultiSourcePlayer = (props: Props) => {
isLastChapterPlaying, isLastChapterPlaying,
loadedProgress, loadedProgress,
muted, muted,
nextSrc,
onError, onError,
onFullscreenClick, onFullscreenClick,
onLoadedProgress, onLoadedProgress,
@ -45,12 +48,16 @@ export const MultiSourcePlayer = (props: Props) => {
seek, seek,
selectedQuality, selectedQuality,
togglePlaying, togglePlaying,
video1Ref,
video2Ref,
videoQualities, videoQualities,
videoRef,
volume, volume,
volumeInPercent, volumeInPercent,
wrapperRef, wrapperRef,
} = useMultiSourcePlayer(props) } = useMultiSourcePlayer(props)
const firstPlayerActive = activePlayer === Players.PLAYER1
return ( return (
<PlayerWrapper <PlayerWrapper
ref={wrapperRef} ref={wrapperRef}
@ -65,15 +72,31 @@ export const MultiSourcePlayer = (props: Props) => {
) )
} }
<VideoPlayer <VideoPlayer
src={activeSrc} src={firstPlayerActive ? activeSrc : nextSrc}
playing={playing} playing={firstPlayerActive ? playing : false}
hidden={!firstPlayerActive}
muted={muted}
volume={volume}
ref={video1Ref}
seek={seek[Players.PLAYER1]}
isFullscreen={isFullscreen}
onLoadedProgress={firstPlayerActive ? onLoadedProgress : undefined}
onPlayedProgress={firstPlayerActive ? onPlayedProgress : undefined}
onEnded={playNextChapter}
onError={onError}
onReady={onReady}
/>
<VideoPlayer
src={!firstPlayerActive ? activeSrc : nextSrc}
playing={!firstPlayerActive ? playing : false}
hidden={firstPlayerActive}
muted={muted} muted={muted}
volume={volume} volume={volume}
ref={videoRef} ref={video2Ref}
seek={seek} seek={seek[Players.PLAYER2]}
isFullscreen={isFullscreen} isFullscreen={isFullscreen}
onLoadedProgress={onLoadedProgress} onLoadedProgress={!firstPlayerActive ? onLoadedProgress : undefined}
onPlayedProgress={onPlayedProgress} onPlayedProgress={!firstPlayerActive ? onPlayedProgress : undefined}
onEnded={playNextChapter} onEnded={playNextChapter}
onError={onError} onError={onError}
onReady={onReady} onReady={onReady}

@ -10,3 +10,8 @@ export type Chapter = {
} }
export type Chapters = Array<Chapter> export type Chapters = Array<Chapter>
export enum Players {
PLAYER1 = 0,
PLAYER2 = 1,
}

@ -15,6 +15,7 @@ type Ref = Parameters<ForwardRefRenderFunction<HTMLVideoElement>>[1]
export type Props = { export type Props = {
className?: string, className?: string,
height?: string, height?: string,
hidden?: boolean,
isFullscreen?: boolean, isFullscreen?: boolean,
muted?: boolean, muted?: boolean,
onDurationChange?: (durationMs: number) => void, onDurationChange?: (durationMs: number) => void,
@ -63,7 +64,7 @@ export const useVideoPlayer = ({
useEffect(() => { useEffect(() => {
const video = videoRef.current const video = videoRef.current
if (!video) return if (!video || !src) return
video.src = src video.src = src
video.load() video.load()

@ -8,6 +8,7 @@ export const VideoPlayer = forwardRef<HTMLVideoElement, Props>((props: Props, re
const { const {
className, className,
height, height,
hidden,
onEnded, onEnded,
onError, onError,
src, src,
@ -26,6 +27,7 @@ export const VideoPlayer = forwardRef<HTMLVideoElement, Props>((props: Props, re
className={className} className={className}
width={width} width={width}
height={height} height={height}
hidden={hidden}
ref={videoRef} ref={videoRef}
src={src} src={src}
muted={volume === 0} muted={volume === 0}

Loading…
Cancel
Save