feat: Improve video playback state syncing for AirPlay and add unoptimized and referrerPolicy to image components.

This commit is contained in:
kuekhaoyang
2025-11-24 16:54:20 +08:00
parent 40d3fb2ce5
commit 66be634b53
6 changed files with 30 additions and 12 deletions
+2
View File
@@ -45,6 +45,8 @@ export const MovieCard = memo(function MovieCard({ movie, onMovieClick }: MovieC
className="object-cover transition-transform duration-300 group-hover:scale-105 rounded-[var(--radius-2xl)]"
sizes="(max-width: 640px) 50vw, (max-width: 768px) 33vw, (max-width: 1024px) 25vw, 20vw"
loading="lazy"
unoptimized
referrerPolicy="no-referrer"
onError={(e) => {
const target = e.currentTarget as HTMLImageElement;
target.style.display = 'none';
+1
View File
@@ -73,6 +73,7 @@ export function DesktopVideoPlayer({
className="w-full h-full object-contain"
src={src}
poster={poster}
x-webkit-airplay="allow"
onPlay={handlePlay}
onPause={handlePause}
onTimeUpdate={handleTimeUpdateEvent}
@@ -123,7 +123,8 @@ export function usePlaybackControls({
videoRef,
isDraggingProgressRef,
setCurrentTime,
setDuration
setDuration,
setIsPlaying
});
return {
@@ -135,7 +135,8 @@ export function useMobilePlaybackControls({
videoRef,
isDraggingProgressRef,
setCurrentTime,
setDuration
setDuration,
setIsPlaying
});
return {
+21 -10
View File
@@ -6,6 +6,7 @@ interface UsePlaybackPollingProps {
isDraggingProgressRef: React.MutableRefObject<boolean>;
setCurrentTime: (time: number) => void;
setDuration: (duration: number) => void;
setIsPlaying: (playing: boolean) => void;
}
/**
@@ -17,24 +18,34 @@ export function usePlaybackPolling({
videoRef,
isDraggingProgressRef,
setCurrentTime,
setDuration
setDuration,
setIsPlaying
}: UsePlaybackPollingProps) {
useEffect(() => {
if (!isPlaying || !videoRef.current) return;
if (!videoRef.current) return;
const interval = setInterval(() => {
if (videoRef.current && !isDraggingProgressRef.current) {
const current = videoRef.current.currentTime;
const total = videoRef.current.duration;
if (videoRef.current) {
// Sync play/pause state (crucial for AirPlay where events might be missed)
const isVideoPaused = videoRef.current.paused;
if (isVideoPaused === isPlaying && !isDraggingProgressRef.current) {
// State mismatch detected (e.g. AirPlay paused but app thinks playing)
setIsPlaying(!isVideoPaused);
}
// Only update if significantly different to avoid jitter
setCurrentTime(current);
if (!isNaN(total) && total > 0) {
setDuration(total);
if (!isDraggingProgressRef.current && !isVideoPaused) {
const current = videoRef.current.currentTime;
const total = videoRef.current.duration;
// Only update if significantly different to avoid jitter
setCurrentTime(current);
if (!isNaN(total) && total > 0) {
setDuration(total);
}
}
}
}, 500); // Poll every 500ms
return () => clearInterval(interval);
}, [isPlaying, videoRef, isDraggingProgressRef, setCurrentTime, setDuration]);
}, [isPlaying, videoRef, isDraggingProgressRef, setCurrentTime, setDuration, setIsPlaying]);
}
+2
View File
@@ -60,6 +60,8 @@ export const VideoCard = memo<VideoCardProps>(({
className="object-cover rounded-[var(--radius-2xl)]"
sizes="(max-width: 640px) 33vw, (max-width: 1024px) 20vw, 16vw"
loading="lazy"
unoptimized
referrerPolicy="no-referrer"
onError={(e) => {
const target = e.currentTarget as HTMLImageElement;
target.style.opacity = '0';