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
142 lines
4.6 KiB
TypeScript
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>
|
|
);
|
|
} |