From 935f39d7db8cc03569279b92bb87c86d1dcf8ecd Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Sat, 15 Aug 2026 21:48:37 +0200 Subject: [PATCH] fix(realdebrid): respect dismissed login windows Remember a user-dismissed browser login per Real-Debrid web account so queued and repeated unrestrict attempts cannot reopen it. Clear the suppression only after explicit login or successful authentication, and close successful login windows without treating them as user cancellations. --- src/main/realdebrid-web.ts | 25 +++++++++++++++++-------- tests/realdebrid-web.test.ts | 24 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/main/realdebrid-web.ts b/src/main/realdebrid-web.ts index 7054e41..a78b26e 100644 --- a/src/main/realdebrid-web.ts +++ b/src/main/realdebrid-web.ts @@ -135,6 +135,8 @@ export class RealDebridWebFallback { private onClosed?: () => void; + private interactiveLoginDismissed = false; + private persistentPartition: string; private transientPartition: string; @@ -176,7 +178,8 @@ export class RealDebridWebFallback { public async openLoginWindow(): Promise { this.throwIfDisposed(); - const window = await this.ensureLoginWindow(); + this.interactiveLoginDismissed = false; + const window = await this.ensureLoginWindow(); if (window.isMinimized()) { window.restore(); } @@ -340,6 +343,7 @@ export class RealDebridWebFallback { let closingTokenProbe: Promise = Promise.resolve(); window.on("close", () => { if (!this.programmaticClosures.has(window)) { + this.interactiveLoginDismissed = true; closingTokenProbe = this.primeTokenFromWindow(window, windowGeneration); } }); @@ -372,6 +376,7 @@ export class RealDebridWebFallback { const changed = token !== this.cachedToken; this.cachedToken = token; this.cachedTokenAt = Date.now(); + this.interactiveLoginDismissed = false; if (changed && this.onAuthenticated) { void Promise.resolve().then(() => this.onAuthenticated?.()); } @@ -581,8 +586,11 @@ export class RealDebridWebFallback { throw new Error("Real-Debrid Web: Unrestrict fehlgeschlagen"); } - private async waitForLoginAndGenerate(link: string, signal?: AbortSignal): Promise { - const window = await this.ensureLoginWindow(); + private async waitForLoginAndGenerate(link: string, signal?: AbortSignal): Promise { + if (this.interactiveLoginDismissed) { + throw new Error("Real-Debrid Web-Login abgebrochen"); + } + const window = await this.ensureLoginWindow(); if (window.isMinimized()) { window.restore(); } @@ -596,11 +604,12 @@ export class RealDebridWebFallback { throw new Error("Real-Debrid Web-Login abgebrochen"); } - const outcome = await this.generate(link, signal); - if (outcome.kind === "success") { - if (!window.isDestroyed()) { - window.close(); - } + const outcome = await this.generate(link, signal); + if (outcome.kind === "success") { + if (!window.isDestroyed()) { + this.programmaticClosures.add(window); + window.close(); + } return outcome.value; } diff --git a/tests/realdebrid-web.test.ts b/tests/realdebrid-web.test.ts index 883be55..4895cc5 100644 --- a/tests/realdebrid-web.test.ts +++ b/tests/realdebrid-web.test.ts @@ -197,6 +197,30 @@ describe("realdebrid-web", () => { ); }); + it("does not reopen an automatically required login after the user closed it", async () => { + mockExecuteJavaScript.mockResolvedValue(""); + mockSessionFetch.mockImplementation(async () => new Response("login", { status: 200 })); + const fallback = new RealDebridWebFallback("persist:realdebrid-web-rdw_dismissed", () => true); + + await fallback.openLoginWindow(); + mockBrowserWindow.close(); + + await expect(fallback.unrestrict("https://rapidgator.net/file/first")).rejects.toThrow("abgebrochen"); + await expect(fallback.unrestrict("https://rapidgator.net/file/second")).rejects.toThrow("abgebrochen"); + expect(mockBrowserWindowCtor).toHaveBeenCalledTimes(1); + }); + + it("allows an explicitly requested login after the user closed the previous window", async () => { + mockExecuteJavaScript.mockResolvedValue(""); + const fallback = new RealDebridWebFallback("persist:realdebrid-web-rdw_reopen", () => true); + + await fallback.openLoginWindow(); + mockBrowserWindow.close(); + await fallback.openLoginWindow(); + + expect(mockBrowserWindowCtor).toHaveBeenCalledTimes(2); + }); + it("notifies the controller when a new browser token is detected", async () => { mockExecuteJavaScript.mockResolvedValue("new-browser-token"); const onAuthenticated = vi.fn();