Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/mobile/src/components/server/ServerResourceCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Server resource monitoring card (CPU + RAM)
* Server resource monitoring card (CPU + RAM + Network)
* Displays real-time server resource utilization with progress bars
* Note: Section header is rendered by parent - this is just the card content
*
Expand Down Expand Up @@ -117,6 +117,9 @@ interface ServerResourceCardProps {
processCpu: number;
hostMemory: number;
processMemory: number;
totalBandwidth?: number;
lanBandwidth?: number;
wanBandwidth?: number;
} | null;
isLoading?: boolean;
error?: Error | null;
Expand Down
22 changes: 20 additions & 2 deletions apps/mobile/src/hooks/useServerStatistics.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Hook for fetching server resource statistics (CPU/RAM)
* Hook for fetching server resource statistics (CPU/RAM/Network)
* Only polls when:
* 1. App is in foreground (AppState === 'active')
* 2. Dashboard tab is focused (useIsFocused)
Expand Down Expand Up @@ -49,7 +49,9 @@ export function useServerStatistics(serverId: string | undefined, enabled: boole

// Add/update data points
for (const point of newData) {
map.set(point.at, point);
if (!map.has(point.at)) {
map.set(point.at, point);
}
}

// Sort by timestamp descending (newest first), keep DATA_POINTS
Expand Down Expand Up @@ -103,6 +105,19 @@ export function useServerStatistics(serverId: string | undefined, enabled: boole
processMemory: Math.round(
dataPoints.reduce((sum: number, p) => sum + p.processMemoryUtilization, 0) / dataLength
),
totalBandwidth:
Math.round(
(dataPoints.reduce((sum: number, p) => sum + p.totalBandwidthMbps, 0) / dataLength) *
10
) / 10,
lanBandwidth:
Math.round(
(dataPoints.reduce((sum: number, p) => sum + p.lanBandwidthMbps, 0) / dataLength) * 10
) / 10,
wanBandwidth:
Math.round(
(dataPoints.reduce((sum: number, p) => sum + p.wanBandwidthMbps, 0) / dataLength) * 10
) / 10,
}
: null;

Expand All @@ -114,6 +129,9 @@ export function useServerStatistics(serverId: string | undefined, enabled: boole
processCpu: Math.round(lastDataPoint.processCpuUtilization),
hostMemory: Math.round(lastDataPoint.hostMemoryUtilization),
processMemory: Math.round(lastDataPoint.processMemoryUtilization),
totalBandwidth: Math.round(lastDataPoint.totalBandwidthMbps * 10) / 10,
lanBandwidth: Math.round(lastDataPoint.lanBandwidthMbps * 10) / 10,
wanBandwidth: Math.round(lastDataPoint.wanBandwidthMbps * 10) / 10,
}
: null;

Expand Down
49 changes: 47 additions & 2 deletions apps/server/src/routes/servers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@ import { servers } from '../db/schema.js';
// Token encryption removed - tokens now stored in plain text (DB is localhost-only)
import { PlexClient, JellyfinClient, EmbyClient } from '../services/mediaServer/index.js';
import { syncServer } from '../services/sync.js';
import type { MediaSession } from '../services/mediaServer/types.js';
import { isPrivateIP } from '../jobs/poller/utils.js';

function calculateBandwidthMbps(sessions: MediaSession[]): {
totalMbps: number;
lanMbps: number;
wanMbps: number;
} {
let lanKbps = 0;
let wanKbps = 0;

for (const session of sessions) {
// Skip sessions that aren't actively playing (paused/stopped won't consume bandwidth)
if (session.playback.state !== 'playing') continue;

const bitrateKbps = session.quality.bitrate;
if (!bitrateKbps || bitrateKbps <= 0) continue;

if (isPrivateIP(session.network.ipAddress)) {
lanKbps += bitrateKbps;
} else {
wanKbps += bitrateKbps;
}
}

const toMbps = (kbps: number) => Math.round((kbps / 1000) * 10) / 10;

return {
lanMbps: toMbps(lanKbps),
wanMbps: toMbps(wanKbps),
totalMbps: toMbps(lanKbps + wanKbps),
};
}

export const serverRoutes: FastifyPluginAsync = async (app) => {
/**
Expand Down Expand Up @@ -248,11 +281,23 @@ export const serverRoutes: FastifyPluginAsync = async (app) => {
token: server.token,
});

const data = await client.getServerStatistics(SERVER_STATS_CONFIG.TIMESPAN_SECONDS);
const [resourceStats, sessions] = await Promise.all([
client.getServerStatistics(SERVER_STATS_CONFIG.TIMESPAN_SECONDS),
client.getSessions(),
]);

const bandwidth = calculateBandwidthMbps(sessions);

const data = resourceStats.map((point) => ({
...point,
totalBandwidthMbps: bandwidth.totalMbps,
lanBandwidthMbps: bandwidth.lanMbps,
wanBandwidthMbps: bandwidth.wanMbps,
}));

// DEBUG: Log what we got back
app.log.info(
{ serverId: id, dataLength: data.length, firstItem: data[0] },
{ serverId: id, dataLength: data.length, bandwidth, firstItem: data[0] },
'Server statistics fetched'
);

Expand Down
7 changes: 7 additions & 0 deletions apps/server/src/services/mediaServer/plex/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,9 @@ export interface PlexStatisticsDataPoint {
processCpuUtilization: number;
hostMemoryUtilization: number;
processMemoryUtilization: number;
totalBandwidthMbps: number;
lanBandwidthMbps: number;
wanBandwidthMbps: number;
}

/**
Expand All @@ -587,6 +590,10 @@ function parseStatisticsDataPoint(raw: PlexRawStatisticsResource): PlexStatistic
processCpuUtilization: parseNumber(raw.processCpuUtilization, 0),
hostMemoryUtilization: parseNumber(raw.hostMemoryUtilization, 0),
processMemoryUtilization: parseNumber(raw.processMemoryUtilization, 0),
// Network bandwidth is computed server-side using active sessions
totalBandwidthMbps: 0,
lanBandwidthMbps: 0,
wanBandwidthMbps: 0,
};
}

Expand Down
Loading
Loading