improve stream previews
This commit is contained in:
@@ -18,6 +18,22 @@ function formatTime(ms: number): string {
|
||||
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
function DurationPill({
|
||||
currentTimeMs,
|
||||
totalDurationMs,
|
||||
}: {
|
||||
currentTimeMs: number;
|
||||
totalDurationMs: number;
|
||||
}) {
|
||||
return (
|
||||
<div className="absolute top-3 right-3 rounded-full bg-white/10 px-2.5 py-1 backdrop-blur-sm">
|
||||
<span className="font-mono text-xs text-white/80">
|
||||
{formatTime(currentTimeMs)} / {formatTime(totalDurationMs)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function MediaParticleView({
|
||||
particle,
|
||||
onEnded,
|
||||
@@ -35,7 +51,10 @@ export function MediaParticleView({
|
||||
const [audioEl, setAudioEl] = useState<HTMLAudioElement | null>(null);
|
||||
const [currentTimeMs, setCurrentTimeMs] = useState(0);
|
||||
|
||||
const audioSource = useAudioSource(audioEl);
|
||||
const data = particle.data as MediaParticleData;
|
||||
const isAudio = data.mime_type?.startsWith("audio/");
|
||||
|
||||
const audioSource = useAudioSource(isAudio ? audioEl : null);
|
||||
|
||||
useEffect(() => {
|
||||
if (cachedUrl) {
|
||||
@@ -60,8 +79,19 @@ export function MediaParticleView({
|
||||
};
|
||||
}, [particle.id, cachedUrl, cacheDownloadUrl]);
|
||||
|
||||
// Start audio playback once the AudioContext source is ready
|
||||
useEffect(() => {
|
||||
const el = videoRef.current ?? audioRef.current;
|
||||
const el = audioRef.current;
|
||||
if (!el || !audioSource) return;
|
||||
|
||||
if (!paused) {
|
||||
el.play().catch(() => {});
|
||||
}
|
||||
}, [audioSource, paused]);
|
||||
|
||||
// Handle video pause/resume
|
||||
useEffect(() => {
|
||||
const el = videoRef.current;
|
||||
if (!el) return;
|
||||
|
||||
if (paused) {
|
||||
@@ -71,6 +101,16 @@ export function MediaParticleView({
|
||||
}
|
||||
}, [paused]);
|
||||
|
||||
// Handle audio pause/resume (after initial play)
|
||||
useEffect(() => {
|
||||
const el = audioRef.current;
|
||||
if (!el || !audioSource) return;
|
||||
|
||||
if (paused) {
|
||||
el.pause();
|
||||
}
|
||||
}, [paused, audioSource]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="text-muted-foreground flex items-center justify-center text-sm">
|
||||
@@ -83,9 +123,6 @@ export function MediaParticleView({
|
||||
return <Skeleton className="h-full w-full rounded-none" />;
|
||||
}
|
||||
|
||||
const data = particle.data as MediaParticleData;
|
||||
const isAudio = data.mime_type?.startsWith("audio/");
|
||||
|
||||
if (isAudio) {
|
||||
return (
|
||||
<div className="relative flex h-full w-full items-center justify-center bg-black/90">
|
||||
@@ -94,8 +131,8 @@ export function MediaParticleView({
|
||||
audioRef.current = el;
|
||||
setAudioEl(el);
|
||||
}}
|
||||
crossOrigin="anonymous"
|
||||
src={url}
|
||||
autoPlay
|
||||
onEnded={onEnded}
|
||||
onTimeUpdate={(e) => {
|
||||
setCurrentTimeMs(e.currentTarget.currentTime * 1000);
|
||||
@@ -106,23 +143,31 @@ export function MediaParticleView({
|
||||
<AudioLevelBars sourceNode={audioSource.sourceNode} />
|
||||
)}
|
||||
|
||||
<div className="absolute top-3 right-3 rounded-full bg-black/30 px-2.5 py-1 backdrop-blur-sm">
|
||||
<span className="font-mono text-xs text-white/80">
|
||||
{formatTime(currentTimeMs)} / {formatTime(data.duration_ms)}
|
||||
</span>
|
||||
</div>
|
||||
<DurationPill
|
||||
currentTimeMs={currentTimeMs}
|
||||
totalDurationMs={data.duration_ms}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={url}
|
||||
autoPlay
|
||||
playsInline
|
||||
onEnded={onEnded}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
<div className="relative h-full w-full">
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={url}
|
||||
autoPlay
|
||||
playsInline
|
||||
onEnded={onEnded}
|
||||
onTimeUpdate={(e) => {
|
||||
setCurrentTimeMs(e.currentTarget.currentTime * 1000);
|
||||
}}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
<DurationPill
|
||||
currentTimeMs={currentTimeMs}
|
||||
totalDurationMs={data.duration_ms}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user