diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts
index 34278c9..586756f 100644
--- a/src/main/download-manager.ts
+++ b/src/main/download-manager.ts
@@ -5702,10 +5702,11 @@ export class DownloadManager extends EventEmitter {
this.clearHybridArchiveState(packageId);
const items = pkg.itemIds.map((id) => this.session.items[id]).filter(Boolean) as DownloadItem[];
const completedItems = items.filter((item) => item.status === "completed");
- if (completedItems.length === 0) return;
+ const targetItems = completedItems.filter((item) => !isExtractedLabel(item.fullStatus));
+ if (targetItems.length === 0) return;
pkg.status = "queued";
pkg.updatedAt = nowMs();
- for (const item of completedItems) {
+ for (const item of targetItems) {
if (!isExtractedLabel(item.fullStatus)) {
item.fullStatus = "Entpacken - Ausstehend";
item.updatedAt = nowMs();
@@ -5713,7 +5714,8 @@ export class DownloadManager extends EventEmitter {
}
logger.info(`Extraktion manuell wiederholt: pkg=${pkg.name}`);
this.logPackageForPackage(pkg, "INFO", "Extraktion manuell wiederholt", {
- completedItems: completedItems.length
+ completedItems: completedItems.length,
+ targetedItems: targetItems.length
});
this.persistSoon();
this.emitState(true);
@@ -5730,16 +5732,18 @@ export class DownloadManager extends EventEmitter {
}
const items = pkg.itemIds.map((id) => this.session.items[id]).filter(Boolean) as DownloadItem[];
const completedItems = items.filter((item) => item.status === "completed");
- if (completedItems.length === 0) return;
+ const targetItems = completedItems.filter((item) => !isExtractedLabel(item.fullStatus));
+ if (targetItems.length === 0) return;
pkg.status = "queued";
pkg.updatedAt = nowMs();
- for (const item of completedItems) {
+ for (const item of targetItems) {
item.fullStatus = "Entpacken - Ausstehend";
item.updatedAt = nowMs();
}
- logger.info(`Jetzt entpacken: pkg=${pkg.name}, completed=${completedItems.length}`);
+ logger.info(`Jetzt entpacken: pkg=${pkg.name}, completed=${completedItems.length}, targeted=${targetItems.length}`);
this.logPackageForPackage(pkg, "INFO", "Jetzt entpacken ausgelöst", {
- completedItems: completedItems.length
+ completedItems: completedItems.length,
+ targetedItems: targetItems.length
});
this.persistSoon();
this.emitState(true);
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index e0785c2..a958c1f 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -5581,7 +5581,7 @@ export function App(): ReactElement {
{hasPackages && !multi && (() => {
const pkg = snapshot.session.packages[contextMenu.packageId];
const items = pkg?.itemIds.map((id) => snapshot.session.items[id]).filter(Boolean) || [];
- const someCompleted = items.some((item) => item && item.status === "completed");
+ const someCompleted = items.some((item) => item && item.status === "completed" && !/^Entpackt\b/i.test(item.fullStatus || ""));
return (<>
{someCompleted && (
@@ -6121,4 +6121,3 @@ const PackageCard = memo(function PackageCard({ pkg, items, packageSpeed, stripe
return true;
});
-
diff --git a/tests/download-manager.test.ts b/tests/download-manager.test.ts
index 0c34588..7251f60 100644
--- a/tests/download-manager.test.ts
+++ b/tests/download-manager.test.ts
@@ -227,6 +227,84 @@ describe("download manager", () => {
expect((manager as any).shouldCollapseQuickPostProcessRequeue(packageId)).toBe(false);
});
+ it("extractNow only re-arms completed items that are not already extracted", () => {
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-extract-now-"));
+ tempDirs.push(root);
+
+ const session = emptySession();
+ const packageId = "extract-now-pkg";
+ const createdAt = Date.now() - 20_000;
+ const outputDir = path.join(root, "downloads", "Extract Now Test");
+ const extractDir = path.join(root, "extract", "Extract Now Test");
+ fs.mkdirSync(outputDir, { recursive: true });
+ fs.mkdirSync(extractDir, { recursive: true });
+
+ const specs = [
+ { id: "extract-now-item-1", fileName: "show.e01.rar", fullStatus: "Entpackt - Done (<1s)" },
+ { id: "extract-now-item-2", fileName: "show.e02.rar", fullStatus: "Entpackt - Done (1.2s)" },
+ { id: "extract-now-item-3", fileName: "show.e03.rar", fullStatus: "Entpacken - Ausstehend" }
+ ] as const;
+
+ session.packageOrder = [packageId];
+ session.packages[packageId] = {
+ id: packageId,
+ name: "Extract Now Test",
+ outputDir,
+ extractDir,
+ status: "completed",
+ itemIds: specs.map((spec) => spec.id),
+ cancelled: false,
+ enabled: true,
+ createdAt,
+ updatedAt: createdAt
+ };
+
+ for (const spec of specs) {
+ const targetPath = path.join(outputDir, spec.fileName);
+ fs.writeFileSync(targetPath, Buffer.alloc(128, 1));
+ session.items[spec.id] = {
+ id: spec.id,
+ packageId,
+ url: `https://example.com/${spec.fileName}`,
+ provider: "realdebrid",
+ status: "completed",
+ retries: 0,
+ speedBps: 0,
+ downloadedBytes: 128,
+ totalBytes: 128,
+ progressPercent: 100,
+ fileName: spec.fileName,
+ targetPath,
+ resumable: true,
+ attempts: 1,
+ lastError: "",
+ fullStatus: spec.fullStatus,
+ createdAt,
+ updatedAt: createdAt
+ };
+ }
+
+ const manager = new DownloadManager(
+ {
+ ...defaultSettings(),
+ token: "rd-token",
+ outputDir: path.join(root, "downloads"),
+ extractDir: path.join(root, "extract"),
+ autoExtract: true,
+ hybridExtract: true
+ },
+ session,
+ createStoragePaths(path.join(root, "state"))
+ );
+
+ manager.extractNow(packageId);
+
+ expect((manager as any).session.items["extract-now-item-1"].fullStatus).toBe("Entpackt - Done (<1s)");
+ expect((manager as any).session.items["extract-now-item-2"].fullStatus).toBe("Entpackt - Done (1.2s)");
+ expect((manager as any).session.items["extract-now-item-3"].fullStatus).toBe("Entpacken - Ausstehend");
+ expect((manager as any).session.packages[packageId].status).toBe("queued");
+ });
+
function createCompletedArchiveSession(root: string, packageName: string, extractedFileName: string): {
session: ReturnType;
packageId: string;