Release v1.5.89
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a18ab484cc
commit
92101e249a
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user