165 lines
4.3 KiB
JavaScript
165 lines
4.3 KiB
JavaScript
import { useState, useEffect, useCallback } from 'react';
|
|
|
|
// Thresholds based on SessionStatsStore.js.coffee
|
|
const SessionStatThresholds = {
|
|
network: {
|
|
ping: { poor: 100, warn: 50 },
|
|
audiojq_median: { poor: 10, warn: 5 },
|
|
jitter_var: { poor: 5, warn: 2 },
|
|
audio_bitrate_rx: { poor: 64000, warn: 96000 },
|
|
audio_bitrate_tx: { poor: 64000, warn: 96000 },
|
|
video_rtpbw_tx: { poor: 500000, warn: 1000000 },
|
|
video_rtpbw_rx: { poor: 500000, warn: 1000000 },
|
|
pkt_loss: { poor: 0.05, warn: 0.02 },
|
|
wifi: { poor: true, warn: true }
|
|
},
|
|
system: {
|
|
cpu: { poor: 80, warn: 60 }
|
|
},
|
|
audio: {
|
|
framesize: { poor: 1024, warn: 512 },
|
|
latency: { poor: 20, warn: 10 },
|
|
input_jitter: { poor: 2, warn: 1 },
|
|
output_jitter: { poor: 2, warn: 1 },
|
|
audio_in_type: { poor: 'wdm', warn: 'wdm' }
|
|
},
|
|
aggregate: {
|
|
latency: { poor: 100, warn: 50 }
|
|
}
|
|
};
|
|
|
|
// Classification logic from SessionStatsStore
|
|
const classify = (holder, field, threshold) => {
|
|
const value = holder[field];
|
|
const fieldLevel = field + '_level';
|
|
|
|
if (value !== undefined && threshold) {
|
|
if (threshold.inverse) {
|
|
if (threshold.zero_is_good) {
|
|
holder[fieldLevel] = 'good';
|
|
} else if (value <= threshold.poor) {
|
|
holder[fieldLevel] = 'poor';
|
|
} else if (value <= threshold.warn) {
|
|
holder[fieldLevel] = 'warn';
|
|
} else {
|
|
holder[fieldLevel] = 'good';
|
|
}
|
|
} else if (threshold.eql) {
|
|
if (value === threshold.poor) {
|
|
holder[fieldLevel] = 'poor';
|
|
} else if (value === threshold.warn) {
|
|
holder[fieldLevel] = 'warn';
|
|
} else {
|
|
holder[fieldLevel] = 'good';
|
|
}
|
|
} else {
|
|
if (value >= threshold.poor) {
|
|
holder[fieldLevel] = 'poor';
|
|
} else if (value >= threshold.warn) {
|
|
holder[fieldLevel] = 'warn';
|
|
} else {
|
|
holder[fieldLevel] = 'good';
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Mock stats data structure with classification
|
|
const createMockStats = clientId => {
|
|
const stats = {
|
|
aggregate: {
|
|
latency: 45.2,
|
|
their_out_latency: 12.5,
|
|
your_in_latency: 8.3,
|
|
one_way: 15.4,
|
|
jq: 9.0
|
|
},
|
|
network: {
|
|
ping: 25.3,
|
|
audiojq_median: 2.1,
|
|
jitter_var: 1.8,
|
|
pkt_loss: 0.05,
|
|
wifi: false,
|
|
audio_bitrate_rx: 128000,
|
|
audio_bitrate_tx: 128000,
|
|
video_rtpbw_rx: 2500000,
|
|
video_rtpbw_tx: 2500000,
|
|
rx_ars_vs_p2p: 'P2P',
|
|
tx_ars_vs_p2p: 'P2P'
|
|
},
|
|
system: {
|
|
cpu: 35.2
|
|
},
|
|
audio: {
|
|
latency: 5.2,
|
|
input_jitter: 0.8,
|
|
output_jitter: 0.6,
|
|
audio_in_type: 'CoreAudio',
|
|
framesize: 128
|
|
}
|
|
};
|
|
|
|
// Apply classification logic
|
|
Object.keys(stats).forEach(category => {
|
|
if (SessionStatThresholds[category]) {
|
|
Object.keys(stats[category]).forEach(field => {
|
|
if (SessionStatThresholds[category][field]) {
|
|
classify(stats[category], field, SessionStatThresholds[category][field]);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Special handling for ARS vs P2P (from SessionStatsStore logic)
|
|
if (stats.network.rx_ars_vs_p2p === 'P2P') {
|
|
stats.network.rx_ars_vs_p2p_level = 'good';
|
|
} else {
|
|
stats.network.rx_ars_vs_p2p_level = 'warn';
|
|
}
|
|
|
|
if (stats.network.tx_ars_vs_p2p === 'P2P') {
|
|
stats.network.tx_ars_vs_p2p_level = 'good';
|
|
} else {
|
|
stats.network.tx_ars_vs_p2p_level = 'warn';
|
|
}
|
|
|
|
return stats;
|
|
};
|
|
|
|
export const useSessionStats = clientId => {
|
|
const [stats, setStats] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
|
|
const refreshStats = useCallback(() => {
|
|
try {
|
|
// In a real implementation, this would fetch from JamClient or a global store
|
|
// For now, we'll use mock data that matches the structure from the web version
|
|
const mockStats = createMockStats(clientId);
|
|
setStats(mockStats);
|
|
setLoading(false);
|
|
} catch (err) {
|
|
setError(err);
|
|
setLoading(false);
|
|
}
|
|
}, [clientId]);
|
|
|
|
useEffect(() => {
|
|
refreshStats();
|
|
|
|
// Set up periodic refresh (similar to how the web version updates)
|
|
const interval = setInterval(refreshStats, 2000); // Update every 2 seconds
|
|
|
|
return () => clearInterval(interval);
|
|
}, [refreshStats]);
|
|
|
|
return {
|
|
stats,
|
|
loading,
|
|
error,
|
|
refreshStats
|
|
};
|
|
};
|
|
|
|
export default useSessionStats;
|