Comparar commits
2 Commits
01347d5153
...
75555b8454
| Autor | SHA1 | Fecha | |
|---|---|---|---|
|
75555b8454
|
|||
|
18d1eb88da
|
@@ -22,7 +22,10 @@ export default function Home() {
|
||||
const p2pManagerRef = useRef(null);
|
||||
const [stats, setStats] = useState({
|
||||
http: 0,
|
||||
p2p: 0
|
||||
p2p: 0,
|
||||
uploadSpeed: 0,
|
||||
downloadSpeed: 0,
|
||||
peers: 0
|
||||
});
|
||||
|
||||
// URLs de ejemplo - usando proxy del servidor
|
||||
@@ -135,10 +138,13 @@ export default function Home() {
|
||||
}, []);
|
||||
|
||||
const handlePeerStats = useCallback((data) => {
|
||||
// Actualizar estadísticas P2P - usar los deltas que envía el componente
|
||||
// Actualizar estadísticas P2P completas
|
||||
setStats(prev => ({
|
||||
...prev,
|
||||
p2p: prev.p2p + (data.downloadSpeed || 0) * 5 // velocidad * 5 segundos = bytes descargados en este intervalo
|
||||
p2p: prev.p2p + (data.downloadSpeed || 0) * 5,
|
||||
uploadSpeed: data.uploadSpeed || 0,
|
||||
downloadSpeed: data.downloadSpeed || 0,
|
||||
peers: data.peers || 0
|
||||
}));
|
||||
}, []);
|
||||
|
||||
@@ -267,12 +273,35 @@ export default function Home() {
|
||||
Streaming de video con tecnología P2P y chat en tiempo real
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* Estadísticas P2P */}
|
||||
{peers > 0 && (
|
||||
<div className="flex items-center space-x-4 text-sm">
|
||||
<div className="flex items-center space-x-1">
|
||||
<span className="text-gray-600">👥</span>
|
||||
<span className="font-semibold text-blue-600">{stats.peers}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<span className="text-gray-600">⬆️</span>
|
||||
<span className="font-semibold text-green-600">
|
||||
{(stats.uploadSpeed / 1024).toFixed(1)} KB/s
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<span className="text-gray-600">⬇️</span>
|
||||
<span className="font-semibold text-purple-600">
|
||||
{(stats.downloadSpeed / 1024).toFixed(1)} KB/s
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span className="text-sm text-gray-600">WebRTC Activo</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
@@ -376,6 +405,46 @@ export default function Home() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Panel de Estadísticas P2P */}
|
||||
{stats.peers > 0 && (
|
||||
<div className="bg-gradient-to-br from-blue-50 to-purple-50 rounded-lg shadow-lg p-4 border-2 border-blue-200">
|
||||
<h3 className="text-lg font-bold text-gray-800 mb-3 flex items-center">
|
||||
<span className="mr-2">📊</span>
|
||||
Estadísticas P2P en Tiempo Real
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="bg-white rounded-lg p-3 shadow">
|
||||
<p className="text-xs text-gray-600 mb-1">👥 Peers Conectados</p>
|
||||
<p className="text-2xl font-bold text-blue-600">{stats.peers}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-3 shadow">
|
||||
<p className="text-xs text-gray-600 mb-1">⬆️ Subida</p>
|
||||
<p className="text-xl font-bold text-green-600">
|
||||
{(stats.uploadSpeed / 1024).toFixed(1)}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">KB/s</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-3 shadow">
|
||||
<p className="text-xs text-gray-600 mb-1">⬇️ Descarga</p>
|
||||
<p className="text-xl font-bold text-purple-600">
|
||||
{(stats.downloadSpeed / 1024).toFixed(1)}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">KB/s</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-3 shadow">
|
||||
<p className="text-xs text-gray-600 mb-1">📦 Total P2P</p>
|
||||
<p className="text-lg font-bold text-orange-600">
|
||||
{(stats.p2p / 1024 / 1024).toFixed(2)}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">MB</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Video Player */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-4" ref={videoPlayerRef}>
|
||||
{watchingUser ? (
|
||||
@@ -437,8 +506,8 @@ export default function Home() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Chat */}
|
||||
<div className="h-full">
|
||||
{/* Chat - altura fija con scroll interno */}
|
||||
<div className="h-[calc(100vh-12rem)] min-h-[600px]">
|
||||
<Chat
|
||||
username={username}
|
||||
onUsernameChange={setUsername}
|
||||
|
||||
@@ -195,7 +195,7 @@ export default function Chat({ username, onUsernameChange, onSocketReady, onWatc
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-lg overflow-hidden flex flex-col h-full">
|
||||
{/* Header */}
|
||||
<div className="bg-gradient-to-r from-blue-500 to-purple-600 p-4">
|
||||
<div className="bg-gradient-to-r from-blue-500 to-purple-600 p-4 flex-shrink-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-white font-bold text-lg">Chat en Vivo</h3>
|
||||
<div className="flex items-center space-x-2">
|
||||
@@ -207,13 +207,12 @@ export default function Chat({ username, onUsernameChange, onSocketReady, onWatc
|
||||
</div>
|
||||
|
||||
{/* Usuarios conectados */}
|
||||
<div className="bg-gray-50 p-3 border-b">
|
||||
<div className="bg-gray-50 p-3 border-b flex-shrink-0">
|
||||
<h4 className="text-xs font-semibold text-gray-600 mb-2">
|
||||
USUARIOS CONECTADOS ({users.length})
|
||||
</h4>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 max-h-40 overflow-y-auto">
|
||||
{users.map((user, index) => {
|
||||
<div className="grid grid-cols-2 gap-2 max-h-32 overflow-y-auto">{users.map((user, index) => {
|
||||
const isCurrentUser = user === username;
|
||||
const hasThumbnail = userThumbnails[user]?.thumbnail;
|
||||
const isHovered = hoveredUser === user;
|
||||
@@ -329,7 +328,7 @@ export default function Chat({ username, onUsernameChange, onSocketReady, onWatc
|
||||
</div>
|
||||
|
||||
{/* Input de mensaje */}
|
||||
<form onSubmit={sendMessage} className="p-4 bg-white border-t">
|
||||
<form onSubmit={sendMessage} className="p-4 bg-white border-t flex-shrink-0">
|
||||
<div className="flex space-x-2">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -257,11 +257,56 @@ const P2PManager = forwardRef(({
|
||||
});
|
||||
|
||||
peer.on('connect', () => {
|
||||
console.log(`✅ Peer conectado con ${targetUser}`);
|
||||
setPeers(prev => {
|
||||
const newPeers = [...prev, targetUser];
|
||||
if (onPeersUpdateRef.current) onPeersUpdateRef.current(newPeers);
|
||||
return newPeers;
|
||||
});
|
||||
|
||||
// Iniciar monitoreo de estadísticas WebRTC cada 1 segundo
|
||||
const statsMonitor = setInterval(async () => {
|
||||
if (!peer._pc || peer.destroyed) {
|
||||
clearInterval(statsMonitor);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = await peer._pc.getStats();
|
||||
let bytesReceived = 0;
|
||||
let bytesSent = 0;
|
||||
|
||||
stats.forEach(report => {
|
||||
if (report.type === 'inbound-rtp' && report.mediaType === 'video') {
|
||||
bytesReceived += report.bytesReceived || 0;
|
||||
}
|
||||
if (report.type === 'outbound-rtp' && report.mediaType === 'video') {
|
||||
bytesSent += report.bytesSent || 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Guardar stats anteriores para calcular delta
|
||||
if (!peer._lastStats) {
|
||||
peer._lastStats = { bytesReceived, bytesSent, timestamp: Date.now() };
|
||||
} else {
|
||||
const deltaTime = (Date.now() - peer._lastStats.timestamp) / 1000;
|
||||
const deltaReceived = bytesReceived - peer._lastStats.bytesReceived;
|
||||
const deltaSent = bytesSent - peer._lastStats.bytesSent;
|
||||
|
||||
if (deltaTime > 0) {
|
||||
downloadBytes.current += deltaReceived;
|
||||
uploadBytes.current += deltaSent;
|
||||
}
|
||||
|
||||
peer._lastStats = { bytesReceived, bytesSent, timestamp: Date.now() };
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignorar errores de stats
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Guardar el interval en el peer para limpiarlo después
|
||||
peer._statsMonitor = statsMonitor;
|
||||
});
|
||||
|
||||
peer.on('data', (data) => {
|
||||
@@ -277,6 +322,10 @@ const P2PManager = forwardRef(({
|
||||
});
|
||||
|
||||
peer.on('close', () => {
|
||||
console.log(`🔌 Peer cerrado con ${targetUser}`);
|
||||
if (peer._statsMonitor) {
|
||||
clearInterval(peer._statsMonitor);
|
||||
}
|
||||
if (peersRef.current[targetUser] === peer) {
|
||||
delete peersRef.current[targetUser];
|
||||
delete remoteStreamsRef.current[targetUser];
|
||||
@@ -290,6 +339,9 @@ const P2PManager = forwardRef(({
|
||||
|
||||
peer.on('error', (err) => {
|
||||
console.error('❌ Error en peer', targetUser, ':', err.message || err);
|
||||
if (peer._statsMonitor) {
|
||||
clearInterval(peer._statsMonitor);
|
||||
}
|
||||
if (peersRef.current[targetUser] === peer) {
|
||||
delete peersRef.current[targetUser];
|
||||
delete remoteStreamsRef.current[targetUser];
|
||||
|
||||
Referencia en una nueva incidencia
Block a user