Add app runtime statistics
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)}`));
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user