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.
124 lines
2.8 KiB
124 lines
2.8 KiB
import type { ForwardRefRenderFunction, SyntheticEvent } from 'react'
|
|
import {
|
|
useEffect,
|
|
useState,
|
|
useRef,
|
|
} from 'react'
|
|
|
|
import isUndefined from 'lodash/isUndefined'
|
|
import isNumber from 'lodash/isNumber'
|
|
|
|
import { useProgressChange } from './useProgressChange'
|
|
|
|
type Ref = Parameters<ForwardRefRenderFunction<HTMLVideoElement>>[1]
|
|
|
|
export type Props = {
|
|
className?: string,
|
|
crossOrigin?: string,
|
|
height?: string,
|
|
hidden?: boolean,
|
|
isFullscreen?: boolean,
|
|
muted?: boolean,
|
|
onDurationChange?: (durationMs: number) => void,
|
|
onEnded?: (e: SyntheticEvent<HTMLVideoElement>) => void,
|
|
onError?: (e?: SyntheticEvent<HTMLVideoElement>) => void,
|
|
onLoadedProgress?: (loadedMs: number) => void,
|
|
onPause?: (e: SyntheticEvent<HTMLVideoElement>) => void,
|
|
onPlayedProgress?: (playedMs: number) => void,
|
|
onReady?: () => void,
|
|
playing?: boolean,
|
|
ref?: Ref,
|
|
seek?: number | null,
|
|
src: string,
|
|
volume?: number,
|
|
width?: string,
|
|
}
|
|
|
|
const useVideoRef = (ref?: Ref) => {
|
|
const videoRef = useRef<HTMLVideoElement>(null)
|
|
if (ref && typeof ref === 'object') return ref
|
|
return videoRef
|
|
}
|
|
|
|
export const useVideoPlayer = ({
|
|
isFullscreen,
|
|
onDurationChange,
|
|
onError,
|
|
onLoadedProgress,
|
|
onPlayedProgress,
|
|
onReady,
|
|
playing,
|
|
ref,
|
|
seek = null,
|
|
src,
|
|
volume,
|
|
}: Props) => {
|
|
const [ready, setReady] = useState(false)
|
|
const videoRef = useVideoRef(ref)
|
|
|
|
const handleReady = () => {
|
|
setReady(true)
|
|
onReady?.()
|
|
}
|
|
|
|
const handleDurationChange = () => {
|
|
onDurationChange?.(videoRef.current?.duration || 0)
|
|
}
|
|
|
|
useEffect(() => {
|
|
const video = videoRef.current
|
|
if (!video || !src) return
|
|
|
|
video.src = src
|
|
video.load()
|
|
}, [src, videoRef])
|
|
|
|
useEffect(() => {
|
|
const video = videoRef.current
|
|
if (video && isNumber(seek)) {
|
|
video.currentTime = seek
|
|
}
|
|
}, [seek, videoRef])
|
|
|
|
useEffect(() => {
|
|
const video = videoRef.current
|
|
if (!video?.src || !ready || isUndefined(playing)) return
|
|
|
|
if (playing) {
|
|
// автовоспроизведение со звуком иногда может не сработать
|
|
// https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#new-behaviors
|
|
video.play().catch(onError)
|
|
} else {
|
|
video.pause()
|
|
}
|
|
}, [
|
|
ready,
|
|
playing,
|
|
src,
|
|
videoRef,
|
|
onError,
|
|
])
|
|
|
|
useEffect(() => {
|
|
const video = videoRef.current
|
|
if (video) {
|
|
video.volume = volume ?? 1
|
|
}
|
|
}, [volume, videoRef])
|
|
|
|
// Todo this logic is responsible for scrolling to the player, delete if not required
|
|
// useEffect(() => {
|
|
// videoRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })
|
|
// }, [isFullscreen, videoRef])
|
|
|
|
return {
|
|
handleDurationChange,
|
|
handleReady,
|
|
videoRef,
|
|
...useProgressChange({
|
|
onLoadedProgress,
|
|
onPlayedProgress,
|
|
videoRef,
|
|
}),
|
|
}
|
|
}
|
|
|