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;
|
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 {
|
public updateSettings(partial: Partial<AppSettings>): AppSettings {
|
||||||
const sanitizedPatch = sanitizeSettingsPatch(partial);
|
const sanitizedPatch = sanitizeSettingsPatch(partial);
|
||||||
const previousSettings = this.settings;
|
const previousSettings = this.settings;
|
||||||
@ -315,20 +336,7 @@ export class AppController {
|
|||||||
return previousSettings;
|
return previousSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
const liveSettings = this.manager.getSettings();
|
this.overlayLiveUsageCounters(nextSettings);
|
||||||
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 || {}) };
|
|
||||||
const retentionChanged = previousSettings.historyRetentionMode !== nextSettings.historyRetentionMode;
|
const retentionChanged = previousSettings.historyRetentionMode !== nextSettings.historyRetentionMode;
|
||||||
this.settings = nextSettings;
|
this.settings = nextSettings;
|
||||||
if (retentionChanged) {
|
if (retentionChanged) {
|
||||||
@ -697,14 +705,18 @@ public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const restoredSettings = normalizeSettings(importedSettings);
|
const restoredSettings = normalizeSettings(importedSettings);
|
||||||
|
|
||||||
|
// 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;
|
this.settings = restoredSettings;
|
||||||
saveSettings(this.storagePaths, this.settings);
|
saveSettings(this.storagePaths, this.settings);
|
||||||
this.manager.setSettings(this.settings);
|
this.manager.setSettings(this.settings, { suppressRetroactiveCleanup: true });
|
||||||
|
|
||||||
// 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.
|
|
||||||
if (!hasSession) {
|
|
||||||
this.audit("INFO", "Backup importiert (nur Einstellungen)", {
|
this.audit("INFO", "Backup importiert (nur Einstellungen)", {
|
||||||
accountSummary: buildAccountSummary(this.settings)
|
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.stop();
|
||||||
this.manager.abortAllPostProcessing();
|
this.manager.abortAllPostProcessing();
|
||||||
this.manager.clearPersistTimer();
|
this.manager.clearPersistTimer();
|
||||||
|
|||||||
@ -2081,7 +2081,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
this.emitState();
|
this.emitState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSettings(next: AppSettings): void {
|
public setSettings(next: AppSettings, opts?: { suppressRetroactiveCleanup?: boolean }): void {
|
||||||
const previous = this.settings;
|
const previous = this.settings;
|
||||||
next.totalDownloadedAllTime = Math.max(next.totalDownloadedAllTime || 0, this.settings.totalDownloadedAllTime || 0);
|
next.totalDownloadedAllTime = Math.max(next.totalDownloadedAllTime || 0, this.settings.totalDownloadedAllTime || 0);
|
||||||
next.totalCompletedFilesAllTime = Math.max(next.totalCompletedFilesAllTime || 0, this.settings.totalCompletedFilesAllTime || 0);
|
next.totalCompletedFilesAllTime = Math.max(next.totalCompletedFilesAllTime || 0, this.settings.totalCompletedFilesAllTime || 0);
|
||||||
@ -2145,7 +2145,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
|
|
||||||
this.resolveExistingQueuedOpaqueFilenames();
|
this.resolveExistingQueuedOpaqueFilenames();
|
||||||
void this.cleanupExistingExtractedArchives().catch((err) => logger.warn(`cleanupExistingExtractedArchives Fehler (setSettings): ${compactErrorText(err)}`));
|
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.applyRetroactiveCleanupPolicy();
|
||||||
}
|
}
|
||||||
this.emitState();
|
this.emitState();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user