Release v1.5.89

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe
2026-03-04 02:57:33 +01:00
co-authored by Claude Opus 4.6
parent a18ab484cc
commit 92101e249a
8 changed files with 147 additions and 7 deletions
+16
View File
@@ -210,6 +210,10 @@ export class AppController {
await this.manager.startPackages(packageIds);
}
public async startItems(itemIds: string[]): Promise<void> {
await this.manager.startItems(itemIds);
}
public stop(): void {
this.manager.stop();
}
@@ -308,6 +312,18 @@ export class AppController {
clearHistory(this.storagePaths);
}
public setPackagePriority(packageId: string, priority: string): void {
this.manager.setPackagePriority(packageId, priority as any);
}
public skipItems(itemIds: string[]): void {
this.manager.skipItems(itemIds);
}
public resetItems(itemIds: string[]): void {
this.manager.resetItems(itemIds);
}
public removeHistoryEntry(entryId: string): void {
removeHistoryEntry(this.storagePaths, entryId);
}
+95
View File
@@ -2627,6 +2627,101 @@ export class DownloadManager extends EventEmitter {
});
}
public async startItems(itemIds: string[]): Promise<void> {
const targetSet = new Set(itemIds);
// Collect affected package IDs
const affectedPackageIds = new Set<string>();
for (const itemId of targetSet) {
const item = this.session.items[itemId];
if (item) affectedPackageIds.add(item.packageId);
}
// Enable affected packages if disabled
for (const pkgId of affectedPackageIds) {
const pkg = this.session.packages[pkgId];
if (pkg && !pkg.enabled) {
pkg.enabled = true;
}
}
// Recover stopped items
for (const itemId of targetSet) {
const item = this.session.items[itemId];
if (!item) continue;
if (item.status === "cancelled" && item.fullStatus === "Gestoppt") {
const pkg = this.session.packages[item.packageId];
if (pkg && !pkg.cancelled && pkg.enabled) {
item.status = "queued";
item.fullStatus = "Wartet";
item.lastError = "";
item.speedBps = 0;
item.updatedAt = nowMs();
}
}
}
// If already running, add items to scheduler
if (this.session.running) {
for (const itemId of targetSet) {
const item = this.session.items[itemId];
if (!item) continue;
if (item.status === "queued" || item.status === "reconnect_wait") {
this.runItemIds.add(item.id);
this.runPackageIds.add(item.packageId);
}
}
this.persistSoon();
this.emitState(true);
return;
}
// Not running: start with only specified items
const runItems = [...targetSet]
.map((id) => this.session.items[id])
.filter((item) => {
if (!item) return false;
if (item.status !== "queued" && item.status !== "reconnect_wait") return false;
const pkg = this.session.packages[item.packageId];
return Boolean(pkg && !pkg.cancelled && pkg.enabled);
});
if (runItems.length === 0) {
this.persistSoon();
this.emitState(true);
return;
}
this.runItemIds = new Set(runItems.map((item) => item.id));
this.runPackageIds = new Set(runItems.map((item) => item.packageId));
this.runOutcomes.clear();
this.runCompletedPackages.clear();
this.retryAfterByItem.clear();
this.session.running = true;
this.session.paused = false;
this.session.runStartedAt = nowMs();
this.session.totalDownloadedBytes = 0;
this.session.summaryText = "";
this.session.reconnectUntil = 0;
this.session.reconnectReason = "";
this.speedEvents = [];
this.speedBytesLastWindow = 0;
this.speedBytesPerPackage.clear();
this.speedEventsHead = 0;
this.lastGlobalProgressBytes = 0;
this.lastGlobalProgressAt = nowMs();
this.summary = null;
this.nonResumableActive = 0;
this.persistSoon();
this.emitState(true);
logger.info(`Start (nur Items: ${itemIds.length}): ${runItems.length} Items`);
void this.ensureScheduler().catch((error) => {
logger.error(`Scheduler abgestürzt: ${compactErrorText(error)}`);
this.session.running = false;
this.session.paused = false;
this.persistSoon();
this.emitState(true);
});
}
public async start(): Promise<void> {
if (this.session.running) {
return;
+21 -1
View File
@@ -292,6 +292,10 @@ function registerIpcHandlers(): void {
if (!Array.isArray(packageIds)) throw new Error("packageIds muss ein Array sein");
return controller.startPackages(packageIds);
});
ipcMain.handle(IPC_CHANNELS.START_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
if (!Array.isArray(itemIds)) throw new Error("itemIds muss ein Array sein");
return controller.startItems(itemIds);
});
ipcMain.handle(IPC_CHANNELS.STOP, () => controller.stop());
ipcMain.handle(IPC_CHANNELS.TOGGLE_PAUSE, () => controller.togglePause());
ipcMain.handle(IPC_CHANNELS.CANCEL_PACKAGE, (_event: IpcMainInvokeEvent, packageId: string) => {
@@ -339,13 +343,29 @@ function registerIpcHandlers(): void {
if (!Array.isArray(itemIds)) throw new Error("itemIds must be an array");
return controller.skipItems(itemIds);
});
ipcMain.handle(IPC_CHANNELS.RESET_ITEMS, (_event: IpcMainInvokeEvent, itemIds: string[]) => {
if (!Array.isArray(itemIds)) throw new Error("itemIds must be an array");
return controller.resetItems(itemIds);
});
ipcMain.handle(IPC_CHANNELS.GET_HISTORY, () => controller.getHistory());
ipcMain.handle(IPC_CHANNELS.CLEAR_HISTORY, () => controller.clearHistory());
ipcMain.handle(IPC_CHANNELS.REMOVE_HISTORY_ENTRY, (_event: IpcMainInvokeEvent, entryId: string) => {
validateString(entryId, "entryId");
return controller.removeHistoryEntry(entryId);
});
ipcMain.handle(IPC_CHANNELS.EXPORT_QUEUE, () => controller.exportQueue());
ipcMain.handle(IPC_CHANNELS.EXPORT_QUEUE, async () => {
const options = {
defaultPath: `rd-queue-export.json`,
filters: [{ name: "Queue Export", extensions: ["json"] }]
};
const result = mainWindow ? await dialog.showSaveDialog(mainWindow, options) : await dialog.showSaveDialog(options);
if (result.canceled || !result.filePath) {
return { saved: false };
}
const json = controller.exportQueue();
await fs.promises.writeFile(result.filePath, json, "utf8");
return { saved: true };
});
ipcMain.handle(IPC_CHANNELS.IMPORT_QUEUE, (_event: IpcMainInvokeEvent, json: string) => {
validateString(json, "json");
const bytes = Buffer.byteLength(json, "utf8");