Add app runtime statistics

This commit is contained in:
Sucukdeluxe
2026-03-09 04:59:00 +01:00
parent 971f669bb6
commit 1f9a26e4b0
12 changed files with 194 additions and 8 deletions
+11
View File
@@ -76,6 +76,7 @@ export class AppController {
private onStateHandler: ((snapshot: UiSnapshot) => void) | null = null;
private autoResumePending = false;
private runtimeStatsTimer: NodeJS.Timeout | null = null;
public constructor() {
configureLogger(this.storagePaths.baseDir);
@@ -114,6 +115,11 @@ export class AppController {
runtimeDir: this.storagePaths.baseDir
});
startDebugServer(this.manager, this.storagePaths.baseDir);
this.runtimeStatsTimer = setInterval(() => {
this.manager.persistRuntimeStats();
this.settings = this.manager.getSettings();
}, 60_000);
this.runtimeStatsTimer.unref?.();
if (this.settings.autoResumeOnStart) {
const snapshot = this.manager.getSnapshot();
@@ -237,6 +243,7 @@ export class AppController {
const liveSettings = this.manager.getSettings();
nextSettings.totalDownloadedAllTime = Math.max(nextSettings.totalDownloadedAllTime || 0, liveSettings.totalDownloadedAllTime || 0);
nextSettings.totalCompletedFilesAllTime = Math.max(nextSettings.totalCompletedFilesAllTime || 0, liveSettings.totalCompletedFilesAllTime || 0);
nextSettings.totalRuntimeAllTimeMs = Math.max(nextSettings.totalRuntimeAllTimeMs || 0, this.manager.getLiveTotalRuntimeMs());
nextSettings.providerDailyUsageDay = liveSettings.providerDailyUsageDay;
nextSettings.providerDailyUsageBytes = { ...(liveSettings.providerDailyUsageBytes || {}) };
nextSettings.providerTotalUsageBytes = { ...(liveSettings.providerTotalUsageBytes || {}) };
@@ -639,6 +646,10 @@ export class AppController {
}
public shutdown(): void {
if (this.runtimeStatsTimer) {
clearInterval(this.runtimeStatsTimer);
this.runtimeStatsTimer = null;
}
stopDebugServer();
abortActiveUpdateDownload();
this.manager.prepareForShutdown();
+1
View File
@@ -104,6 +104,7 @@ export function defaultSettings(): AppSettings {
confirmDeleteSelection: true,
totalDownloadedAllTime: 0,
totalCompletedFilesAllTime: 0,
totalRuntimeAllTimeMs: 0,
bandwidthSchedules: [],
columnOrder: ["name", "size", "progress", "hoster", "account", "prio", "status", "speed"],
extractCpuPriority: "high",
+2 -1
View File
@@ -681,7 +681,8 @@ function handleRequest(req: http.IncomingMessage, res: http.ServerResponse): voi
...buildStatsPayload(snapshot),
allTime: {
totalDownloadedAllTime: settings.totalDownloadedAllTime,
totalCompletedFilesAllTime: settings.totalCompletedFilesAllTime
totalCompletedFilesAllTime: settings.totalCompletedFilesAllTime,
totalRuntimeAllTimeMs: settings.totalRuntimeAllTimeMs
}
});
return;
+54 -1
View File
@@ -1254,6 +1254,9 @@ export class DownloadManager extends EventEmitter {
private lastPersistAt = 0;
private lastSettingsPersistAt = 0;
private appSessionStartedAt = 0;
private runtimePersistedTotalMs = 0;
private runtimePersistedAt = 0;
private cleanupQueue: Promise<void> = Promise.resolve();
@@ -1332,6 +1335,10 @@ export class DownloadManager extends EventEmitter {
public constructor(settings: AppSettings, session: SessionState, storagePaths: StoragePaths, options: DownloadManagerOptions = {}) {
super();
this.settings = settings;
const startedAt = nowMs();
this.appSessionStartedAt = startedAt;
this.runtimePersistedTotalMs = Math.max(0, Number(settings.totalRuntimeAllTimeMs || 0));
this.runtimePersistedAt = startedAt;
this.session = cloneSession(session);
this.itemCount = Object.keys(this.session.items).length;
this.storagePaths = storagePaths;
@@ -1600,7 +1607,11 @@ export class DownloadManager extends EventEmitter {
const previous = this.settings;
next.totalDownloadedAllTime = Math.max(next.totalDownloadedAllTime || 0, this.settings.totalDownloadedAllTime || 0);
next.totalCompletedFilesAllTime = Math.max(next.totalCompletedFilesAllTime || 0, this.settings.totalCompletedFilesAllTime || 0);
const now = nowMs();
next.totalRuntimeAllTimeMs = Math.max(next.totalRuntimeAllTimeMs || 0, this.getLiveTotalRuntimeMs(now));
this.settings = next;
this.runtimePersistedTotalMs = this.settings.totalRuntimeAllTimeMs || 0;
this.runtimePersistedAt = now;
this.ensureProviderDailyUsageFresh(nowMs());
this.debridService.setSettings(next);
this.allDebridHostInfoCache.clear();
@@ -1749,13 +1760,53 @@ export class DownloadManager extends EventEmitter {
totalFilesSession: this.sessionCompletedFiles,
totalFilesAllTime: this.settings.totalCompletedFilesAllTime,
totalPackages: this.session.packageOrder.length,
sessionStartedAt: this.session.runStartedAt
sessionStartedAt: this.session.runStartedAt,
appSessionStartedAt: this.appSessionStartedAt,
sessionRuntimeMs: this.getAppSessionRuntimeMs(now),
totalRuntimeMs: this.getLiveTotalRuntimeMs(now),
runtimeMeasuredAt: now
};
this.statsCache = stats;
this.statsCacheAt = now;
return stats;
}
public getLiveTotalRuntimeMs(now = nowMs()): number {
return Math.max(0, this.runtimePersistedTotalMs + Math.max(0, now - this.runtimePersistedAt));
}
private getAppSessionRuntimeMs(now = nowMs()): number {
return this.appSessionStartedAt > 0 ? Math.max(0, now - this.appSessionStartedAt) : 0;
}
private foldRuntimeIntoSettings(now = nowMs()): boolean {
const totalRuntimeMs = this.getLiveTotalRuntimeMs(now);
if (!Number.isFinite(totalRuntimeMs) || totalRuntimeMs <= (this.settings.totalRuntimeAllTimeMs || 0)) {
return false;
}
this.settings.totalRuntimeAllTimeMs = totalRuntimeMs;
this.runtimePersistedTotalMs = totalRuntimeMs;
this.runtimePersistedAt = now;
this.invalidateStatsCache();
return true;
}
public persistRuntimeStats(sync = false): void {
if (this.blockAllPersistence) {
return;
}
const now = nowMs();
if (!this.foldRuntimeIntoSettings(now)) {
return;
}
this.lastSettingsPersistAt = now;
if (sync) {
saveSettings(this.storagePaths, this.settings);
return;
}
void saveSettingsAsync(this.storagePaths, this.settings).catch((err) => logger.warn(`saveSettingsAsync Fehler: ${compactErrorText(err as Error)}`));
}
private invalidateStatsCache(): void {
this.statsCache = null;
this.statsCacheAt = 0;
@@ -4293,6 +4344,7 @@ export class DownloadManager extends EventEmitter {
const pkgCount = Object.keys(this.session.packages).length;
const itemCount = Object.keys(this.session.items).length;
logger.info(`Shutdown-Save: ${pkgCount} Pakete, ${itemCount} Items`);
this.foldRuntimeIntoSettings(nowMs());
saveSession(this.storagePaths, this.session);
saveSettings(this.storagePaths, this.settings);
} else {
@@ -4614,6 +4666,7 @@ export class DownloadManager extends EventEmitter {
this.lastPersistAt = now;
void saveSessionAsync(this.storagePaths, this.session).catch((err) => logger.warn(`saveSessionAsync Fehler: ${compactErrorText(err)}`));
if (now - this.lastSettingsPersistAt >= 30000) {
this.foldRuntimeIntoSettings(now);
this.lastSettingsPersistAt = now;
void saveSettingsAsync(this.storagePaths, this.settings).catch((err) => logger.warn(`saveSettingsAsync Fehler: ${compactErrorText(err as Error)}`));
}
+1
View File
@@ -384,6 +384,7 @@ export function normalizeSettings(settings: AppSettings): AppSettings {
confirmDeleteSelection: settings.confirmDeleteSelection !== undefined ? Boolean(settings.confirmDeleteSelection) : defaults.confirmDeleteSelection,
totalDownloadedAllTime: typeof settings.totalDownloadedAllTime === "number" && settings.totalDownloadedAllTime >= 0 ? settings.totalDownloadedAllTime : defaults.totalDownloadedAllTime,
totalCompletedFilesAllTime: typeof settings.totalCompletedFilesAllTime === "number" && settings.totalCompletedFilesAllTime >= 0 ? settings.totalCompletedFilesAllTime : defaults.totalCompletedFilesAllTime,
totalRuntimeAllTimeMs: typeof settings.totalRuntimeAllTimeMs === "number" && settings.totalRuntimeAllTimeMs >= 0 ? settings.totalRuntimeAllTimeMs : defaults.totalRuntimeAllTimeMs,
theme: VALID_THEMES.has(settings.theme) ? settings.theme : defaults.theme,
bandwidthSchedules: normalizeBandwidthSchedules(settings.bandwidthSchedules),
columnOrder: normalizeColumnOrder(settings.columnOrder),
+2 -1
View File
@@ -89,7 +89,8 @@ export function buildSupportBundle(manager: DownloadManager, baseDir: string): B
...buildStatsPayload(snapshot),
allTime: {
totalDownloadedAllTime: settings.totalDownloadedAllTime,
totalCompletedFilesAllTime: settings.totalCompletedFilesAllTime
totalCompletedFilesAllTime: settings.totalCompletedFilesAllTime,
totalRuntimeAllTimeMs: settings.totalRuntimeAllTimeMs
}
});
addJson(zip, "overview/debug-setup.json", debugSetup);
+1
View File
@@ -134,6 +134,7 @@ export function buildRedactedSettingsPayload(settings: AppSettings): Record<stri
statistics: {
totalDownloadedAllTime: settings.totalDownloadedAllTime,
totalCompletedFilesAllTime: settings.totalCompletedFilesAllTime,
totalRuntimeAllTimeMs: settings.totalRuntimeAllTimeMs,
providerDailyLimitBytes: settings.providerDailyLimitBytes,
providerDailyUsageBytes: settings.providerDailyUsageBytes,
providerTotalUsageBytes: settings.providerTotalUsageBytes,