From c5342f81640ac6e86968af6e7c3f4410d329644a Mon Sep 17 00:00:00 2001 From: sebastjanartic <45803536-sebastjanartic@users.noreply.replit.com> Date: Thu, 25 Sep 2025 20:36:37 +0000 Subject: [PATCH] Update live stream player to use HLS.js for improved playback Replaced Video.js with HLS.js for live streaming on the LivePage, improving compatibility and playback efficiency. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 45a1dcfc-f8a2-475a-a6b9-96fbb841dc27 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/60d372ff-2c10-46c7-b01b-10c3435136b0/45a1dcfc-f8a2-475a-a6b9-96fbb841dc27/VVQLpmQ --- client/src/pages/LivePage.tsx | 238 ++++++++++++++++++---------------- 1 file changed, 128 insertions(+), 110 deletions(-) diff --git a/client/src/pages/LivePage.tsx b/client/src/pages/LivePage.tsx index 0634bf0..e17343c 100644 --- a/client/src/pages/LivePage.tsx +++ b/client/src/pages/LivePage.tsx @@ -4,36 +4,21 @@ import { Link } from 'wouter'; import { Button } from '@/components/ui/button'; import AdSenseAd from '@/components/adsense-ad'; -interface VideoJSPlayer { - ready: (callback: () => void) => void; - src: (source?: any) => any; - dispose: () => void; - play: () => Promise; - pause: () => void; - currentTime: (time?: number) => number; - duration: () => number; - volume: (vol?: number) => number; - muted: (mute?: boolean) => boolean; - requestFullscreen: () => void; - on: (event: string, handler: any) => void; - off: (event: string, handler?: any) => void; - tech: (deep?: boolean) => any; -} - declare global { interface Window { - videojs: any; + Hls: any; } } export default function LivePage() { const videoRef = useRef(null); - const playerRef = useRef(null); + const hlsRef = useRef(null); const [isLoading, setIsLoading] = useState(true); const [isPlaying, setIsPlaying] = useState(false); const [volume, setVolume] = useState(1); const [isMuted, setIsMuted] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false); + const [error, setError] = useState(null); // HLS stream URL const streamUrl = 'https://cdne.folxplay.tv/fxt/streams/ch-4/master.m3u8'; @@ -63,97 +48,103 @@ export default function LivePage() { }, []); useEffect(() => { - let player: VideoJSPlayer | null = null; - const initializePlayer = async () => { if (!videoRef.current) return; - // Load Video.js dynamically - if (!window.videojs) { - const script = document.createElement('script'); - script.src = 'https://vjs.zencdn.net/8.8.0/video.min.js'; - script.async = true; - document.head.appendChild(script); - - const link = document.createElement('link'); - link.href = 'https://vjs.zencdn.net/8.8.0/video-js.css'; - link.rel = 'stylesheet'; - document.head.appendChild(link); - - await new Promise((resolve) => { - script.onload = resolve; - }); - } - try { - player = window.videojs(videoRef.current, { - controls: true, - fluid: true, - responsive: true, - aspectRatio: '16:9', - playbackRates: [0.5, 1, 1.25, 1.5, 2], - html5: { - hls: { - enableLowInitialPlaylist: true, - smoothQualityChange: true, - overrideNative: true - } - }, - techOrder: ['html5'], - sources: [{ - src: streamUrl, - type: 'application/x-mpegURL' - }] - }); - - player.ready(() => { - console.log('πŸ”΄ Live stream player ready'); - setIsLoading(false); + // Load HLS.js + if (!window.Hls) { + const script = document.createElement('script'); + script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest'; + script.async = true; + document.head.appendChild(script); - // Auto-play live stream - if (player) { - player.play().catch((error: any) => { - console.log('Live stream autoplay prevented:', error); - }); - } - }); - - // Event listeners - if (player) { - player.on('play', () => { - console.log('πŸ”΄ Live stream started'); - setIsPlaying(true); - }); - - player.on('pause', () => { - console.log('⏸️ Live stream paused'); - setIsPlaying(false); - }); - - player.on('volumechange', () => { - if (player) { - const vol = player.volume(); - const muted = player.muted(); - setVolume(vol); - setIsMuted(muted); - console.log('πŸ”Š Live stream volume change:', { volume: vol, muted }); - } - }); - - player.on('fullscreenchange', () => { - setIsFullscreen(document.fullscreenElement !== null); - }); - - player.on('error', (e: any) => { - console.error('🚨 Live stream error:', e); - setIsLoading(false); + await new Promise((resolve, reject) => { + script.onload = resolve; + script.onerror = reject; }); } - playerRef.current = player; + const video = videoRef.current; + + if (window.Hls && window.Hls.isSupported()) { + console.log('πŸ”΄ Initializing HLS.js for live stream'); + const hls = new window.Hls({ + enableWorker: false, + lowLatencyMode: true, + backBufferLength: 90 + }); + + hls.loadSource(streamUrl); + hls.attachMedia(video); + + hls.on(window.Hls.Events.MEDIA_ATTACHED, () => { + console.log('πŸ”΄ HLS media attached'); + }); + + hls.on(window.Hls.Events.MANIFEST_PARSED, () => { + console.log('πŸ”΄ HLS manifest parsed, starting playback'); + setIsLoading(false); + // Try auto-play + video.play().catch((e) => { + console.log('Auto-play blocked:', e); + setError('Kliknite play za zagon stream-a'); + }); + }); + + hls.on(window.Hls.Events.ERROR, (event: any, data: any) => { + console.error('🚨 HLS Error:', event, data); + if (data.fatal) { + switch (data.type) { + case window.Hls.ErrorTypes.NETWORK_ERROR: + setError('Napaka pri povezavi s streamom'); + hls.startLoad(); + break; + case window.Hls.ErrorTypes.MEDIA_ERROR: + setError('Napaka pri predvajanju videa'); + hls.recoverMediaError(); + break; + default: + setError('Neznana napaka pri streamingu'); + break; + } + } + }); + + hlsRef.current = hls; + + } else if (video.canPlayType('application/vnd.apple.mpegurl')) { + // Native HLS support (Safari) + console.log('πŸ”΄ Using native HLS support'); + video.src = streamUrl; + video.addEventListener('loadedmetadata', () => { + console.log('πŸ”΄ Native HLS loaded'); + setIsLoading(false); + }); + } else { + setError('HLS not supported in this browser'); + setIsLoading(false); + } + + // Video event listeners + video.addEventListener('play', () => { + console.log('πŸ”΄ Live stream playing'); + setIsPlaying(true); + }); + + video.addEventListener('pause', () => { + console.log('⏸️ Live stream paused'); + setIsPlaying(false); + }); + + video.addEventListener('volumechange', () => { + setVolume(video.volume); + setIsMuted(video.muted); + }); } catch (error) { - console.error('Failed to initialize live stream player:', error); + console.error('Failed to initialize live stream:', error); + setError('Napaka pri nalaganju stream-a'); setIsLoading(false); } }; @@ -161,31 +152,36 @@ export default function LivePage() { initializePlayer(); return () => { - if (player) { - player.dispose(); + if (hlsRef.current) { + hlsRef.current.destroy(); } }; }, []); const togglePlayPause = () => { - if (playerRef.current) { + if (videoRef.current) { if (isPlaying) { - playerRef.current.pause(); + videoRef.current.pause(); } else { - playerRef.current.play(); + videoRef.current.play().catch((e) => { + console.log('Play failed:', e); + setError('Napaka pri predvajanju'); + }); } } }; const toggleMute = () => { - if (playerRef.current) { - playerRef.current.muted(!isMuted); + if (videoRef.current) { + videoRef.current.muted = !isMuted; } }; const toggleFullscreen = () => { - if (playerRef.current) { - playerRef.current.requestFullscreen(); + if (videoRef.current) { + if (videoRef.current.requestFullscreen) { + videoRef.current.requestFullscreen(); + } } }; @@ -232,11 +228,33 @@ export default function LivePage() {
+ {error && ( +
+
+ +

Stream Error

+

{error}

+ +
+
+ )} +