Add live video streaming functionality with interactive player controls

Implement a new page for live video streaming using Video.js. The player includes standard controls, responsive design, HLS support, and autoplay functionality. Metadata for SEO and social sharing is also configured.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 45a1dcfc-f8a2-475a-a6b9-96fbb841dc27
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/60d372ff-2c10-46c7-b01b-10c3435136b0/45a1dcfc-f8a2-475a-a6b9-96fbb841dc27/CdguB3F
This commit is contained in:
sebastjanartic 2025-09-25 20:28:39 +00:00
parent 74b64a0067
commit b41387f04f

View File

@ -0,0 +1,373 @@
import { useState, useEffect, useRef } from 'react';
import { ChevronLeft, Maximize, Volume2, VolumeX, Radio } from 'lucide-react';
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<void>;
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;
}
}
export default function LivePage() {
const videoRef = useRef<HTMLVideoElement>(null);
const playerRef = useRef<VideoJSPlayer | null>(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);
// HLS stream URL
const streamUrl = 'https://cdne.folxplay.tv/fxt/streams/ch-4/master.m3u8';
useEffect(() => {
// Set page meta tags
document.title = 'LIVE Stream | video.folx.tv';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Živý prenos na video.folx.tv - sledujte exkluzívny obsah v reálnom čase.');
}
const updateMetaTag = (property: string, content: string) => {
let meta = document.querySelector(`meta[property="${property}"]`);
if (!meta) {
meta = document.createElement('meta');
meta.setAttribute('property', property);
document.head.appendChild(meta);
}
meta.setAttribute('content', content);
};
updateMetaTag('og:title', 'LIVE Stream - video.folx.tv');
updateMetaTag('og:description', 'Živý prenos na video.folx.tv - sledujte exkluzívny obsah v reálnom čase.');
updateMetaTag('og:type', 'video.other');
}, []);
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);
// Auto-play live stream
player?.play().catch((error: any) => {
console.log('Live stream autoplay prevented:', error);
});
});
// Event listeners
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(!!player?.isFullscreen());
});
player.on('error', (e: any) => {
console.error('🚨 Live stream error:', e);
setIsLoading(false);
});
playerRef.current = player;
} catch (error) {
console.error('Failed to initialize live stream player:', error);
setIsLoading(false);
}
};
initializePlayer();
return () => {
if (player) {
player.dispose();
}
};
}, []);
const togglePlayPause = () => {
if (playerRef.current) {
if (isPlaying) {
playerRef.current.pause();
} else {
playerRef.current.play();
}
}
};
const toggleMute = () => {
if (playerRef.current) {
playerRef.current.muted(!isMuted);
}
};
const toggleFullscreen = () => {
if (playerRef.current) {
playerRef.current.requestFullscreen();
}
};
if (isLoading) {
return (
<div className="min-h-screen bg-bunny-dark flex items-center justify-center">
<div className="text-center">
<div className="w-16 h-16 bg-red-600 rounded-lg flex items-center justify-center shadow-lg animate-pulse mb-4 mx-auto">
<Radio className="w-8 h-8 text-white" />
</div>
<h3 className="text-white text-xl font-bold mb-2">video.folx.tv</h3>
<p className="text-bunny-light">Connecting to live stream...</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-bunny-dark">
{/* Header */}
<div className="bg-bunny-darker border-b border-white/10">
<div className="container mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<Link href="/" className="flex items-center space-x-3 hover:opacity-80 transition-opacity">
<Button variant="ghost" size="sm" className="text-white hover:bg-white/10">
<ChevronLeft className="w-4 h-4 mr-2" />
Back
</Button>
</Link>
<div className="flex items-center space-x-3">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-red-500 rounded-full animate-pulse"></div>
<span className="text-red-500 font-bold text-sm uppercase tracking-wide">LIVE</span>
</div>
</div>
</div>
</div>
</div>
<div className="container mx-auto px-4 py-6">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
{/* Main Video Player */}
<div className="lg:col-span-3">
<div className="bg-black rounded-lg overflow-hidden shadow-2xl">
<div className="relative aspect-video">
<video
ref={videoRef}
className="video-js vjs-default-skin w-full h-full"
playsInline
data-setup="{}"
/>
{/* Live Overlay */}
<div className="absolute top-4 left-4 z-10">
<div className="flex items-center space-x-2 bg-red-600/90 text-white px-3 py-1 rounded-full text-sm font-semibold">
<div className="w-2 h-2 bg-white rounded-full animate-pulse"></div>
<span>LIVE</span>
</div>
</div>
{/* Custom Controls Overlay */}
<div className="absolute bottom-4 left-4 right-4 flex items-center justify-between z-10 bg-black/50 rounded-lg p-3">
<div className="flex items-center space-x-3">
<Button
variant="ghost"
size="sm"
onClick={togglePlayPause}
className="text-white hover:bg-white/20"
data-testid="button-play-pause"
>
{isPlaying ? '⏸️' : '▶️'}
</Button>
<Button
variant="ghost"
size="sm"
onClick={toggleMute}
className="text-white hover:bg-white/20"
data-testid="button-mute"
>
{isMuted ? <VolumeX className="w-4 h-4" /> : <Volume2 className="w-4 h-4" />}
</Button>
<span className="text-white text-sm">
Live Stream - CH4
</span>
</div>
<Button
variant="ghost"
size="sm"
onClick={toggleFullscreen}
className="text-white hover:bg-white/20"
data-testid="button-fullscreen"
>
<Maximize className="w-4 h-4" />
</Button>
</div>
</div>
</div>
{/* Stream Info */}
<div className="mt-6 space-y-4">
<div className="flex items-center space-x-3">
<div className="w-4 h-4 bg-red-500 rounded-full animate-pulse"></div>
<h1 className="text-2xl font-bold text-white">
FOLX TV Live Stream
</h1>
</div>
<p className="text-bunny-light text-lg leading-relaxed">
Sledujte živý prenos FOLX TV s exkluzívnym obsahom a najnovšími videoami.
Stream prebieha 24/7 s najlepšími hitmi a videoklipmi.
</p>
<div className="flex items-center gap-4 text-sm text-bunny-muted">
<span className="flex items-center space-x-2">
<Radio className="w-4 h-4" />
<span>Live Stream</span>
</span>
<span>Channel 4</span>
<span>HD Quality</span>
</div>
</div>
</div>
{/* Sidebar */}
<div className="lg:col-span-1 space-y-6">
{/* Ad Space */}
<div className="bg-bunny-darker rounded-lg p-4">
<AdSenseAd
adSlot="9876543210"
adFormat="vertical"
style={{ width: '100%', height: '250px' }}
/>
</div>
{/* Live Chat or Additional Content */}
<div className="bg-bunny-darker rounded-lg p-6 space-y-4">
<h3 className="text-white font-bold text-lg flex items-center space-x-2">
<Radio className="w-5 h-5 text-red-500" />
<span>Live Info</span>
</h3>
<div className="space-y-3 text-sm text-bunny-light">
<div className="flex justify-between">
<span>Status:</span>
<span className="text-red-500 font-semibold"> LIVE</span>
</div>
<div className="flex justify-between">
<span>Quality:</span>
<span>Auto (HD)</span>
</div>
<div className="flex justify-between">
<span>Channel:</span>
<span>CH-4</span>
</div>
<div className="flex justify-between">
<span>Format:</span>
<span>HLS</span>
</div>
</div>
</div>
{/* Navigation Links */}
<div className="bg-bunny-darker rounded-lg p-6 space-y-4">
<h3 className="text-white font-bold text-lg">Explore</h3>
<div className="space-y-2">
<Link href="/" className="block text-bunny-light hover:text-white transition-colors">
Back to Home
</Link>
<Link href="/folx-stadl" className="block text-bunny-light hover:text-white transition-colors">
FOLX STADL Episodes
</Link>
<Link href="/geschichte-lied" className="block text-bunny-light hover:text-white transition-colors">
Geschichte des Liedes
</Link>
<Link href="/gipfelstammtisch" className="block text-bunny-light hover:text-white transition-colors">
Gipfelstammtisch
</Link>
</div>
</div>
</div>
</div>
</div>
</div>
);
}