mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-22 08:56:44 +00:00
fix(torrent): use live peer telemetry in properties
- source the Properties peer card from live Aria2 status counts - distinguish connected peers from unavailable peer details - remove redundant peer-summary IPC and harden count parsing - add responsive, accessible peer/seeder presentation and regressions
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
formatPropertiesDiagnosticCount,
|
||||
getPropertiesAvailabilityDiagnosticState,
|
||||
getPropertiesPeerDiagnosticState,
|
||||
hasLiveTorrentPeerWithoutDetails,
|
||||
} from './propertiesDiagnostics';
|
||||
|
||||
const emptyPeerDiagnostics = {
|
||||
@@ -49,4 +50,11 @@ describe('Properties peer diagnostics presentation state', () => {
|
||||
expect(getPropertiesAvailabilityDiagnosticState(null, false, 'idle')).toBe('unavailable');
|
||||
expect(getPropertiesAvailabilityDiagnosticState(null, false, 'error')).toBe('error');
|
||||
});
|
||||
|
||||
it('distinguishes a live connection from an empty peer-detail snapshot', () => {
|
||||
expect(hasLiveTorrentPeerWithoutDetails(1, 0)).toBe(true);
|
||||
expect(hasLiveTorrentPeerWithoutDetails(0, 0)).toBe(false);
|
||||
expect(hasLiveTorrentPeerWithoutDetails(undefined, 0)).toBe(false);
|
||||
expect(hasLiveTorrentPeerWithoutDetails(2, 1)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,14 @@ export const formatPropertiesDiagnosticCount = (value: number, locale: string):
|
||||
return new Intl.NumberFormat(resolveAppLocale(locale)).format(value);
|
||||
};
|
||||
|
||||
export const hasLiveTorrentPeerWithoutDetails = (
|
||||
connectedPeers: number | undefined,
|
||||
detailedPeers: number,
|
||||
): boolean => Number.isSafeInteger(connectedPeers)
|
||||
&& (connectedPeers ?? 0) > 0
|
||||
&& Number.isSafeInteger(detailedPeers)
|
||||
&& detailedPeers === 0;
|
||||
|
||||
export const formatPropertiesAvailability = (availability: number, locale: string): string => {
|
||||
if (!Number.isFinite(availability) || availability < 0) return '—';
|
||||
return new Intl.NumberFormat(resolveAppLocale(locale), { maximumFractionDigits: 2 }).format(availability);
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { isCurrentTorrentPeerSummary, isTorrentPeerSummaryStatus } from './propertiesPeerSummary';
|
||||
|
||||
describe('Torrent peer summary lifecycle', () => {
|
||||
it('accepts only the current active Torrent lifecycle', () => {
|
||||
const request = {
|
||||
currentDownloadId: 'torrent-1',
|
||||
requestDownloadId: 'torrent-1',
|
||||
currentLifecycleEpoch: 8,
|
||||
requestLifecycleEpoch: 8,
|
||||
currentStatus: 'downloading',
|
||||
};
|
||||
expect(isCurrentTorrentPeerSummary(request)).toBe(true);
|
||||
expect(isCurrentTorrentPeerSummary({ ...request, currentLifecycleEpoch: 9 })).toBe(false);
|
||||
expect(isCurrentTorrentPeerSummary({ ...request, requestDownloadId: 'torrent-2' })).toBe(false);
|
||||
});
|
||||
|
||||
it('stops live summary polling for paused and completed lifecycles', () => {
|
||||
expect(isTorrentPeerSummaryStatus('paused')).toBe(false);
|
||||
expect(isTorrentPeerSummaryStatus('completed')).toBe(false);
|
||||
expect(isTorrentPeerSummaryStatus('seeding')).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
const TORRENT_PEER_SUMMARY_ACTIVE_STATUSES = [
|
||||
'downloading',
|
||||
'verifying',
|
||||
'seeding',
|
||||
'waitingToSeed',
|
||||
'retrying',
|
||||
] as const;
|
||||
|
||||
export const isTorrentPeerSummaryStatus = (status: string): boolean =>
|
||||
(TORRENT_PEER_SUMMARY_ACTIVE_STATUSES as readonly string[]).includes(status);
|
||||
|
||||
export const isCurrentTorrentPeerSummary = ({
|
||||
currentDownloadId,
|
||||
requestDownloadId,
|
||||
currentLifecycleEpoch,
|
||||
requestLifecycleEpoch,
|
||||
currentStatus,
|
||||
}: {
|
||||
currentDownloadId: string | null;
|
||||
requestDownloadId: string;
|
||||
currentLifecycleEpoch: number;
|
||||
requestLifecycleEpoch: number;
|
||||
currentStatus: string;
|
||||
}): boolean => currentDownloadId === requestDownloadId
|
||||
&& currentLifecycleEpoch === requestLifecycleEpoch
|
||||
&& isTorrentPeerSummaryStatus(currentStatus);
|
||||
@@ -65,26 +65,29 @@ describe('Properties connection presentation', () => {
|
||||
})).toEqual({
|
||||
kind: 'torrent',
|
||||
showHeaderMetric: true,
|
||||
labelKey: 'torrentConnectedPeers',
|
||||
labelKey: 'torrentPeersSeeders',
|
||||
value: '—',
|
||||
torrentPeerCounts: {
|
||||
connectedPeers: undefined,
|
||||
connectedSeeders: undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('exposes the explicit live peer and seeder summary values', () => {
|
||||
it('uses live connected peer and seeder counts from the Properties snapshot', () => {
|
||||
expect(getPropertiesConnectionPresentation({
|
||||
isMedia: false,
|
||||
isTorrent: true,
|
||||
}, {
|
||||
totalPeers: 41,
|
||||
totalSeeders: 2,
|
||||
torrentConnectedPeers: 10,
|
||||
torrentConnectedSeeders: 2,
|
||||
})).toEqual({
|
||||
kind: 'torrent',
|
||||
showHeaderMetric: true,
|
||||
labelKey: 'torrentConnectedPeers',
|
||||
labelKey: 'torrentPeersSeeders',
|
||||
value: '—',
|
||||
torrentPeerSummary: {
|
||||
totalPeers: 41,
|
||||
totalSeeders: 2,
|
||||
torrentPeerCounts: {
|
||||
connectedPeers: 10,
|
||||
connectedSeeders: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { PropertiesSnapshot } from '../propertiesBridge';
|
||||
import { resolveDownloadFraction } from './downloadProgress';
|
||||
|
||||
export type PropertiesConnectionKind = 'media' | 'torrent' | 'aria2';
|
||||
export type PropertiesConnectionLabelKey = 'fragmentConcurrency' | 'torrentConnectedPeers' | 'connections';
|
||||
export type PropertiesTorrentPeerSummary = {
|
||||
totalPeers: number;
|
||||
totalSeeders: number;
|
||||
export type PropertiesConnectionLabelKey = 'fragmentConcurrency' | 'torrentPeersSeeders' | 'connections';
|
||||
export type PropertiesTorrentPeerCounts = {
|
||||
connectedPeers?: number;
|
||||
connectedSeeders?: number;
|
||||
};
|
||||
|
||||
export type PropertiesConnectionPresentation = {
|
||||
@@ -13,7 +13,7 @@ export type PropertiesConnectionPresentation = {
|
||||
showHeaderMetric: boolean;
|
||||
labelKey: PropertiesConnectionLabelKey;
|
||||
value: string;
|
||||
torrentPeerSummary?: PropertiesTorrentPeerSummary;
|
||||
torrentPeerCounts?: PropertiesTorrentPeerCounts;
|
||||
};
|
||||
|
||||
const displayCount = (value: number | undefined): string => value == null ? '—' : String(value);
|
||||
@@ -25,8 +25,7 @@ export const getPropertiesProgress = (
|
||||
: resolveDownloadFraction(snapshot);
|
||||
|
||||
export const getPropertiesConnectionPresentation = (
|
||||
snapshot: Pick<PropertiesSnapshot, 'isMedia' | 'isTorrent' | 'connections' | 'activeConnections' | 'requestedConnections'>,
|
||||
torrentPeerSummary?: PropertiesTorrentPeerSummary | null,
|
||||
snapshot: Pick<PropertiesSnapshot, 'isMedia' | 'isTorrent' | 'connections' | 'activeConnections' | 'requestedConnections' | 'torrentConnectedPeers' | 'torrentConnectedSeeders'>,
|
||||
): PropertiesConnectionPresentation => {
|
||||
if (snapshot.isMedia === true) {
|
||||
return {
|
||||
@@ -41,9 +40,12 @@ export const getPropertiesConnectionPresentation = (
|
||||
return {
|
||||
kind: 'torrent',
|
||||
showHeaderMetric: true,
|
||||
labelKey: 'torrentConnectedPeers',
|
||||
labelKey: 'torrentPeersSeeders',
|
||||
value: '—',
|
||||
...(torrentPeerSummary ? { torrentPeerSummary } : {}),
|
||||
torrentPeerCounts: {
|
||||
connectedPeers: snapshot.torrentConnectedPeers,
|
||||
connectedSeeders: snapshot.torrentConnectedSeeders,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { isTorrentLiveStatus } from './propertiesTorrentLifecycle';
|
||||
|
||||
describe('Torrent live lifecycle', () => {
|
||||
it('identifies statuses with live Aria2 telemetry', () => {
|
||||
expect(isTorrentLiveStatus('downloading')).toBe(true);
|
||||
expect(isTorrentLiveStatus('retrying')).toBe(true);
|
||||
expect(isTorrentLiveStatus('paused')).toBe(false);
|
||||
expect(isTorrentLiveStatus('completed')).toBe(false);
|
||||
expect(isTorrentLiveStatus('seeding')).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
const TORRENT_LIVE_STATUSES = [
|
||||
'downloading',
|
||||
'verifying',
|
||||
'seeding',
|
||||
'waitingToSeed',
|
||||
'retrying',
|
||||
] as const;
|
||||
|
||||
export const isTorrentLiveStatus = (status: string): boolean =>
|
||||
(TORRENT_LIVE_STATUSES as readonly string[]).includes(status);
|
||||
Reference in New Issue
Block a user