Improve video preview by adding HLS support for smoother playback

Integrate HLS.js into the video card component to enable adaptive streaming for video previews, enhancing the user experience by ensuring smoother playback across different network conditions.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 2eb1084e-b728-4449-9231-f1665924c8d5
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/2eb1084e-b728-4449-9231-f1665924c8d5/P3O2FU7
This commit is contained in:
sebastjanartic 2025-08-29 10:08:22 +00:00
parent e0a01aa0b5
commit 50cc4a1346

View File

@ -2,6 +2,8 @@ import { Play } from "lucide-react";
import { type Video } from "@shared/schema";
import HLSPreviewThumbnail from "./hls-preview-thumbnail";
import { useState, useRef, useEffect } from "react";
// @ts-ignore
import Hls from 'hls.js';
interface VideoCardProps {
video: Video;
@ -46,6 +48,8 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
const [isHovered, setIsHovered] = useState(false);
const [showPreview, setShowPreview] = useState(false);
const hoverTimeoutRef = useRef<NodeJS.Timeout>();
const videoRef = useRef<HTMLVideoElement>(null);
const hlsRef = useRef<any>(null);
// Delay preview start to avoid loading on quick mouse passes
useEffect(() => {
@ -67,6 +71,52 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
};
}, [isHovered]);
// Setup HLS when preview is shown
useEffect(() => {
if (showPreview && videoRef.current && video.videoUrl) {
const videoElement = videoRef.current;
if (Hls.isSupported()) {
console.log('Setting up HLS preview for:', video.title);
hlsRef.current = new Hls({
enableWorker: false,
lowLatencyMode: false,
backBufferLength: 30,
maxBufferLength: 60,
maxMaxBufferLength: 120,
maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.5,
startLevel: 0,
autoStartLoad: true,
debug: false,
});
hlsRef.current.loadSource(video.videoUrl);
hlsRef.current.attachMedia(videoElement);
hlsRef.current.on(Hls.Events.MANIFEST_PARSED, () => {
console.log('HLS manifest parsed, starting playback');
videoElement.play().catch(e => console.log('Autoplay failed:', e));
});
hlsRef.current.on(Hls.Events.ERROR, (event: any, data: any) => {
console.log('HLS error:', data);
});
} else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
// Safari native HLS support
videoElement.src = video.videoUrl;
videoElement.play().catch(e => console.log('Autoplay failed:', e));
}
}
return () => {
if (hlsRef.current) {
hlsRef.current.destroy();
hlsRef.current = null;
}
};
}, [showPreview, video.videoUrl]);
return (
<div
data-testid={`card-video-${video.id}`}
@ -111,29 +161,17 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
{showPreview && (
<div className="absolute inset-0">
<video
ref={videoRef}
className="w-full h-full object-cover"
style={{
objectPosition: video.faceCenterPosition || 'center center',
objectFit: 'cover'
}}
autoPlay
muted
loop
playsInline
preload="none"
onLoadStart={() => console.log('Preview loading for:', video.title)}
onError={(e) => console.log('Preview failed for:', video.title)}
onCanPlay={() => console.log('Preview ready for:', video.title)}
>
{/* Try MP4 source first for faster loading */}
{video.videoUrlMp4 && (
<source src={video.videoUrlMp4} type="video/mp4" />
)}
{/* Fallback to HLS if MP4 fails */}
{video.videoUrl && (
<source src={video.videoUrl} type="application/x-mpegURL" />
)}
</video>
/>
</div>
)}