|
|
|
@@ -19,11 +19,22 @@ function MediaCarousel({ |
|
|
|
const markPlaying = (i: number) => setPlaying(p => { const n = new Set(p); n.add(i); return n; }); |
|
|
|
const markPaused = (i: number) => setPlaying(p => { const n = new Set(p); n.delete(i); return n; }); |
|
|
|
|
|
|
|
const applyVolume = (v: HTMLVideoElement, item: MediaItem) => { |
|
|
|
const desired = item.volume ?? 1; |
|
|
|
v.volume = desired; |
|
|
|
v.muted = desired === 0; |
|
|
|
}; |
|
|
|
|
|
|
|
const togglePlay = (i: number) => { |
|
|
|
const v = videoRefs.current[i]; |
|
|
|
if (!v) return; |
|
|
|
if (v.paused) v.play().catch(() => {}); |
|
|
|
else v.pause(); |
|
|
|
if (v.paused) { |
|
|
|
const item = items[i]; |
|
|
|
if (item) applyVolume(v, item); |
|
|
|
v.play().catch(() => {}); |
|
|
|
} else { |
|
|
|
v.pause(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const prev = useCallback(() => setCurrent(i => (i - 1 + items.length) % items.length), [items.length]); |
|
|
|
@@ -48,8 +59,12 @@ function MediaCarousel({ |
|
|
|
if (idx !== current) { vid.pause(); return; } |
|
|
|
const item = items[idx]; |
|
|
|
if (item && isVideoUrl(item.url) && item.autoplay) { |
|
|
|
vid.muted = true; |
|
|
|
vid.play().catch(() => {}); |
|
|
|
applyVolume(vid, item); |
|
|
|
vid.play().catch(() => { |
|
|
|
// Browser blocked unmuted autoplay — fall back to muted. |
|
|
|
vid.muted = true; |
|
|
|
vid.play().catch(() => {}); |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, [current, items]); |
|
|
|
@@ -90,8 +105,8 @@ function MediaCarousel({ |
|
|
|
src={item.url} |
|
|
|
className="w-full h-full object-contain bg-black pointer-events-none" |
|
|
|
playsInline |
|
|
|
muted={!!item.autoplay} |
|
|
|
preload="metadata" |
|
|
|
onLoadedMetadata={(e) => applyVolume(e.currentTarget, item)} |
|
|
|
onPlay={() => markPlaying(i)} |
|
|
|
onPause={() => markPaused(i)} |
|
|
|
onEnded={() => markPaused(i)} |
|
|
|
@@ -178,11 +193,22 @@ function FullscreenViewer({ |
|
|
|
const markPlaying = (i: number) => setPlaying(p => { const n = new Set(p); n.add(i); return n; }); |
|
|
|
const markPaused = (i: number) => setPlaying(p => { const n = new Set(p); n.delete(i); return n; }); |
|
|
|
|
|
|
|
const applyVolume = (v: HTMLVideoElement, item: MediaItem) => { |
|
|
|
const desired = item.volume ?? 1; |
|
|
|
v.volume = desired; |
|
|
|
v.muted = desired === 0; |
|
|
|
}; |
|
|
|
|
|
|
|
const togglePlay = (i: number) => { |
|
|
|
const v = videoRefs.current[i]; |
|
|
|
if (!v) return; |
|
|
|
if (v.paused) v.play().catch(() => {}); |
|
|
|
else v.pause(); |
|
|
|
if (v.paused) { |
|
|
|
const item = items[i]; |
|
|
|
if (item) applyVolume(v, item); |
|
|
|
v.play().catch(() => {}); |
|
|
|
} else { |
|
|
|
v.pause(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const prev = useCallback(() => setCurrent(i => (i - 1 + items.length) % items.length), [items.length]); |
|
|
|
@@ -198,6 +224,23 @@ function FullscreenViewer({ |
|
|
|
return () => window.removeEventListener('keydown', onKey); |
|
|
|
}, [prev, next, onClose]); |
|
|
|
|
|
|
|
// Pause non-current videos in fullscreen |
|
|
|
useEffect(() => { |
|
|
|
Object.entries(videoRefs.current).forEach(([key, vid]) => { |
|
|
|
if (!vid) return; |
|
|
|
const idx = parseInt(key, 10); |
|
|
|
if (idx !== current) { vid.pause(); return; } |
|
|
|
const item = items[idx]; |
|
|
|
if (item && isVideoUrl(item.url) && item.autoplay) { |
|
|
|
applyVolume(vid, item); |
|
|
|
vid.play().catch(() => { |
|
|
|
vid.muted = true; |
|
|
|
vid.play().catch(() => {}); |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, [current, items]); |
|
|
|
|
|
|
|
const onTouchStart = (e: React.TouchEvent) => { touchStartX.current = e.touches[0].clientX; }; |
|
|
|
const onTouchEnd = (e: React.TouchEvent) => { |
|
|
|
if (touchStartX.current === null) return; |
|
|
|
@@ -231,8 +274,8 @@ function FullscreenViewer({ |
|
|
|
src={item.url} |
|
|
|
className="max-w-full max-h-full pointer-events-none" |
|
|
|
playsInline |
|
|
|
autoPlay={!!item.autoplay} |
|
|
|
muted={!!item.autoplay} |
|
|
|
preload="metadata" |
|
|
|
onLoadedMetadata={(e) => applyVolume(e.currentTarget, item)} |
|
|
|
onPlay={() => markPlaying(i)} |
|
|
|
onPause={() => markPaused(i)} |
|
|
|
onEnded={() => markPaused(i)} |
|
|
|
|