videofolxtv/client/src/components/quality-indicator.tsx
sebastjanartic 8e8c07f34c Show video loading speed and quality indicators
Add detailed bitrate and download speed monitoring to the video player, along with visual indicators for buffering and quality levels.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 11420304-80a9-4ef2-adff-cbdaa418ffa8
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/11420304-80a9-4ef2-adff-cbdaa418ffa8/Qpg7dKb
2025-08-07 09:22:30 +00:00

142 lines
4.6 KiB
TypeScript

import { useState, useEffect } from "react";
import { Signal, Activity, Wifi } from "lucide-react";
interface QualityIndicatorProps {
hlsInstance: any;
className?: string;
}
export default function QualityIndicator({ hlsInstance, className = "" }: QualityIndicatorProps) {
const [currentQuality, setCurrentQuality] = useState<string>("");
const [currentBitrate, setCurrentBitrate] = useState<number>(0);
const [networkType, setNetworkType] = useState<string>("");
const [bufferHealth, setBufferHealth] = useState<"good" | "medium" | "poor">("good");
const [downloadSpeed, setDownloadSpeed] = useState<number>(0);
const [isBuffering, setIsBuffering] = useState<boolean>(false);
useEffect(() => {
if (!hlsInstance) return;
// Monitor quality changes
const handleLevelSwitch = (event: any, data: any) => {
const level = hlsInstance.levels[data.level];
setCurrentQuality(`${level.height}p`);
setCurrentBitrate(Math.round(level.bitrate / 1000)); // Convert to kbps
console.log(`Preklopil na: ${level.height}p @ ${Math.round(level.bitrate/1000)}kbps`);
};
// Monitor buffer health
const handleBufferAppending = () => {
const video = hlsInstance.media;
if (video && video.buffered.length > 0) {
const bufferLevel = video.buffered.end(video.buffered.length - 1) - video.currentTime;
if (bufferLevel > 10) {
setBufferHealth("good");
} else if (bufferLevel > 3) {
setBufferHealth("medium");
} else {
setBufferHealth("poor");
}
}
};
// Monitor loading progress and speed
const handleFragLoaded = (event: any, data: any) => {
const stats = data.stats;
if (stats) {
const speed = (stats.total * 8) / (stats.loading.end - stats.loading.start); // bits per ms
const speedKbps = Math.round(speed); // Convert to kbps
setDownloadSpeed(speedKbps);
console.log(`Download hitrost: ${speedKbps} kbps`);
}
};
const handleWaiting = () => setIsBuffering(true);
const handlePlaying = () => setIsBuffering(false);
hlsInstance.on('hlsLevelSwitched', handleLevelSwitch);
hlsInstance.on('hlsBufferAppending', handleBufferAppending);
hlsInstance.on('hlsFragLoaded', handleFragLoaded);
const video = hlsInstance.media;
if (video) {
video.addEventListener('waiting', handleWaiting);
video.addEventListener('playing', handlePlaying);
}
// Detect network connection
const connection = (navigator as any).connection;
if (connection) {
setNetworkType(connection.effectiveType || "unknown");
const handleConnectionChange = () => {
setNetworkType(connection.effectiveType || "unknown");
};
connection.addEventListener('change', handleConnectionChange);
return () => {
connection.removeEventListener('change', handleConnectionChange);
};
}
return () => {
if (hlsInstance) {
hlsInstance.off('hlsLevelSwitched', handleLevelSwitch);
hlsInstance.off('hlsBufferAppending', handleBufferAppending);
hlsInstance.off('hlsFragLoaded', handleFragLoaded);
}
if (video) {
video.removeEventListener('waiting', handleWaiting);
video.removeEventListener('playing', handlePlaying);
}
};
}, [hlsInstance]);
const getSignalColor = () => {
switch (bufferHealth) {
case "good": return "text-green-500";
case "medium": return "text-yellow-500";
case "poor": return "text-red-500";
default: return "text-gray-500";
}
};
if (!currentQuality) return null;
return (
<div className={`flex flex-col gap-1 text-xs text-white bg-black/70 px-3 py-2 rounded ${className}`}>
{/* Quality and Signal */}
<div className="flex items-center gap-2">
<Signal className={`w-3 h-3 ${getSignalColor()}`} />
<span className="font-semibold">{currentQuality}</span>
{isBuffering && (
<Activity className="w-3 h-3 text-yellow-400 animate-pulse" />
)}
</div>
{/* Bitrate */}
{currentBitrate > 0 && (
<div className="flex items-center gap-1">
<span className="opacity-75">Bitrate:</span>
<span>{currentBitrate} kbps</span>
</div>
)}
{/* Download Speed */}
{downloadSpeed > 0 && (
<div className="flex items-center gap-1">
<Wifi className="w-3 h-3" />
<span>{downloadSpeed} kbps</span>
</div>
)}
{/* Network Type */}
{networkType && (
<div className="opacity-75">
Omrežje: {networkType}
</div>
)}
</div>
);
}