Ott 475 watch match from last pause (#171)

* feat(#475): added match last watch seconds request

* feat(#475): resuming stream/full match
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent c12897677f
commit 6a2bd02403
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/config/procedures.tsx
  2. 3
      src/features/MatchCard/CardLiveHover/index.tsx
  3. 65
      src/features/MatchPage/hooks/useLastPlayPosition.tsx
  4. 3
      src/features/MatchPage/hooks/useVideoData.tsx
  5. 13
      src/features/MatchPage/index.tsx
  6. 9
      src/features/MultiSourcePlayer/helpers/index.tsx
  7. 33
      src/features/MultiSourcePlayer/hooks/index.tsx
  8. 12
      src/features/MultiSourcePlayer/hooks/usePlayingState.tsx
  9. 4
      src/features/StreamPlayer/hooks/index.tsx
  10. 43
      src/requests/getMatchLastWatchSeconds.tsx
  11. 1
      src/requests/index.tsx

@ -15,6 +15,7 @@ export const PROCEDURES = {
get_tournament_matches: 'get_tournament_matches',
get_user_favorites: 'get_user_favorites',
get_user_info: 'get_user_info',
get_user_match_second: 'get_user_match_second',
logout_user: 'logout_user',
lst_c_country: 'lst_c_country',
param_lexical: 'param_lexical',

@ -3,6 +3,7 @@ import React from 'react'
import { Link } from 'react-router-dom'
import { RESUME_KEY } from 'features/MatchPage/hooks/useLastPlayPosition'
import { OutsideClick } from 'features/OutsideClick'
import {
@ -44,7 +45,7 @@ export const CardLiveHover = ({
</Row>
<Row>
<Link to={`/${sportName}/matches/${id}`}>
<Link to={`/${sportName}/matches/${id}?${RESUME_KEY}=true`}>
<MoreVideo t='watch_from_last_pause' />
</Link>
</Row>

@ -0,0 +1,65 @@
import {
useEffect,
useState,
useMemo,
} from 'react'
import { useLocation } from 'react-router'
import isBoolean from 'lodash/isBoolean'
import type { LastPlayPosition } from 'requests'
import { getMatchLastWatchSeconds } from 'requests'
import {
useSportNameParam,
usePageId,
useRequest,
} from 'hooks'
export const RESUME_KEY = 'resume'
const readResumeParam = (search: string) => {
const params = new URLSearchParams(search)
const rawValue = params.get(RESUME_KEY)
if (!rawValue) return false
const value = JSON.parse(rawValue)
return isBoolean(value) && Boolean(value)
}
const initialPosition = {
half: 0,
second: 0,
}
export const useLastPlayPosition = () => {
const { search } = useLocation()
const { sportType } = useSportNameParam()
const matchId = usePageId()
const [
lastPlayPosition,
setPosition,
] = useState<LastPlayPosition>(initialPosition)
const {
isFetching: isLastPlayPositionFetching,
request: requestLastPlayPosition,
} = useRequest(getMatchLastWatchSeconds)
const resume = useMemo(() => readResumeParam(search), [search])
useEffect(() => {
if (resume) {
requestLastPlayPosition(sportType, matchId).then(setPosition)
}
}, [
sportType,
matchId,
resume,
requestLastPlayPosition,
])
return {
isLastPlayPositionFetching,
lastPlayPosition,
}
}

@ -6,6 +6,8 @@ import { getLiveVideos, getVideos } from 'requests'
import { useSportNameParam, usePageId } from 'hooks'
import { isNull } from 'lodash'
import { useLastPlayPosition } from './useLastPlayPosition'
export const useVideoData = () => {
const [videos, setVideos] = useState<Videos>([])
const [liveVideos, setLiveVideos] = useState<LiveVideos>([])
@ -32,5 +34,6 @@ export const useVideoData = () => {
return {
url: liveVideos[0] || '',
videos,
...useLastPlayPosition(),
}
}

@ -13,11 +13,16 @@ import { MainWrapper, Container } from './styled'
export const MatchPage = () => {
const profile = useMatchProfile()
const { url, videos } = useVideoData()
const {
isLastPlayPositionFetching,
lastPlayPosition,
url,
videos,
} = useVideoData()
const { onPlayerProgressChange, onPlayingChange } = usePlayerProgressReporter()
const isLiveMatch = Boolean(url)
const isFinishedMatch = !isEmpty(videos)
const isLiveMatch = Boolean(url) && !isLastPlayPositionFetching
const isFinishedMatch = !isEmpty(videos) && !isLastPlayPositionFetching
return (
<MainWrapper>
@ -29,6 +34,7 @@ export const MatchPage = () => {
url={url}
onPlayingChange={onPlayingChange}
onProgressChange={onPlayerProgressChange}
resumeFrom={lastPlayPosition.second}
/>
)
}
@ -38,6 +44,7 @@ export const MatchPage = () => {
videos={videos}
onPlayingChange={onPlayingChange}
onProgressChange={onPlayerProgressChange}
resumeFrom={lastPlayPosition}
/>
)
}

@ -5,24 +5,23 @@ import findIndex from 'lodash/findIndex'
import type { Chapters } from '../types'
type Args = {
resume?: boolean,
from?: number,
url: string,
videoRef: RefObject<HTMLVideoElement>,
}
export const preparePlayer = ({
resume = false,
from = 0,
url,
videoRef,
}: Args) => {
const video = videoRef?.current
if (!video) return
const wasAtTime = video.currentTime
// eslint-disable-next-line no-param-reassign
video.src = url
if (resume) {
video.currentTime = wasAtTime
if (from) {
video.currentTime = from
}
video.load()
}

@ -8,7 +8,7 @@ import {
import size from 'lodash/size'
import type { Videos } from 'requests'
import type { LastPlayPosition, Videos } from 'requests'
import { useEventListener } from 'hooks'
import { useFullscreen } from 'features/StreamPlayer/hooks/useFullscreen'
@ -25,16 +25,18 @@ export type Props = {
onError?: () => void,
onPlayingChange: (playing: boolean) => void,
onProgressChange: (seconds: number, period: number) => void,
resumeFrom: LastPlayPosition,
videos: Videos,
}
export const useMultiSourcePlayer = ({
resumeFrom,
onError = () => {},
videos,
onPlayingChange,
onProgressChange: onProgressChangeCallback,
}: Props) => {
const activeChapterIndex = useRef(0)
const activeChapterIndex = useRef(resumeFrom.half)
const wrapperRef = useRef<HTMLDivElement>(null)
const videoRef = useRef<HTMLVideoElement>(null)
const [loadedProgress, setLoadedProgress] = useState(0)
@ -67,13 +69,18 @@ export const useMultiSourcePlayer = ({
videoRef,
})
const getCurrentChapterUrl = useCallback((quality: string = selectedQuality) => (
const getActiveChapterUrl = useCallback((quality: string = selectedQuality) => (
chapters[activeChapterIndex.current].urls[quality]
), [selectedQuality, chapters])
const getActiveChapterStart = useCallback(() => (
chapters[activeChapterIndex.current]?.startMs || 0
), [chapters])
const onQualitySelect = (quality: string) => {
const from = videoRef.current?.currentTime
setSelectedQuality(quality)
continuePlaying(getCurrentChapterUrl(quality), true)
continuePlaying(getActiveChapterUrl(quality), from)
}
const playNextChapter = () => {
@ -81,9 +88,9 @@ export const useMultiSourcePlayer = ({
const isLastChapterPlayed = activeChapterIndex.current === size(chapters)
if (isLastChapterPlayed) {
activeChapterIndex.current = 0
stopPlaying(getCurrentChapterUrl())
stopPlaying(getActiveChapterUrl())
} else {
continuePlaying(getCurrentChapterUrl())
continuePlaying(getActiveChapterUrl())
}
}
@ -93,10 +100,6 @@ export const useMultiSourcePlayer = ({
}
}
const getActiveChapterStart = () => (
chapters[activeChapterIndex.current]?.startMs || 0
)
const onLoadedChange = (loadedMs: number) => {
const chapterStart = getActiveChapterStart()
setLoadedProgress(chapterStart + loadedMs)
@ -108,13 +111,17 @@ export const useMultiSourcePlayer = ({
}
useEffect(() => {
const url = getCurrentChapterUrl()
const url = getActiveChapterUrl()
const chapterStartMs = getActiveChapterStart()
const from = resumeFrom.second - (chapterStartMs / 1000)
if (url && firstTimeStart) {
startPlaying(url)
startPlaying(url, from)
}
}, [
firstTimeStart,
getCurrentChapterUrl,
resumeFrom,
getActiveChapterUrl,
getActiveChapterStart,
startPlaying,
])

@ -27,8 +27,12 @@ export const usePlayingState = (videoRef: RefObject<HTMLVideoElement>) => {
setPlaying(false)
}, [videoRef])
const startPlaying = useCallback((url: string) => {
preparePlayer({ url, videoRef })
const startPlaying = useCallback((url: string, from?: number) => {
preparePlayer({
from,
url,
videoRef,
})
videoRef.current?.play()
setFirstTimeStart(false)
setPlaying(true)
@ -36,10 +40,10 @@ export const usePlayingState = (videoRef: RefObject<HTMLVideoElement>) => {
const continuePlaying = useCallback((
url: string,
rememberTime: boolean = false,
from?: number,
) => {
preparePlayer({
resume: rememberTime,
from,
url,
videoRef,
})

@ -22,6 +22,7 @@ type ProgressState = {
export type Props = {
onPlayingChange: (playing: boolean) => void,
onProgressChange: (seconds: number) => void,
resumeFrom: number,
url: string,
}
@ -30,12 +31,13 @@ const toMilliSeconds = (seconds: number) => seconds * 1000
export const useVideoPlayer = ({
onPlayingChange,
onProgressChange: progressChangeCallback,
resumeFrom,
}: Props) => {
const [ready, setReady] = useState(false)
const [playing, setPlaying] = useState(false)
const [duration, setDuration] = useState(0)
const [loadedProgress, setLoadedProgress] = useState(0)
const [playedProgress, setPlayedProgress] = useState(0)
const [playedProgress, setPlayedProgress] = useState(toMilliSeconds(resumeFrom))
const wrapperRef = useRef<HTMLDivElement>(null)
const playerRef = useRef<ReactPlayer>(null)

@ -0,0 +1,43 @@
import {
DATA_URL,
PROCEDURES,
SportTypes,
} from 'config'
import { callApi, getResponseData } from 'helpers'
const proc = PROCEDURES.get_user_match_second
type Response = {
_p_half: number | null,
_p_second: number | null,
}
export type LastPlayPosition = {
half: number,
second: number,
}
export const getMatchLastWatchSeconds = async (
sportType: SportTypes,
matchId: number,
) => {
const config = {
body: {
params: {
_p_match_id: matchId,
_p_sport: sportType,
},
proc,
},
}
const response: Response = await callApi({
config,
url: DATA_URL,
}).then(getResponseData(proc))
return {
half: response?._p_half ?? 0,
second: response?._p_second ?? 0,
}
}

@ -18,3 +18,4 @@ export * from './getVideos'
export * from './saveUserInfo'
export * from './getPlayerInfo'
export * from './getLiveVideos'
export * from './getMatchLastWatchSeconds'

Loading…
Cancel
Save