Compare commits
6 Commits
v2.0.0-bet
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a7467a8b0 | ||
|
|
f25e61573d | ||
|
|
25b7104580 | ||
|
|
e0eab43763 | ||
|
|
4df0d40ece | ||
|
|
3567cc173c |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader-beta",
|
"name": "real-debrid-downloader-beta",
|
||||||
"version": "2.0.0-beta.2",
|
"version": "2.0.0-beta.5",
|
||||||
"description": "Desktop downloader",
|
"description": "Desktop downloader",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export const MAX_LINK_ARTIFACT_BYTES = 256 * 1024;
|
|||||||
export const SPEED_WINDOW_SECONDS = 1;
|
export const SPEED_WINDOW_SECONDS = 1;
|
||||||
export const CLIPBOARD_POLL_INTERVAL_MS = 2000;
|
export const CLIPBOARD_POLL_INTERVAL_MS = 2000;
|
||||||
|
|
||||||
export const DEFAULT_UPDATE_REPO = "Administrator/real-debrid-downloader";
|
export const DEFAULT_UPDATE_REPO = "Administrator/beta-real-debrid-downloader";
|
||||||
|
|
||||||
export function defaultSettings(): AppSettings {
|
export function defaultSettings(): AppSettings {
|
||||||
const baseDir = path.join(os.homedir(), "Downloads", "RealDebrid");
|
const baseDir = path.join(os.homedir(), "Downloads", "RealDebrid");
|
||||||
|
|||||||
@ -523,23 +523,37 @@ export class DownloadManager extends EventEmitter {
|
|||||||
pkg.cancelled = true;
|
pkg.cancelled = true;
|
||||||
pkg.status = "cancelled";
|
pkg.status = "cancelled";
|
||||||
pkg.updatedAt = nowMs();
|
pkg.updatedAt = nowMs();
|
||||||
for (const itemId of pkg.itemIds) {
|
const outputDir = pkg.outputDir;
|
||||||
|
const itemIds = [...pkg.itemIds];
|
||||||
|
|
||||||
|
for (const itemId of itemIds) {
|
||||||
const item = this.session.items[itemId];
|
const item = this.session.items[itemId];
|
||||||
if (!item) continue;
|
if (!item) continue;
|
||||||
const slot = this.activeTasks.get(itemId);
|
const slot = this.activeTasks.get(itemId);
|
||||||
if (slot) { slot.abortReason = "cancel"; slot.abortController.abort("cancel"); }
|
if (slot) { slot.abortReason = "cancel"; slot.abortController.abort("cancel"); }
|
||||||
if (!isFinishedStatus(item.status)) {
|
if (item.status !== "completed") {
|
||||||
item.status = "cancelled";
|
this.runOutcomes.set(itemId, "cancelled");
|
||||||
item.fullStatus = "Abgebrochen";
|
|
||||||
item.speedBps = 0;
|
|
||||||
item.updatedAt = nowMs();
|
|
||||||
}
|
}
|
||||||
this.releaseTargetPath(itemId);
|
this.releaseTargetPath(itemId);
|
||||||
this.retryManager.removeItem(itemId);
|
this.retryManager.removeItem(itemId);
|
||||||
|
this.cachedDirectUrls.delete(itemId);
|
||||||
|
this.itemContributedBytes.delete(itemId);
|
||||||
|
delete this.session.items[itemId];
|
||||||
|
this.itemCount = Math.max(0, this.itemCount - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.postProcessor.abortPackage(packageId);
|
this.postProcessor.abortPackage(packageId);
|
||||||
|
this.historyRecordedPackages.delete(packageId);
|
||||||
|
this.hybridExtractRequeue.delete(packageId);
|
||||||
|
this.packagePostProcessTasks.delete(packageId);
|
||||||
|
delete this.session.packages[packageId];
|
||||||
|
this.session.packageOrder = this.session.packageOrder.filter(id => id !== packageId);
|
||||||
|
|
||||||
this.persistSoon();
|
this.persistSoon();
|
||||||
this.emitState();
|
this.emitState();
|
||||||
|
|
||||||
|
// Cleanup artifacts in background
|
||||||
|
void cleanupCancelledPackageArtifactsAsync(outputDir).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
public resetPackage(packageId: string): void {
|
public resetPackage(packageId: string): void {
|
||||||
@ -1580,16 +1594,47 @@ export class DownloadManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private normalizeSessionStatuses(): void {
|
private normalizeSessionStatuses(): void {
|
||||||
|
// Critical: reset session-level running state on startup.
|
||||||
|
// Without this, if the app crashes while running, session.running stays true
|
||||||
|
// in the persisted JSON and start() silently returns on next launch.
|
||||||
|
this.session.running = false;
|
||||||
|
this.session.paused = false;
|
||||||
|
this.session.reconnectUntil = 0;
|
||||||
|
this.session.reconnectReason = "";
|
||||||
|
|
||||||
for (const item of Object.values(this.session.items)) {
|
for (const item of Object.values(this.session.items)) {
|
||||||
if (item.status === "downloading" || item.status === "validating" || item.status === "integrity_check") {
|
// Items that were stopped mid-run should be re-queued
|
||||||
|
if (item.status === "cancelled" && item.fullStatus === "Gestoppt") {
|
||||||
item.status = "queued";
|
item.status = "queued";
|
||||||
item.fullStatus = "Wartet";
|
item.fullStatus = "Wartet";
|
||||||
|
item.lastError = "";
|
||||||
|
item.speedBps = 0;
|
||||||
|
item.provider = null;
|
||||||
|
item.updatedAt = nowMs();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Items that were extracting/checking integrity are already fully downloaded
|
||||||
|
if (item.status === "extracting" || item.status === "integrity_check") {
|
||||||
|
item.status = "completed";
|
||||||
|
item.fullStatus = `Fertig (${humanSize(item.downloadedBytes)})`;
|
||||||
|
item.speedBps = 0;
|
||||||
|
item.updatedAt = nowMs();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Active/paused/reconnecting items → re-queue
|
||||||
|
if (item.status === "downloading" || item.status === "validating"
|
||||||
|
|| item.status === "paused" || item.status === "reconnect_wait") {
|
||||||
|
item.status = "queued";
|
||||||
|
const pkg = this.session.packages[item.packageId];
|
||||||
|
item.fullStatus = (pkg && pkg.enabled === false) ? "Paket gestoppt" : "Wartet";
|
||||||
item.speedBps = 0;
|
item.speedBps = 0;
|
||||||
item.updatedAt = nowMs();
|
item.updatedAt = nowMs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const pkg of Object.values(this.session.packages)) {
|
for (const pkg of Object.values(this.session.packages)) {
|
||||||
if (pkg.status === "downloading" || pkg.status === "validating" || pkg.status === "extracting") {
|
if (pkg.status === "downloading" || pkg.status === "validating"
|
||||||
|
|| pkg.status === "extracting" || pkg.status === "integrity_check"
|
||||||
|
|| pkg.status === "paused" || pkg.status === "reconnect_wait") {
|
||||||
pkg.status = "queued";
|
pkg.status = "queued";
|
||||||
pkg.updatedAt = nowMs();
|
pkg.updatedAt = nowMs();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ afterEach(() => {
|
|||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
it("normalizes update repo input", () => {
|
it("normalizes update repo input", () => {
|
||||||
expect(normalizeUpdateRepo("")).toBe("Administrator/real-debrid-downloader");
|
expect(normalizeUpdateRepo("")).toBe("Administrator/beta-real-debrid-downloader");
|
||||||
expect(normalizeUpdateRepo("owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("owner/repo")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("https://codeberg.org/owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("https://codeberg.org/owner/repo")).toBe("owner/repo");
|
||||||
expect(normalizeUpdateRepo("https://www.codeberg.org/owner/repo")).toBe("owner/repo");
|
expect(normalizeUpdateRepo("https://www.codeberg.org/owner/repo")).toBe("owner/repo");
|
||||||
@ -518,14 +518,14 @@ describe("normalizeUpdateRepo extended", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns default for malformed inputs", () => {
|
it("returns default for malformed inputs", () => {
|
||||||
expect(normalizeUpdateRepo("just-one-part")).toBe("Administrator/real-debrid-downloader");
|
expect(normalizeUpdateRepo("just-one-part")).toBe("Administrator/beta-real-debrid-downloader");
|
||||||
expect(normalizeUpdateRepo(" ")).toBe("Administrator/real-debrid-downloader");
|
expect(normalizeUpdateRepo(" ")).toBe("Administrator/beta-real-debrid-downloader");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects traversal-like owner or repo segments", () => {
|
it("rejects traversal-like owner or repo segments", () => {
|
||||||
expect(normalizeUpdateRepo("../owner/repo")).toBe("Administrator/real-debrid-downloader");
|
expect(normalizeUpdateRepo("../owner/repo")).toBe("Administrator/beta-real-debrid-downloader");
|
||||||
expect(normalizeUpdateRepo("owner/../repo")).toBe("Administrator/real-debrid-downloader");
|
expect(normalizeUpdateRepo("owner/../repo")).toBe("Administrator/beta-real-debrid-downloader");
|
||||||
expect(normalizeUpdateRepo("https://codeberg.org/owner/../../repo")).toBe("Administrator/real-debrid-downloader");
|
expect(normalizeUpdateRepo("https://codeberg.org/owner/../../repo")).toBe("Administrator/beta-real-debrid-downloader");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles www prefix", () => {
|
it("handles www prefix", () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user