Fix: Settings-only-Backup-Import wischte Live-Queue + Zaehler (B/I)
importBackup wendete die Settings fuer beide Pfade ueber setSettings an, das bei
nicht-"never"-CleanupPolicy applyRetroactiveCleanupPolicy ausloest. Beim reinen
Settings-Restore purgte das die LIVE-Queue (fertige Items), obwohl der Vertrag
"running queue stays untouched" lautet (Dateien blieben auf Platte). Zudem rollte
der Import die laufenden Usage-/Status-Zaehler auf den (aelteren) Backup-Stand
zurueck (anders als updateSettings).
- setSettings bekommt optionales { suppressRetroactiveCleanup }; der Settings-only
Import setzt es. Die importierte Policy gilt weiter fuer KUENFTIGE Completions
ueber den normalen Vorwaertspfad (immediate/package_done) — nur der retroaktive
Sweep wird hier unterdrueckt.
- overlayLiveUsageCounters aus updateSettings extrahiert und im Settings-only Import
wiederverwendet (inkl. Key-Filter der Debrid-Link-Per-Key-Usage auf existierende
Keys). Nicht ueber updateSettings geroutet (vermeidet dessen resetHistoryForRetention).
This commit is contained in:
parent
61a830475b
commit
dc05b51083
@ -303,6 +303,27 @@ export class AppController {
|
||||
return next;
|
||||
}
|
||||
|
||||
// Carry the live, runtime-maintained usage/status counters onto a settings
|
||||
// object about to be applied, so they are never rolled back to a stale snapshot.
|
||||
// All-time totals take the max; daily/total usage and account statuses are taken
|
||||
// live; per-key Debrid-Link usage is filtered to keys that still exist.
|
||||
private overlayLiveUsageCounters(target: AppSettings): void {
|
||||
const liveSettings = this.manager.getSettings();
|
||||
target.totalDownloadedAllTime = Math.max(target.totalDownloadedAllTime || 0, liveSettings.totalDownloadedAllTime || 0);
|
||||
target.totalCompletedFilesAllTime = Math.max(target.totalCompletedFilesAllTime || 0, liveSettings.totalCompletedFilesAllTime || 0);
|
||||
target.totalRuntimeAllTimeMs = Math.max(target.totalRuntimeAllTimeMs || 0, this.manager.getLiveTotalRuntimeMs());
|
||||
target.providerDailyUsageDay = liveSettings.providerDailyUsageDay;
|
||||
target.providerDailyUsageBytes = { ...(liveSettings.providerDailyUsageBytes || {}) };
|
||||
target.providerTotalUsageBytes = { ...(liveSettings.providerTotalUsageBytes || {}) };
|
||||
target.debridLinkApiKeyDailyUsageBytes = Object.fromEntries(
|
||||
Object.entries(liveSettings.debridLinkApiKeyDailyUsageBytes || {}).filter(([keyId]) => getDebridLinkApiKeyIds(target.debridLinkApiKeys).includes(keyId))
|
||||
);
|
||||
target.debridLinkApiKeyTotalUsageBytes = Object.fromEntries(
|
||||
Object.entries(liveSettings.debridLinkApiKeyTotalUsageBytes || {}).filter(([keyId]) => getDebridLinkApiKeyIds(target.debridLinkApiKeys).includes(keyId))
|
||||
);
|
||||
target.debridAccountStatuses = { ...(liveSettings.debridAccountStatuses || {}) };
|
||||
}
|
||||
|
||||
public updateSettings(partial: Partial<AppSettings>): AppSettings {
|
||||
const sanitizedPatch = sanitizeSettingsPatch(partial);
|
||||
const previousSettings = this.settings;
|
||||
@ -315,20 +336,7 @@ export class AppController {
|
||||
return previousSettings;
|
||||
}
|
||||
|
||||
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 || {}) };
|
||||
nextSettings.debridLinkApiKeyDailyUsageBytes = Object.fromEntries(
|
||||
Object.entries(liveSettings.debridLinkApiKeyDailyUsageBytes || {}).filter(([keyId]) => getDebridLinkApiKeyIds(nextSettings.debridLinkApiKeys).includes(keyId))
|
||||
);
|
||||
nextSettings.debridLinkApiKeyTotalUsageBytes = Object.fromEntries(
|
||||
Object.entries(liveSettings.debridLinkApiKeyTotalUsageBytes || {}).filter(([keyId]) => getDebridLinkApiKeyIds(nextSettings.debridLinkApiKeys).includes(keyId))
|
||||
);
|
||||
nextSettings.debridAccountStatuses = { ...(liveSettings.debridAccountStatuses || {}) };
|
||||
this.overlayLiveUsageCounters(nextSettings);
|
||||
const retentionChanged = previousSettings.historyRetentionMode !== nextSettings.historyRetentionMode;
|
||||
this.settings = nextSettings;
|
||||
if (retentionChanged) {
|
||||
@ -697,14 +705,18 @@ public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
|
||||
}
|
||||
}
|
||||
const restoredSettings = normalizeSettings(importedSettings);
|
||||
this.settings = restoredSettings;
|
||||
saveSettings(this.storagePaths, this.settings);
|
||||
this.manager.setSettings(this.settings);
|
||||
|
||||
// Settings-only backup: settings are already applied live (same path as the
|
||||
// normal updateSettings flow). Do NOT stop the manager, wipe the session,
|
||||
// block persistence or relaunch — the running queue stays untouched.
|
||||
// Settings-only backup: keep the running queue AND the live counters untouched.
|
||||
// Overlay the live usage/status counters so they don't roll back to the backup's
|
||||
// (older) snapshot (BUG I), and suppress the retroactive cleanup sweep so the
|
||||
// backup's cleanup policy can't purge the live completed queue here (BUG B) — the
|
||||
// policy still governs FUTURE completions through the normal path. Do NOT stop the
|
||||
// manager, wipe the session, block persistence or relaunch.
|
||||
if (!hasSession) {
|
||||
this.overlayLiveUsageCounters(restoredSettings);
|
||||
this.settings = restoredSettings;
|
||||
saveSettings(this.storagePaths, this.settings);
|
||||
this.manager.setSettings(this.settings, { suppressRetroactiveCleanup: true });
|
||||
this.audit("INFO", "Backup importiert (nur Einstellungen)", {
|
||||
accountSummary: buildAccountSummary(this.settings)
|
||||
});
|
||||
@ -715,6 +727,10 @@ public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
|
||||
};
|
||||
}
|
||||
|
||||
this.settings = restoredSettings;
|
||||
saveSettings(this.storagePaths, this.settings);
|
||||
this.manager.setSettings(this.settings);
|
||||
|
||||
this.manager.stop();
|
||||
this.manager.abortAllPostProcessing();
|
||||
this.manager.clearPersistTimer();
|
||||
|
||||
@ -2081,7 +2081,7 @@ export class DownloadManager extends EventEmitter {
|
||||
this.emitState();
|
||||
}
|
||||
|
||||
public setSettings(next: AppSettings): void {
|
||||
public setSettings(next: AppSettings, opts?: { suppressRetroactiveCleanup?: boolean }): void {
|
||||
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);
|
||||
@ -2145,7 +2145,7 @@ export class DownloadManager extends EventEmitter {
|
||||
|
||||
this.resolveExistingQueuedOpaqueFilenames();
|
||||
void this.cleanupExistingExtractedArchives().catch((err) => logger.warn(`cleanupExistingExtractedArchives Fehler (setSettings): ${compactErrorText(err)}`));
|
||||
if (next.completedCleanupPolicy !== "never") {
|
||||
if (!opts?.suppressRetroactiveCleanup && next.completedCleanupPolicy !== "never") {
|
||||
this.applyRetroactiveCleanupPolicy();
|
||||
}
|
||||
this.emitState();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user