168 lines
4.0 KiB
TypeScript
168 lines
4.0 KiB
TypeScript
import { useEffect, useRef } from 'react';
|
|
import useAppStore from '../../store/app-store';
|
|
import './VideoPlayer.scss';
|
|
|
|
interface VideoPlayerProps {
|
|
src: string;
|
|
audioSrc?: string;
|
|
playing: boolean;
|
|
muted?: boolean;
|
|
width?: number | string;
|
|
height?: number | string;
|
|
onEnded?: () => void;
|
|
}
|
|
|
|
const VideoPlayer = ({
|
|
src,
|
|
audioSrc,
|
|
playing,
|
|
muted = false,
|
|
width = '100%',
|
|
height = '100%',
|
|
onEnded
|
|
}: VideoPlayerProps) => {
|
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
|
const audioRef = useRef<HTMLAudioElement | null>(null);
|
|
const masterVolume = useAppStore(state => state.settings.volume.master);
|
|
const musicVolume = useAppStore(state => state.settings.volume.music);
|
|
|
|
// Manage play/pause state for video
|
|
useEffect(() => {
|
|
const video = videoRef.current;
|
|
if (!video) return;
|
|
|
|
if (playing) {
|
|
video.play().catch(error => {
|
|
console.warn('Video playback prevented:', error);
|
|
});
|
|
} else {
|
|
video.pause();
|
|
}
|
|
}, [playing]);
|
|
|
|
// Manage play/pause state for audio
|
|
useEffect(() => {
|
|
const audio = audioRef.current;
|
|
if (!audio || !audioSrc) return;
|
|
|
|
if (playing) {
|
|
// Sync with video if needed
|
|
if (videoRef.current) {
|
|
audio.currentTime = videoRef.current.currentTime;
|
|
}
|
|
|
|
audio.play().catch(error => {
|
|
console.warn('Audio playback prevented:', error);
|
|
});
|
|
} else {
|
|
audio.pause();
|
|
}
|
|
}, [playing, audioSrc]);
|
|
|
|
// Sync audio with video when video seeks or loads
|
|
useEffect(() => {
|
|
const video = videoRef.current;
|
|
const audio = audioRef.current;
|
|
if (!video || !audio || !audioSrc) return;
|
|
|
|
const handleTimeUpdate = () => {
|
|
// Only sync if the difference is significant (more than 0.1 seconds)
|
|
if (Math.abs(video.currentTime - audio.currentTime) > 0.1) {
|
|
audio.currentTime = video.currentTime;
|
|
}
|
|
};
|
|
|
|
const handlePlay = () => {
|
|
if (playing) {
|
|
audio.play().catch(error => {
|
|
console.warn('Audio playback prevented:', error);
|
|
});
|
|
}
|
|
};
|
|
|
|
const handlePause = () => {
|
|
audio.pause();
|
|
};
|
|
|
|
video.addEventListener('seeked', handleTimeUpdate);
|
|
video.addEventListener('play', handlePlay);
|
|
video.addEventListener('pause', handlePause);
|
|
|
|
return () => {
|
|
video.removeEventListener('seeked', handleTimeUpdate);
|
|
video.removeEventListener('play', handlePlay);
|
|
video.removeEventListener('pause', handlePause);
|
|
};
|
|
}, [playing, audioSrc]);
|
|
|
|
// Manage volume for video
|
|
useEffect(() => {
|
|
const video = videoRef.current;
|
|
if (!video) return;
|
|
|
|
// If we have a separate audio source, mute the video
|
|
if (audioSrc) {
|
|
video.muted = true;
|
|
} else {
|
|
// Otherwise use the video's audio
|
|
if (muted) {
|
|
video.muted = true;
|
|
} else {
|
|
video.muted = false;
|
|
video.volume = masterVolume * musicVolume;
|
|
}
|
|
}
|
|
}, [masterVolume, musicVolume, muted, audioSrc]);
|
|
|
|
// Manage volume for audio
|
|
useEffect(() => {
|
|
const audio = audioRef.current;
|
|
if (!audio || !audioSrc) return;
|
|
|
|
if (muted) {
|
|
audio.muted = true;
|
|
} else {
|
|
audio.muted = false;
|
|
audio.volume = masterVolume * musicVolume;
|
|
}
|
|
}, [masterVolume, musicVolume, muted, audioSrc]);
|
|
|
|
// Setup event handlers for video
|
|
useEffect(() => {
|
|
const video = videoRef.current;
|
|
if (!video) return;
|
|
|
|
const handleEnded = () => {
|
|
if (onEnded) onEnded();
|
|
};
|
|
|
|
video.addEventListener('ended', handleEnded);
|
|
|
|
return () => {
|
|
video.removeEventListener('ended', handleEnded);
|
|
};
|
|
}, [onEnded]);
|
|
|
|
return (
|
|
<div className="video-player" style={{ width, height }}>
|
|
<video
|
|
ref={videoRef}
|
|
src={src}
|
|
preload="auto"
|
|
playsInline
|
|
width="100%"
|
|
height="100%"
|
|
muted={!!audioSrc || muted} // Always mute video if using separate audio
|
|
/>
|
|
{audioSrc && (
|
|
<audio
|
|
ref={audioRef}
|
|
src={audioSrc}
|
|
preload="auto"
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default VideoPlayer; |