Round 7 bug fixes (13 fixes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fad0f1060b
commit
75775f2798
@ -317,6 +317,9 @@ export class AppController {
|
||||
// Prevent prepareForShutdown from overwriting the restored session file
|
||||
// with the old in-memory session when the app quits after backup restore.
|
||||
this.manager.skipShutdownPersist = true;
|
||||
// Block all persistence (including persistSoon from any IPC operations
|
||||
// the user might trigger before restarting) to protect the restored backup.
|
||||
this.manager.blockAllPersistence = true;
|
||||
return { restored: true, message: "Backup wiederhergestellt. Bitte App neustarten." };
|
||||
}
|
||||
|
||||
|
||||
@ -803,6 +803,10 @@ export class DownloadManager extends EventEmitter {
|
||||
|
||||
public skipShutdownPersist = false;
|
||||
|
||||
/** Block ALL persistence (persistSoon + shutdown). Set after importBackup to prevent
|
||||
* the old in-memory session from overwriting the restored backup on disk. */
|
||||
public blockAllPersistence = false;
|
||||
|
||||
private debridService: DebridService;
|
||||
|
||||
private invalidateMegaSessionFn?: () => void;
|
||||
@ -1083,7 +1087,7 @@ export class DownloadManager extends EventEmitter {
|
||||
seen.add(id);
|
||||
return true;
|
||||
});
|
||||
const remaining = this.session.packageOrder.filter((id) => !valid.includes(id));
|
||||
const remaining = this.session.packageOrder.filter((id) => !seen.has(id));
|
||||
this.session.packageOrder = [...valid, ...remaining];
|
||||
this.persistSoon();
|
||||
this.emitState(true);
|
||||
@ -3116,6 +3120,7 @@ export class DownloadManager extends EventEmitter {
|
||||
this.session.reconnectUntil = 0;
|
||||
this.session.reconnectReason = "";
|
||||
this.retryAfterByItem.clear();
|
||||
this.retryStateByItem.clear();
|
||||
this.lastGlobalProgressBytes = this.session.totalDownloadedBytes;
|
||||
this.lastGlobalProgressAt = nowMs();
|
||||
this.speedEvents = [];
|
||||
@ -3132,12 +3137,13 @@ export class DownloadManager extends EventEmitter {
|
||||
active.abortReason = "stop";
|
||||
active.abortController.abort("stop");
|
||||
}
|
||||
// Reset all non-finished items to clean "Wartet" state
|
||||
// Reset all non-finished items to clean "Wartet" / "Paket gestoppt" state
|
||||
for (const item of Object.values(this.session.items)) {
|
||||
if (!isFinishedStatus(item.status)) {
|
||||
item.status = "queued";
|
||||
item.speedBps = 0;
|
||||
item.fullStatus = "Wartet";
|
||||
const pkg = this.session.packages[item.packageId];
|
||||
item.fullStatus = pkg && !pkg.enabled ? "Paket gestoppt" : "Wartet";
|
||||
item.updatedAt = nowMs();
|
||||
}
|
||||
}
|
||||
@ -3229,7 +3235,7 @@ export class DownloadManager extends EventEmitter {
|
||||
this.session.summaryText = "";
|
||||
// Persist synchronously on shutdown to guarantee data is written before process exits
|
||||
// Skip if a backup was just imported — the restored session on disk must not be overwritten
|
||||
if (!this.skipShutdownPersist) {
|
||||
if (!this.skipShutdownPersist && !this.blockAllPersistence) {
|
||||
saveSession(this.storagePaths, this.session);
|
||||
saveSettings(this.storagePaths, this.settings);
|
||||
}
|
||||
@ -3494,7 +3500,7 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
|
||||
private persistSoon(): void {
|
||||
if (this.persistTimer) {
|
||||
if (this.persistTimer || this.blockAllPersistence) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6343,18 +6349,9 @@ export class DownloadManager extends EventEmitter {
|
||||
extractCpuPriority: this.settings.extractCpuPriority,
|
||||
onProgress: (progress) => {
|
||||
if (progress.phase === "done") {
|
||||
// Mark all remaining active archives as done
|
||||
for (const [archName, archItems] of activeHybridArchiveMap) {
|
||||
const doneAt = nowMs();
|
||||
const startedAt = hybridArchiveStartTimes.get(archName) || doneAt;
|
||||
const doneLabel = formatExtractDone(doneAt - startedAt);
|
||||
for (const entry of archItems) {
|
||||
if (!isExtractedLabel(entry.fullStatus)) {
|
||||
entry.fullStatus = doneLabel;
|
||||
entry.updatedAt = doneAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Do NOT mark remaining archives as "Done" here — some may have
|
||||
// failed. The post-extraction code (result.failed check) will
|
||||
// assign the correct label. Only clear the tracking maps.
|
||||
activeHybridArchiveMap.clear();
|
||||
hybridArchiveStartTimes.clear();
|
||||
return;
|
||||
@ -6639,18 +6636,9 @@ export class DownloadManager extends EventEmitter {
|
||||
extractCpuPriority: this.settings.extractCpuPriority,
|
||||
onProgress: (progress) => {
|
||||
if (progress.phase === "done") {
|
||||
// Mark all remaining active archives as done
|
||||
for (const [archName, items] of activeArchiveItemsMap) {
|
||||
const doneAt = nowMs();
|
||||
const startedAt = archiveStartTimes.get(archName) || doneAt;
|
||||
const doneLabel = formatExtractDone(doneAt - startedAt);
|
||||
for (const entry of items) {
|
||||
if (!isExtractedLabel(entry.fullStatus)) {
|
||||
entry.fullStatus = doneLabel;
|
||||
entry.updatedAt = doneAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Do NOT mark remaining archives as "Done" here — some may have
|
||||
// failed. The post-extraction code (result.failed check) will
|
||||
// assign the correct label. Only clear the tracking maps.
|
||||
activeArchiveItemsMap.clear();
|
||||
archiveStartTimes.clear();
|
||||
emitExtractStatus("Entpacken 100%", true);
|
||||
@ -6786,9 +6774,9 @@ export class DownloadManager extends EventEmitter {
|
||||
for (const entry of completedItems) {
|
||||
if (/^Entpacken/i.test(entry.fullStatus || "") || /^Passwort/i.test(entry.fullStatus || "")) {
|
||||
entry.fullStatus = "Entpacken abgebrochen (wird fortgesetzt)";
|
||||
}
|
||||
entry.updatedAt = nowMs();
|
||||
}
|
||||
}
|
||||
pkg.status = (pkg.enabled && !this.session.paused) ? "queued" : "paused";
|
||||
pkg.updatedAt = nowMs();
|
||||
logger.info(`Post-Processing Entpacken abgebrochen: pkg=${pkg.name}`);
|
||||
@ -6801,9 +6789,9 @@ export class DownloadManager extends EventEmitter {
|
||||
for (const entry of completedItems) {
|
||||
if (!isExtractedLabel(entry.fullStatus)) {
|
||||
entry.fullStatus = `Entpack-Fehler: ${reason}`;
|
||||
}
|
||||
entry.updatedAt = nowMs();
|
||||
}
|
||||
}
|
||||
pkg.status = "failed";
|
||||
}
|
||||
} finally {
|
||||
|
||||
@ -2195,7 +2195,7 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
|
||||
for (const nestedArchive of nestedCandidates) {
|
||||
if (options.signal?.aborted) throw new Error("aborted:extract");
|
||||
const nestedName = path.basename(nestedArchive);
|
||||
const nestedKey = archiveNameKey(nestedName);
|
||||
const nestedKey = archiveNameKey(`nested:${nestedName}`);
|
||||
if (resumeCompleted.has(nestedKey)) {
|
||||
logger.info(`Nested-Extraction übersprungen (bereits entpackt): ${nestedName}`);
|
||||
continue;
|
||||
@ -2258,7 +2258,7 @@ export async function extractPackageArchives(options: ExtractOptions): Promise<{
|
||||
}
|
||||
|
||||
if (extracted > 0) {
|
||||
const hasOutputAfter = await hasAnyEntries(options.targetDir);
|
||||
const hasOutputAfter = await hasAnyFilesRecursive(options.targetDir);
|
||||
const hadResumeProgress = resumeCompletedAtStart > 0;
|
||||
if (!hasOutputAfter && conflictMode !== "skip" && !hadResumeProgress) {
|
||||
lastError = "Keine entpackten Dateien erkannt";
|
||||
|
||||
@ -78,6 +78,11 @@ async function sleepWithSignal(ms: number, signal?: AbortSignal): Promise<void>
|
||||
await sleep(ms);
|
||||
return;
|
||||
}
|
||||
// Check before entering the Promise constructor to avoid a race where the timer
|
||||
// resolves before the aborted check runs (especially when ms=0).
|
||||
if (signal.aborted) {
|
||||
throw new Error("aborted");
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
let timer: NodeJS.Timeout | null = setTimeout(() => {
|
||||
timer = null;
|
||||
@ -94,10 +99,6 @@ async function sleepWithSignal(ms: number, signal?: AbortSignal): Promise<void>
|
||||
reject(new Error("aborted"));
|
||||
};
|
||||
|
||||
if (signal.aborted) {
|
||||
onAbort();
|
||||
return;
|
||||
}
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
@ -3331,24 +3331,14 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, isFirs
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
case "hoster": return (
|
||||
<span key={col} className="pkg-col pkg-col-hoster" title={(() => {
|
||||
const hosters = [...new Set(items.map((item) => extractHoster(item.url)).filter(Boolean))];
|
||||
return hosters.join(", ");
|
||||
})()}>{(() => {
|
||||
const hosters = [...new Set(items.map((item) => extractHoster(item.url)).filter(Boolean))];
|
||||
return hosters.length > 0 ? hosters.join(", ") : "";
|
||||
})()}</span>
|
||||
);
|
||||
case "account": return (
|
||||
<span key={col} className="pkg-col pkg-col-account" title={(() => {
|
||||
const providers = [...new Set(items.map((item) => item.provider).filter(Boolean))];
|
||||
return providers.map((p) => providerLabels[p!] || p).join(", ");
|
||||
})()}>{(() => {
|
||||
const providers = [...new Set(items.map((item) => item.provider).filter(Boolean))];
|
||||
return providers.length > 0 ? providers.map((p) => providerLabels[p!] || p).join(", ") : "";
|
||||
})()}</span>
|
||||
);
|
||||
case "hoster": {
|
||||
const hosterText = [...new Set(items.map((item) => extractHoster(item.url)).filter(Boolean))].join(", ");
|
||||
return <span key={col} className="pkg-col pkg-col-hoster" title={hosterText}>{hosterText}</span>;
|
||||
}
|
||||
case "account": {
|
||||
const accountText = [...new Set(items.map((item) => item.provider).filter(Boolean))].map((p) => providerLabels[p!] || p).join(", ");
|
||||
return <span key={col} className="pkg-col pkg-col-account" title={accountText}>{accountText}</span>;
|
||||
}
|
||||
case "prio": return (
|
||||
<span key={col} className={`pkg-col pkg-col-prio${pkg.priority === "high" ? " prio-high" : pkg.priority === "low" ? " prio-low" : ""}`}>{pkg.priority === "high" ? "Hoch" : pkg.priority === "low" ? "Niedrig" : ""}</span>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user