Release v1.6.16
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
56c0b633c8
commit
b02aef2af9
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.6.15",
|
"version": "1.6.16",
|
||||||
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -81,8 +81,15 @@ export class AppController {
|
|||||||
void this.manager.getStartConflicts().then((conflicts) => {
|
void this.manager.getStartConflicts().then((conflicts) => {
|
||||||
const hasConflicts = conflicts.length > 0;
|
const hasConflicts = conflicts.length > 0;
|
||||||
if (this.hasAnyProviderToken(this.settings) && !hasConflicts) {
|
if (this.hasAnyProviderToken(this.settings) && !hasConflicts) {
|
||||||
this.autoResumePending = true;
|
// If the onState handler is already set (renderer connected), start immediately.
|
||||||
logger.info("Auto-Resume beim Start vorgemerkt");
|
// Otherwise mark as pending so the onState setter triggers the start.
|
||||||
|
if (this.onStateHandler) {
|
||||||
|
logger.info("Auto-Resume beim Start aktiviert (nach Konflikt-Check)");
|
||||||
|
void this.manager.start().catch((err) => logger.warn(`Auto-Resume Start Fehler: ${String(err)}`));
|
||||||
|
} else {
|
||||||
|
this.autoResumePending = true;
|
||||||
|
logger.info("Auto-Resume beim Start vorgemerkt");
|
||||||
|
}
|
||||||
} else if (hasConflicts) {
|
} else if (hasConflicts) {
|
||||||
logger.info("Auto-Resume übersprungen: Start-Konflikte erkannt");
|
logger.info("Auto-Resume übersprungen: Start-Konflikte erkannt");
|
||||||
}
|
}
|
||||||
@ -299,6 +306,9 @@ export class AppController {
|
|||||||
// so no extraction tasks from it should keep running.
|
// so no extraction tasks from it should keep running.
|
||||||
this.manager.stop();
|
this.manager.stop();
|
||||||
this.manager.abortAllPostProcessing();
|
this.manager.abortAllPostProcessing();
|
||||||
|
// Cancel any deferred persist timer so the old in-memory session
|
||||||
|
// does not overwrite the restored session file on disk.
|
||||||
|
this.manager.clearPersistTimer();
|
||||||
const restoredSession = parsed.session as ReturnType<typeof loadSession>;
|
const restoredSession = parsed.session as ReturnType<typeof loadSession>;
|
||||||
saveSession(this.storagePaths, restoredSession);
|
saveSession(this.storagePaths, restoredSession);
|
||||||
return { restored: true, message: "Backup wiederhergestellt. Bitte App neustarten." };
|
return { restored: true, message: "Backup wiederhergestellt. Bitte App neustarten." };
|
||||||
|
|||||||
@ -2527,6 +2527,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
pkg.cancelled = false;
|
pkg.cancelled = false;
|
||||||
pkg.enabled = true;
|
pkg.enabled = true;
|
||||||
pkg.updatedAt = nowMs();
|
pkg.updatedAt = nowMs();
|
||||||
|
this.historyRecordedPackages.delete(packageId);
|
||||||
|
|
||||||
logger.info(`Paket "${pkg.name}" zurückgesetzt (${itemIds.length} Items)`);
|
logger.info(`Paket "${pkg.name}" zurückgesetzt (${itemIds.length} Items)`);
|
||||||
this.persistSoon();
|
this.persistSoon();
|
||||||
@ -3302,7 +3303,7 @@ export class DownloadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearPersistTimer(): void {
|
public clearPersistTimer(): void {
|
||||||
if (!this.persistTimer) {
|
if (!this.persistTimer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3557,7 +3558,11 @@ export class DownloadManager extends EventEmitter {
|
|||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
this.packagePostProcessWaiters.push({ packageId, resolve });
|
this.packagePostProcessWaiters.push({ packageId, resolve });
|
||||||
});
|
});
|
||||||
this.packagePostProcessActive += 1;
|
// Guard: stop() may have reset the counter to 0 while we were waiting.
|
||||||
|
// Only increment if below max to avoid phantom slot usage.
|
||||||
|
if (this.packagePostProcessActive < maxConcurrent) {
|
||||||
|
this.packagePostProcessActive += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private releasePostProcessSlot(): void {
|
private releasePostProcessSlot(): void {
|
||||||
@ -6715,9 +6720,14 @@ export class DownloadManager extends EventEmitter {
|
|||||||
};
|
};
|
||||||
this.session.summaryText = `Summary: Dauer ${duration}s, Ø Speed ${humanSize(avgSpeed)}/s, Erfolg ${success}/${total}`;
|
this.session.summaryText = `Summary: Dauer ${duration}s, Ø Speed ${humanSize(avgSpeed)}/s, Erfolg ${success}/${total}`;
|
||||||
this.runItemIds.clear();
|
this.runItemIds.clear();
|
||||||
this.runPackageIds.clear();
|
|
||||||
this.runOutcomes.clear();
|
this.runOutcomes.clear();
|
||||||
this.runCompletedPackages.clear();
|
// Keep runPackageIds and runCompletedPackages alive when post-processing tasks
|
||||||
|
// are still running (autoExtractWhenStopped) so handlePackagePostProcessing()
|
||||||
|
// can still update runCompletedPackages. They are cleared by the next start().
|
||||||
|
if (this.packagePostProcessTasks.size === 0) {
|
||||||
|
this.runPackageIds.clear();
|
||||||
|
this.runCompletedPackages.clear();
|
||||||
|
}
|
||||||
this.retryAfterByItem.clear();
|
this.retryAfterByItem.clear();
|
||||||
this.retryStateByItem.clear();
|
this.retryStateByItem.clear();
|
||||||
this.reservedTargetPaths.clear();
|
this.reservedTargetPaths.clear();
|
||||||
|
|||||||
@ -50,6 +50,7 @@ process.on("unhandledRejection", (reason) => {
|
|||||||
let mainWindow: BrowserWindow | null = null;
|
let mainWindow: BrowserWindow | null = null;
|
||||||
let tray: Tray | null = null;
|
let tray: Tray | null = null;
|
||||||
let clipboardTimer: ReturnType<typeof setInterval> | null = null;
|
let clipboardTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
|
let updateQuitTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
let lastClipboardText = "";
|
let lastClipboardText = "";
|
||||||
const controller = new AppController();
|
const controller = new AppController();
|
||||||
const CLIPBOARD_MAX_TEXT_CHARS = 50_000;
|
const CLIPBOARD_MAX_TEXT_CHARS = 50_000;
|
||||||
@ -236,7 +237,7 @@ function registerIpcHandlers(): void {
|
|||||||
mainWindow.webContents.send(IPC_CHANNELS.UPDATE_INSTALL_PROGRESS, progress);
|
mainWindow.webContents.send(IPC_CHANNELS.UPDATE_INSTALL_PROGRESS, progress);
|
||||||
});
|
});
|
||||||
if (result.started) {
|
if (result.started) {
|
||||||
setTimeout(() => {
|
updateQuitTimer = setTimeout(() => {
|
||||||
app.quit();
|
app.quit();
|
||||||
}, 2500);
|
}, 2500);
|
||||||
}
|
}
|
||||||
@ -289,8 +290,8 @@ function registerIpcHandlers(): void {
|
|||||||
ipcMain.handle(IPC_CHANNELS.CLEAR_ALL, () => controller.clearAll());
|
ipcMain.handle(IPC_CHANNELS.CLEAR_ALL, () => controller.clearAll());
|
||||||
ipcMain.handle(IPC_CHANNELS.START, () => controller.start());
|
ipcMain.handle(IPC_CHANNELS.START, () => controller.start());
|
||||||
ipcMain.handle(IPC_CHANNELS.START_PACKAGES, (_event: IpcMainInvokeEvent, packageIds: string[]) => {
|
ipcMain.handle(IPC_CHANNELS.START_PACKAGES, (_event: IpcMainInvokeEvent, packageIds: string[]) => {
|
||||||
if (!Array.isArray(packageIds)) throw new Error("packageIds muss ein Array sein");
|
validateStringArray(packageIds ?? [], "packageIds");
|
||||||
return controller.startPackages(packageIds);
|
return controller.startPackages(packageIds ?? []);
|
||||||
});
|
});
|
||||||
ipcMain.handle(IPC_CHANNELS.START_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
|
ipcMain.handle(IPC_CHANNELS.START_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
|
||||||
validateStringArray(itemIds ?? [], "itemIds");
|
validateStringArray(itemIds ?? [], "itemIds");
|
||||||
@ -337,15 +338,18 @@ function registerIpcHandlers(): void {
|
|||||||
ipcMain.handle(IPC_CHANNELS.SET_PACKAGE_PRIORITY, (_event: IpcMainInvokeEvent, packageId: string, priority: string) => {
|
ipcMain.handle(IPC_CHANNELS.SET_PACKAGE_PRIORITY, (_event: IpcMainInvokeEvent, packageId: string, priority: string) => {
|
||||||
validateString(packageId, "packageId");
|
validateString(packageId, "packageId");
|
||||||
validateString(priority, "priority");
|
validateString(priority, "priority");
|
||||||
return controller.setPackagePriority(packageId, priority as any);
|
if (priority !== "high" && priority !== "normal" && priority !== "low") {
|
||||||
|
throw new Error("priority muss 'high', 'normal' oder 'low' sein");
|
||||||
|
}
|
||||||
|
return controller.setPackagePriority(packageId, priority);
|
||||||
});
|
});
|
||||||
ipcMain.handle(IPC_CHANNELS.SKIP_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
|
ipcMain.handle(IPC_CHANNELS.SKIP_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
|
||||||
if (!Array.isArray(itemIds)) throw new Error("itemIds must be an array");
|
validateStringArray(itemIds ?? [], "itemIds");
|
||||||
return controller.skipItems(itemIds);
|
return controller.skipItems(itemIds ?? []);
|
||||||
});
|
});
|
||||||
ipcMain.handle(IPC_CHANNELS.RESET_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
|
ipcMain.handle(IPC_CHANNELS.RESET_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
|
||||||
if (!Array.isArray(itemIds)) throw new Error("itemIds must be an array");
|
validateStringArray(itemIds ?? [], "itemIds");
|
||||||
return controller.resetItems(itemIds);
|
return controller.resetItems(itemIds ?? []);
|
||||||
});
|
});
|
||||||
ipcMain.handle(IPC_CHANNELS.GET_HISTORY, () => controller.getHistory());
|
ipcMain.handle(IPC_CHANNELS.GET_HISTORY, () => controller.getHistory());
|
||||||
ipcMain.handle(IPC_CHANNELS.CLEAR_HISTORY, () => controller.clearHistory());
|
ipcMain.handle(IPC_CHANNELS.CLEAR_HISTORY, () => controller.clearHistory());
|
||||||
@ -493,6 +497,7 @@ app.on("window-all-closed", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.on("before-quit", () => {
|
app.on("before-quit", () => {
|
||||||
|
if (updateQuitTimer) { clearTimeout(updateQuitTimer); updateQuitTimer = null; }
|
||||||
stopClipboardWatcher();
|
stopClipboardWatcher();
|
||||||
destroyTray();
|
destroyTray();
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -2935,7 +2935,7 @@ export function App(): ReactElement {
|
|||||||
{(hasPackages || hasStartableItems) && (
|
{(hasPackages || hasStartableItems) && (
|
||||||
<button className="ctx-menu-item" onClick={() => {
|
<button className="ctx-menu-item" onClick={() => {
|
||||||
const pkgIds = [...selectedIds].filter((id) => snapshot.session.packages[id]);
|
const pkgIds = [...selectedIds].filter((id) => snapshot.session.packages[id]);
|
||||||
const itemIds = [...selectedIds].filter((id) => snapshot.session.items[id]);
|
const itemIds = [...selectedIds].filter((id) => { const it = snapshot.session.items[id]; return it && startableStatuses.has(it.status); });
|
||||||
if (pkgIds.length > 0) void window.rd.startPackages(pkgIds);
|
if (pkgIds.length > 0) void window.rd.startPackages(pkgIds);
|
||||||
if (itemIds.length > 0) void window.rd.startItems(itemIds);
|
if (itemIds.length > 0) void window.rd.startItems(itemIds);
|
||||||
setContextMenu(null);
|
setContextMenu(null);
|
||||||
@ -3071,9 +3071,9 @@ export function App(): ReactElement {
|
|||||||
const contextEntry = historyEntries.find(e => e.id === historyCtxMenu.entryId);
|
const contextEntry = historyEntries.find(e => e.id === historyCtxMenu.entryId);
|
||||||
const hasUrls = (contextEntry?.urls?.length ?? 0) > 0;
|
const hasUrls = (contextEntry?.urls?.length ?? 0) > 0;
|
||||||
const removeSelected = (): void => {
|
const removeSelected = (): void => {
|
||||||
const ids = [...selectedHistoryIds];
|
const idSet = new Set(selectedHistoryIds);
|
||||||
void Promise.all(ids.map(id => window.rd.removeHistoryEntry(id))).then(() => {
|
void Promise.all([...idSet].map(id => window.rd.removeHistoryEntry(id))).then(() => {
|
||||||
setHistoryEntries((prev) => prev.filter((e) => !selectedHistoryIds.has(e.id)));
|
setHistoryEntries((prev) => prev.filter((e) => !idSet.has(e.id)));
|
||||||
setSelectedHistoryIds(new Set());
|
setSelectedHistoryIds(new Set());
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
void window.rd.getHistory().then((entries) => { setHistoryEntries(entries); setSelectedHistoryIds(new Set()); }).catch(() => {});
|
void window.rd.getHistory().then((entries) => { setHistoryEntries(entries); setSelectedHistoryIds(new Set()); }).catch(() => {});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user