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

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

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

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

@ -10,3 +10,8 @@ export type 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 = {
className?: string,
height?: string,
hidden?: boolean,
isFullscreen?: boolean,
muted?: boolean,
onDurationChange?: (durationMs: number) => void,
@ -63,7 +64,7 @@ export const useVideoPlayer = ({
useEffect(() => {
const video = videoRef.current
if (!video) return
if (!video || !src) return
video.src = src
video.load()

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

Loading…
Cancel
Save