fix(downloads): restore actionable package states and extraction controls

Resolve child archive selections to complete multipart sets, support bulk package extraction, refresh extraction passwords at execution time, and keep archive failures scoped by their full paths.

Restore sortable download columns, preserve verified availability, classify package retry and extraction states, secure link copying through the preload bridge, and release cancelled provider work without stale cooldowns.
This commit is contained in:
Sucukdeluxe
2026-08-22 22:52:49 +02:00
parent a5920869a3
commit 55b8911e94
33 changed files with 2187 additions and 561 deletions
+9 -15
View File
@@ -26,6 +26,8 @@ import { migrateProductUserDataDirectory } from "./storage";
import { validateCollectorContainerInspectionRequest, validateCollectorInspectionRequest } from "../shared/collector";
import { DailyStartScheduler, hasDailyStartRulePatch, prepareDailyStartSettingsPatch } from "./daily-start-scheduler";
import { forceDarkNativeTheme } from "./native-theme";
import { normalizeExtractNowRequest } from "../shared/extract-now";
import { validateClipboardWriteText } from "./clipboard-write";
forceDarkNativeTheme(nativeTheme);
@@ -44,7 +46,6 @@ function validatePlainObject(value: unknown, name: string): Record<string, unkno
}
const IMPORT_QUEUE_MAX_BYTES = 10 * 1024 * 1024;
const CLIPBOARD_WRITE_MAX_BYTES = 4096;
const RENAME_PACKAGE_MAX_CHARS = 240;
const RESETTABLE_PROVIDER_KEYS = new Set<DebridProvider>([
"realdebrid",
@@ -645,10 +646,9 @@ function registerIpcHandlers(): void {
validateString(packageId, "packageId");
return controller.retryExtraction(packageId);
});
handleTrusted(IPC_CHANNELS.EXTRACT_NOW, (_event: IpcMainInvokeEvent, packageId: string) => {
validateString(packageId, "packageId");
return controller.extractNow(packageId);
});
handleTrusted(IPC_CHANNELS.EXTRACT_NOW, (_event: IpcMainInvokeEvent, request: unknown) => {
return controller.extractNow(normalizeExtractNowRequest(request));
});
handleTrusted(IPC_CHANNELS.RESET_PACKAGE, (_event: IpcMainInvokeEvent, packageId: string) => {
validateString(packageId, "packageId");
return controller.resetPackage(packageId);
@@ -713,16 +713,10 @@ function registerIpcHandlers(): void {
updateClipboardWatcher();
return next;
});
handleTrusted(IPC_CHANNELS.WRITE_CLIPBOARD_TEXT, (_event: IpcMainInvokeEvent, rawText: unknown) => {
const text = validateString(rawText, "text");
const bytes = Buffer.byteLength(text, "utf8");
if (!text.trim()) {
throw new Error("text darf nicht leer sein");
}
if (bytes > CLIPBOARD_WRITE_MAX_BYTES) {
throw new Error(`text ist zu groß (max ${CLIPBOARD_WRITE_MAX_BYTES} Bytes)`);
}
try {
handleTrusted(IPC_CHANNELS.WRITE_CLIPBOARD_TEXT, (_event: IpcMainInvokeEvent, rawText: unknown) => {
const text = validateClipboardWriteText(rawText);
const bytes = Buffer.byteLength(text, "utf8");
try {
clipboard.writeText(text);
return true;
} catch (error) {