feat: Add poster prop to VideoControls component

This commit is contained in:
Peifan Li
2025-12-27 21:07:15 -05:00
parent db16896ead
commit 3632151811
2 changed files with 19 additions and 3 deletions

View File

@@ -43,6 +43,7 @@ interface VideoControlsProps {
onSubtitlesToggle?: (enabled: boolean) => void;
onLoopToggle?: (enabled: boolean) => void;
onEnded?: () => void;
poster?: string;
}
const VideoControls: React.FC<VideoControlsProps> = ({
@@ -56,7 +57,8 @@ const VideoControls: React.FC<VideoControlsProps> = ({
subtitlesEnabled: initialSubtitlesEnabled = true,
onSubtitlesToggle,
onLoopToggle,
onEnded
onEnded,
poster
}) => {
const theme = useTheme();
const { t } = useLanguage();
@@ -131,6 +133,14 @@ const VideoControls: React.FC<VideoControlsProps> = ({
videoElement.src = src;
// For mobile browsers, try to load the video
const handleLoadedMetadata = () => {
setIsLoading(false);
if (loadTimeoutRef.current) {
clearTimeout(loadTimeoutRef.current);
loadTimeoutRef.current = null;
}
};
const handleCanPlay = () => {
setIsLoading(false);
if (loadTimeoutRef.current) {
@@ -156,11 +166,13 @@ const VideoControls: React.FC<VideoControlsProps> = ({
}
};
videoElement.addEventListener('loadedmetadata', handleLoadedMetadata);
videoElement.addEventListener('canplay', handleCanPlay);
videoElement.addEventListener('loadeddata', handleLoadedData);
videoElement.addEventListener('error', handleError);
return () => {
videoElement.removeEventListener('loadedmetadata', handleLoadedMetadata);
videoElement.removeEventListener('canplay', handleCanPlay);
videoElement.removeEventListener('loadeddata', handleLoadedData);
videoElement.removeEventListener('error', handleError);
@@ -726,6 +738,7 @@ const VideoControls: React.FC<VideoControlsProps> = ({
}}
playsInline
crossOrigin="anonymous"
poster={poster}
>
{subtitles && subtitles.map((subtitle) => (
<track

View File

@@ -27,7 +27,6 @@ import { Collection, Video } from '../types';
import { getRecommendations } from '../utils/recommendations';
import { validateUrlForOpen } from '../utils/urlValidation';
const API_URL = import.meta.env.VITE_API_URL;
const BACKEND_URL = import.meta.env.VITE_BACKEND_URL;
const VideoPlayer: React.FC = () => {
const { id } = useParams<{ id: string }>();
@@ -472,7 +471,7 @@ const VideoPlayer: React.FC = () => {
onSuccess: (data, visibility) => {
if (data.success) {
queryClient.setQueryData(['video', id], (old: Video | undefined) => old ? { ...old, visibility } : old);
queryClient.setQueryData(['videos'], (old: Video[] | undefined) =>
queryClient.setQueryData(['videos'], (old: Video[] | undefined) =>
old ? old.map(v => v.id === id ? { ...v, visibility } : v) : []
);
showSnackbar(visibility === 1 ? t('showVideo') : t('hideVideo'), 'success');
@@ -613,6 +612,9 @@ const VideoPlayer: React.FC = () => {
}
};
// Get thumbnail URL for poster
const posterUrl = useCloudStorageUrl(video?.thumbnailPath, 'thumbnail');
return (
<Container maxWidth={false} disableGutters sx={{ py: { xs: 0, md: 4 }, px: { xs: 0, md: 2 } }}>
<Grid container spacing={{ xs: 0, md: 4 }}>
@@ -620,6 +622,7 @@ const VideoPlayer: React.FC = () => {
<Grid size={{ xs: 12, lg: 8 }}>
<VideoControls
src={videoUrl || video?.sourceUrl}
poster={posterUrl || video?.thumbnailUrl}
autoPlay={autoPlay}
autoLoop={autoLoop}
onTimeUpdate={handleTimeUpdate}