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
This commit is contained in:
parent
3b648347cd
commit
8e8c07f34c
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Signal } from "lucide-react";
|
import { Signal, Activity, Wifi } from "lucide-react";
|
||||||
|
|
||||||
interface QualityIndicatorProps {
|
interface QualityIndicatorProps {
|
||||||
hlsInstance: any;
|
hlsInstance: any;
|
||||||
@ -8,8 +8,11 @@ interface QualityIndicatorProps {
|
|||||||
|
|
||||||
export default function QualityIndicator({ hlsInstance, className = "" }: QualityIndicatorProps) {
|
export default function QualityIndicator({ hlsInstance, className = "" }: QualityIndicatorProps) {
|
||||||
const [currentQuality, setCurrentQuality] = useState<string>("");
|
const [currentQuality, setCurrentQuality] = useState<string>("");
|
||||||
|
const [currentBitrate, setCurrentBitrate] = useState<number>(0);
|
||||||
const [networkType, setNetworkType] = useState<string>("");
|
const [networkType, setNetworkType] = useState<string>("");
|
||||||
const [bufferHealth, setBufferHealth] = useState<"good" | "medium" | "poor">("good");
|
const [bufferHealth, setBufferHealth] = useState<"good" | "medium" | "poor">("good");
|
||||||
|
const [downloadSpeed, setDownloadSpeed] = useState<number>(0);
|
||||||
|
const [isBuffering, setIsBuffering] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hlsInstance) return;
|
if (!hlsInstance) return;
|
||||||
@ -18,6 +21,8 @@ export default function QualityIndicator({ hlsInstance, className = "" }: Qualit
|
|||||||
const handleLevelSwitch = (event: any, data: any) => {
|
const handleLevelSwitch = (event: any, data: any) => {
|
||||||
const level = hlsInstance.levels[data.level];
|
const level = hlsInstance.levels[data.level];
|
||||||
setCurrentQuality(`${level.height}p`);
|
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
|
// Monitor buffer health
|
||||||
@ -35,8 +40,29 @@ export default function QualityIndicator({ hlsInstance, className = "" }: Qualit
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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('hlsLevelSwitched', handleLevelSwitch);
|
||||||
hlsInstance.on('hlsBufferAppending', handleBufferAppending);
|
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
|
// Detect network connection
|
||||||
const connection = (navigator as any).connection;
|
const connection = (navigator as any).connection;
|
||||||
@ -58,6 +84,11 @@ export default function QualityIndicator({ hlsInstance, className = "" }: Qualit
|
|||||||
if (hlsInstance) {
|
if (hlsInstance) {
|
||||||
hlsInstance.off('hlsLevelSwitched', handleLevelSwitch);
|
hlsInstance.off('hlsLevelSwitched', handleLevelSwitch);
|
||||||
hlsInstance.off('hlsBufferAppending', handleBufferAppending);
|
hlsInstance.off('hlsBufferAppending', handleBufferAppending);
|
||||||
|
hlsInstance.off('hlsFragLoaded', handleFragLoaded);
|
||||||
|
}
|
||||||
|
if (video) {
|
||||||
|
video.removeEventListener('waiting', handleWaiting);
|
||||||
|
video.removeEventListener('playing', handlePlaying);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [hlsInstance]);
|
}, [hlsInstance]);
|
||||||
@ -74,11 +105,37 @@ export default function QualityIndicator({ hlsInstance, className = "" }: Qualit
|
|||||||
if (!currentQuality) return null;
|
if (!currentQuality) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex items-center gap-2 text-sm text-white bg-black/50 px-2 py-1 rounded ${className}`}>
|
<div className={`flex flex-col gap-1 text-xs text-white bg-black/70 px-3 py-2 rounded ${className}`}>
|
||||||
<Signal className={`w-4 h-4 ${getSignalColor()}`} />
|
{/* Quality and Signal */}
|
||||||
<span>{currentQuality}</span>
|
<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 && (
|
{networkType && (
|
||||||
<span className="text-xs opacity-75">({networkType})</span>
|
<div className="opacity-75">
|
||||||
|
Omrežje: {networkType}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -132,25 +132,43 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
|
|||||||
console.log('HLS manifest loaded - Available qualities:',
|
console.log('HLS manifest loaded - Available qualities:',
|
||||||
data.levels.map(l => `${l.height}p @ ${Math.round(l.bitrate/1000)}kbps`));
|
data.levels.map(l => `${l.height}p @ ${Math.round(l.bitrate/1000)}kbps`));
|
||||||
|
|
||||||
|
// Log bitrate analysis
|
||||||
|
console.log('BITRATE ANALIZA:');
|
||||||
|
data.levels.forEach((level, index) => {
|
||||||
|
console.log(`Nivo ${index}: ${level.width}x${level.height} @ ${Math.round(level.bitrate/1000)}kbps`);
|
||||||
|
});
|
||||||
|
|
||||||
// Set initial quality based on connection
|
// Set initial quality based on connection
|
||||||
const connection = (navigator as any).connection;
|
const connection = (navigator as any).connection;
|
||||||
if (connection) {
|
if (connection) {
|
||||||
const effectiveType = connection.effectiveType;
|
const effectiveType = connection.effectiveType;
|
||||||
console.log('Network type detected:', effectiveType);
|
const downlink = connection.downlink; // Mbps
|
||||||
|
console.log(`Omrežje: ${effectiveType}, hitrost: ${downlink} Mbps`);
|
||||||
|
|
||||||
// Adjust starting level based on network
|
// More aggressive quality selection for slow connections
|
||||||
if (effectiveType === 'slow-2g' || effectiveType === '2g') {
|
if (effectiveType === 'slow-2g' || effectiveType === '2g' || downlink < 1) {
|
||||||
hls.startLevel = 0; // Start with lowest quality
|
hls.startLevel = 0; // Lowest quality
|
||||||
} else if (effectiveType === '3g') {
|
console.log('Nastavljam najnižjo kakovost zaradi počasne povezave');
|
||||||
|
} else if (effectiveType === '3g' || downlink < 3) {
|
||||||
hls.startLevel = Math.min(1, data.levels.length - 1);
|
hls.startLevel = Math.min(1, data.levels.length - 1);
|
||||||
|
console.log('Nastavljam nizko kakovost zaradi 3G povezave');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quality level monitoring
|
// Quality level monitoring with detailed stats
|
||||||
hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
|
hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
|
||||||
const level = hls.levels[data.level];
|
const level = hls.levels[data.level];
|
||||||
console.log(`Kakovost preklopljena na: ${level.height}p @ ${Math.round(level.bitrate/1000)}kbps`);
|
console.log(`PREKLOPIL KAKOVOST: ${level.height}p @ ${Math.round(level.bitrate/1000)}kbps`);
|
||||||
|
console.log('Razlog preklopa: adaptivni algoritem na podlagi omrežne hitrosti');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fragment loading stats
|
||||||
|
hls.on(Hls.Events.FRAG_LOADED, (event, data) => {
|
||||||
|
const stats = data.stats;
|
||||||
|
const loadTime = stats.loading.end - stats.loading.start;
|
||||||
|
const speed = (stats.total * 8) / loadTime; // bits per ms = kbps
|
||||||
|
console.log(`Fragment naložen v ${loadTime}ms, hitrost: ${Math.round(speed)} kbps`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Buffer monitoring for dynamic adjustment
|
// Buffer monitoring for dynamic adjustment
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user