From 438a9f209ed26509941d499461aaa0ad756b4452 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Fri, 6 Mar 2026 11:26:48 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(bestdebrid):=20add=20BestDebri?= =?UTF-8?q?d=20web-login=20provider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New BestDebrid web-login option (BrowserWindow + session.fetch) - Uses bestdebrid.com/api/v1/generateLink with browser session cookies - Login via BestDebrid website in embedded browser window - Toggle "BestDebrid per Web-Login statt API-Token verwenden" - Provider label shows "BestDebrid (Web)" or "BestDebrid (API)" - Session persistence respects "Token merken" setting Co-Authored-By: Claude Opus 4.6 --- src/main/app-controller.ts | 14 ++ src/main/bestdebrid-web.ts | 301 +++++++++++++++++++++++++++++++++++ src/main/constants.ts | 1 + src/main/debrid.ts | 20 ++- src/main/download-manager.ts | 6 +- src/main/main.ts | 4 + src/main/storage.ts | 2 + src/preload/preload.ts | 1 + src/renderer/App.tsx | 23 ++- src/shared/ipc.ts | 1 + src/shared/preload-api.ts | 1 + src/shared/types.ts | 1 + 12 files changed, 368 insertions(+), 7 deletions(-) create mode 100644 src/main/bestdebrid-web.ts diff --git a/src/main/app-controller.ts b/src/main/app-controller.ts index 33ffad4..ee57fec 100644 --- a/src/main/app-controller.ts +++ b/src/main/app-controller.ts @@ -23,6 +23,7 @@ import { fetchAllDebridHostInfo } from "./debrid"; import { parseCollectorInput } from "./link-parser"; import { configureLogger, getLogFilePath, logger } from "./logger"; import { AllDebridWebFallback } from "./all-debrid-web"; +import { BestDebridWebFallback } from "./bestdebrid-web"; import { RealDebridWebFallback } from "./realdebrid-web"; import { initSessionLog, getSessionLogPath, shutdownSessionLog } from "./session-log"; import { MegaWebFallback } from "./mega-web-fallback"; @@ -50,6 +51,8 @@ export class AppController { private allDebridWebFallback: AllDebridWebFallback; + private bestDebridWebFallback: BestDebridWebFallback; + private lastUpdateCheck: UpdateCheckResult | null = null; private lastUpdateCheckAt = 0; @@ -71,10 +74,12 @@ export class AppController { })); this.realDebridWebFallback = new RealDebridWebFallback(() => this.settings.rememberToken); this.allDebridWebFallback = new AllDebridWebFallback(() => this.settings.rememberToken); + this.bestDebridWebFallback = new BestDebridWebFallback(() => this.settings.rememberToken); this.manager = new DownloadManager(this.settings, session, this.storagePaths, { megaWebUnrestrict: (link: string, signal?: AbortSignal) => this.megaWebFallback.unrestrict(link, signal), allDebridWebUnrestrict: (link: string, signal?: AbortSignal) => this.allDebridWebFallback.unrestrict(link, signal), realDebridWebUnrestrict: (link: string, signal?: AbortSignal) => this.realDebridWebFallback.unrestrict(link, signal), + bestDebridWebUnrestrict: (link: string, signal?: AbortSignal) => this.bestDebridWebFallback.unrestrict(link, signal), invalidateMegaSession: () => this.megaWebFallback.invalidateSession(), onHistoryEntry: (entry: HistoryEntry) => { addHistoryEntry(this.storagePaths, entry); @@ -117,6 +122,7 @@ export class AppController { || settings.realDebridUseWebLogin || (settings.megaLogin.trim() && settings.megaPassword.trim()) || settings.bestToken.trim() + || settings.bestDebridUseWebLogin || settings.allDebridUseWebLogin || settings.allDebridToken.trim() || (settings.ddownloadLogin.trim() && settings.ddownloadPassword.trim()) @@ -180,6 +186,9 @@ export class AppController { void this.allDebridWebFallback.clearSessions().catch((error) => { logger.warn(`AllDebrid Web-Session konnte nicht gelöscht werden: ${String(error)}`); }); + void this.bestDebridWebFallback.clearSessions().catch((error) => { + logger.warn(`BestDebrid Web-Session konnte nicht gelöscht werden: ${String(error)}`); + }); } return this.settings; } @@ -192,6 +201,10 @@ export class AppController { await this.allDebridWebFallback.openLoginWindow(); } + public async openBestDebridLoginWindow(): Promise { + await this.bestDebridWebFallback.openLoginWindow(); + } + public async getAllDebridHostInfo(host = "rapidgator"): Promise { if (this.settings.allDebridUseWebLogin) { return this.allDebridWebFallback.getHostInfo(host); @@ -394,6 +407,7 @@ export class AppController { this.megaWebFallback.dispose(); this.realDebridWebFallback.dispose(); this.allDebridWebFallback.dispose(); + this.bestDebridWebFallback.dispose(); shutdownSessionLog(); logger.info("App beendet"); } diff --git a/src/main/bestdebrid-web.ts b/src/main/bestdebrid-web.ts new file mode 100644 index 0000000..29ba1c4 --- /dev/null +++ b/src/main/bestdebrid-web.ts @@ -0,0 +1,301 @@ +import { BrowserWindow, session } from "electron"; +import { UnrestrictedLink } from "./realdebrid"; +import { filenameFromUrl, sleep } from "./utils"; + +const BESTDEBRID_BASE_URL = "https://bestdebrid.com"; +const BESTDEBRID_LOGIN_URL = `${BESTDEBRID_BASE_URL}/en/downloader/`; +const BESTDEBRID_GENERATE_URL = `${BESTDEBRID_BASE_URL}/api/v1/generateLink`; +const BESTDEBRID_PERSISTENT_PARTITION = "persist:bestdebrid-web"; +const BESTDEBRID_TRANSIENT_PARTITION = "bestdebrid-web"; +const BESTDEBRID_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"; + +type GenerateOutcome = + | { kind: "success"; value: UnrestrictedLink } + | { kind: "login_required" }; + +function abortError(): Error { + return new Error("aborted:bestdebrid-web"); +} + +function withTimeoutSignal(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal { + const timeoutSignal = AbortSignal.timeout(timeoutMs); + if (!signal) { + return timeoutSignal; + } + return AbortSignal.any([signal, timeoutSignal]); +} + +function throwIfAborted(signal?: AbortSignal): void { + if (signal?.aborted) { + throw abortError(); + } +} + +async function sleepWithSignal(ms: number, signal?: AbortSignal): Promise { + if (!signal) { + await sleep(ms); + return; + } + if (signal.aborted) { + throw abortError(); + } + + await new Promise((resolve, reject) => { + let timer: NodeJS.Timeout | null = setTimeout(() => { + timer = null; + signal.removeEventListener("abort", onAbort); + resolve(); + }, Math.max(0, ms)); + + const onAbort = (): void => { + if (timer) { + clearTimeout(timer); + timer = null; + } + signal.removeEventListener("abort", onAbort); + reject(abortError()); + }; + + signal.addEventListener("abort", onAbort, { once: true }); + }); +} + +function parseJson(text: string): Record | null { + try { + const parsed = JSON.parse(text) as unknown; + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + return null; + } + return parsed as Record; + } catch { + return null; + } +} + +export class BestDebridWebFallback { + private queue: Promise = Promise.resolve(); + + private loginWindow: BrowserWindow | null = null; + + private loginWindowPartition = ""; + + private getRememberSession: () => boolean; + + public constructor(getRememberSession: () => boolean) { + this.getRememberSession = getRememberSession; + } + + public async unrestrict(link: string, signal?: AbortSignal): Promise { + const overallSignal = withTimeoutSignal(signal, 10 * 60 * 1000); + return this.runExclusive(async () => { + throwIfAborted(overallSignal); + if (!String(link || "").trim()) { + return null; + } + + const initial = await this.generate(link, overallSignal); + if (initial.kind === "success") { + return initial.value; + } + return this.waitForLoginAndGenerate(link, overallSignal); + }, overallSignal); + } + + public async openLoginWindow(): Promise { + const window = await this.ensureLoginWindow(); + if (window.isMinimized()) { + window.restore(); + } + window.show(); + window.focus(); + } + + public async clearSessions(): Promise { + this.disposeLoginWindow(); + for (const partition of [BESTDEBRID_PERSISTENT_PARTITION, BESTDEBRID_TRANSIENT_PARTITION]) { + const currentSession = session.fromPartition(partition); + try { + await currentSession.clearStorageData({ + storages: ["cookies", "indexdb", "localstorage", "serviceworkers", "cachestorage"] + }); + } catch { + // ignore + } + try { + await currentSession.clearCache(); + } catch { + // ignore + } + } + } + + public dispose(): void { + this.disposeLoginWindow(); + } + + private getPartition(): string { + return this.getRememberSession() ? BESTDEBRID_PERSISTENT_PARTITION : BESTDEBRID_TRANSIENT_PARTITION; + } + + private disposeLoginWindow(): void { + const current = this.loginWindow; + this.loginWindow = null; + this.loginWindowPartition = ""; + if (current && !current.isDestroyed()) { + current.close(); + } + } + + private async runExclusive(job: () => Promise, signal?: AbortSignal): Promise { + const queuedAt = Date.now(); + const queueWaitTimeoutMs = 90_000; + const guardedJob = async (): Promise => { + throwIfAborted(signal); + const waited = Date.now() - queuedAt; + if (waited > queueWaitTimeoutMs) { + throw new Error(`BestDebrid-Web Queue-Timeout (${Math.floor(waited / 1000)}s gewartet)`); + } + return job(); + }; + const run = this.queue.then(guardedJob, guardedJob); + this.queue = run.then(() => undefined, () => undefined); + return run; + } + + private async ensureLoginWindow(): Promise { + const partition = this.getPartition(); + const existing = this.loginWindow; + if (existing && !existing.isDestroyed() && this.loginWindowPartition === partition) { + return existing; + } + + if (existing && !existing.isDestroyed()) { + existing.close(); + } + + const window = new BrowserWindow({ + width: 1120, + height: 900, + minWidth: 980, + minHeight: 760, + autoHideMenuBar: true, + title: "BestDebrid Web-Login", + webPreferences: { + partition, + contextIsolation: true, + nodeIntegration: false + } + }); + window.setMenuBarVisibility(false); + window.on("closed", () => { + if (this.loginWindow === window) { + this.loginWindow = null; + this.loginWindowPartition = ""; + } + }); + this.loginWindow = window; + this.loginWindowPartition = partition; + await window.loadURL(BESTDEBRID_LOGIN_URL); + return window; + } + + private async generate(link: string, signal?: AbortSignal): Promise { + throwIfAborted(signal); + const currentSession = session.fromPartition(this.getPartition()); + const response = await currentSession.fetch(BESTDEBRID_GENERATE_URL, { + method: "POST", + headers: { + Accept: "application/json, text/javascript, */*; q=0.01", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + Origin: BESTDEBRID_BASE_URL, + Referer: BESTDEBRID_LOGIN_URL, + "User-Agent": BESTDEBRID_USER_AGENT, + "X-Requested-With": "XMLHttpRequest" + }, + body: new URLSearchParams({ link, pass: "", boxlinklist: "" }).toString(), + signal: withTimeoutSignal(signal, 30_000) + }); + + const text = await response.text(); + + // Not logged in — BestDebrid redirects or returns HTML login page + if (!response.ok || text.trim().startsWith(" = { B: 1, KB: 1024, MB: 1024 * 1024, GB: 1024 * 1024 * 1024, TB: 1024 * 1024 * 1024 * 1024 }; + fileSize = Math.floor(value * (multipliers[unit] || 1)); + } + } + + return { + kind: "success", + value: { + directUrl, + fileName, + fileSize, + retriesUsed: 0 + } + }; + } + + private async waitForLoginAndGenerate(link: string, signal?: AbortSignal): Promise { + const window = await this.ensureLoginWindow(); + if (window.isMinimized()) { + window.restore(); + } + window.show(); + window.focus(); + + const startedAt = Date.now(); + while (Date.now() - startedAt < 10 * 60 * 1000) { + throwIfAborted(signal); + if (window.isDestroyed()) { + throw new Error("BestDebrid Web-Login abgebrochen"); + } + + const outcome = await this.generate(link, signal); + if (outcome.kind === "success") { + if (!window.isDestroyed()) { + window.close(); + } + return outcome.value; + } + + await sleepWithSignal(1_500, signal); + } + + throw new Error("BestDebrid Web-Login Timeout"); + } +} diff --git a/src/main/constants.ts b/src/main/constants.ts index 378d92f..97d46de 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -46,6 +46,7 @@ export function defaultSettings(): AppSettings { megaPassword: "", megaDebridPreferApi: true, bestToken: "", + bestDebridUseWebLogin: false, allDebridToken: "", allDebridUseWebLogin: false, ddownloadLogin: "", diff --git a/src/main/debrid.ts b/src/main/debrid.ts index c598d1e..c84f5be 100644 --- a/src/main/debrid.ts +++ b/src/main/debrid.ts @@ -34,11 +34,13 @@ interface ProviderUnrestrictedLink extends UnrestrictedLink { export type MegaWebUnrestrictor = (link: string, signal?: AbortSignal) => Promise; export type AllDebridWebUnrestrictor = (link: string, signal?: AbortSignal) => Promise; export type RealDebridWebUnrestrictor = (link: string, signal?: AbortSignal) => Promise; +export type BestDebridWebUnrestrictor = (link: string, signal?: AbortSignal) => Promise; interface DebridServiceOptions { megaWebUnrestrict?: MegaWebUnrestrictor; allDebridWebUnrestrict?: AllDebridWebUnrestrictor; realDebridWebUnrestrict?: RealDebridWebUnrestrictor; + bestDebridWebUnrestrict?: BestDebridWebUnrestrictor; } function cloneSettings(settings: AppSettings): AppSettings { @@ -1585,6 +1587,10 @@ export class DebridService { return Boolean(settings.allDebridUseWebLogin && this.options.allDebridWebUnrestrict); } + private shouldUseBestDebridWeb(settings: AppSettings): boolean { + return Boolean(settings.bestDebridUseWebLogin && this.options.bestDebridWebUnrestrict); + } + public async unrestrictLink(link: string, signal?: AbortSignal, settingsSnapshot?: AppSettings): Promise { const settings = settingsSnapshot ? cloneSettings(settingsSnapshot) : cloneSettings(this.settings); @@ -1718,7 +1724,7 @@ export class DebridService { if (provider === "onefichier") { return Boolean(settings.oneFichierApiKey.trim()); } - return Boolean(settings.bestToken.trim()); + return Boolean(this.shouldUseBestDebridWeb(settings) || settings.bestToken.trim()); } private async unrestrictViaProvider(settings: AppSettings, provider: DebridProvider, link: string, signal?: AbortSignal): Promise { @@ -1757,6 +1763,16 @@ export class DebridService { if (provider === "onefichier") { return new OneFichierClient(settings.oneFichierApiKey).unrestrictLink(link, signal); } - return new BestDebridClient(settings.bestToken).unrestrictLink(link, signal); + if (this.shouldUseBestDebridWeb(settings) && this.options.bestDebridWebUnrestrict) { + const bdResult = await this.options.bestDebridWebUnrestrict(link, signal); + if (!bdResult) { + throw new Error("BestDebrid-Web-Fallback nicht verfügbar"); + } + bdResult.sourceLabel = "Web"; + return bdResult; + } + const bdResult = await new BestDebridClient(settings.bestToken).unrestrictLink(link, signal); + bdResult.sourceLabel = "API"; + return bdResult; } } diff --git a/src/main/download-manager.ts b/src/main/download-manager.ts index a6412cd..ecddc75 100644 --- a/src/main/download-manager.ts +++ b/src/main/download-manager.ts @@ -38,7 +38,7 @@ function releaseTlsSkip(): void { } } import { cleanupCancelledPackageArtifactsAsync, removeDownloadLinkArtifacts, removeSampleArtifacts } from "./cleanup"; -import { AllDebridWebUnrestrictor, DebridService, MegaWebUnrestrictor, RealDebridWebUnrestrictor, checkRapidgatorOnline } from "./debrid"; +import { AllDebridWebUnrestrictor, BestDebridWebUnrestrictor, DebridService, MegaWebUnrestrictor, RealDebridWebUnrestrictor, checkRapidgatorOnline } from "./debrid"; import { cleanupArchives, clearExtractResumeState, collectArchiveCleanupTargets, extractPackageArchives, findArchiveCandidates, hasAnyFilesRecursive, removeEmptyDirectoryTree } from "./extractor"; import { validateFileAgainstManifest } from "./integrity"; import { logger } from "./logger"; @@ -159,6 +159,7 @@ type DownloadManagerOptions = { megaWebUnrestrict?: MegaWebUnrestrictor; allDebridWebUnrestrict?: AllDebridWebUnrestrictor; realDebridWebUnrestrict?: RealDebridWebUnrestrictor; + bestDebridWebUnrestrict?: BestDebridWebUnrestrictor; invalidateMegaSession?: () => void; onHistoryEntry?: HistoryEntryCallback; }; @@ -953,7 +954,8 @@ export class DownloadManager extends EventEmitter { this.debridService = new DebridService(settings, { megaWebUnrestrict: options.megaWebUnrestrict, allDebridWebUnrestrict: options.allDebridWebUnrestrict, - realDebridWebUnrestrict: options.realDebridWebUnrestrict + realDebridWebUnrestrict: options.realDebridWebUnrestrict, + bestDebridWebUnrestrict: options.bestDebridWebUnrestrict }); this.invalidateMegaSessionFn = options.invalidateMegaSession; this.onHistoryEntryCallback = options.onHistoryEntry; diff --git a/src/main/main.ts b/src/main/main.ts index 45fe7de..694f9e0 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -454,6 +454,10 @@ function registerIpcHandlers(): void { await controller.openAllDebridLoginWindow(); }); + ipcMain.handle(IPC_CHANNELS.OPEN_BESTDEBRID_LOGIN, async () => { + await controller.openBestDebridLoginWindow(); + }); + ipcMain.handle(IPC_CHANNELS.GET_ALLDEBRID_HOST_INFO, async () => { return controller.getAllDebridHostInfo(); }); diff --git a/src/main/storage.ts b/src/main/storage.ts index 921e474..bafe6ef 100644 --- a/src/main/storage.ts +++ b/src/main/storage.ts @@ -112,6 +112,7 @@ export function normalizeSettings(settings: AppSettings): AppSettings { megaPassword: asText(settings.megaPassword), megaDebridPreferApi: settings.megaDebridPreferApi !== undefined ? Boolean(settings.megaDebridPreferApi) : true, bestToken: asText(settings.bestToken), + bestDebridUseWebLogin: Boolean(settings.bestDebridUseWebLogin), allDebridToken: asText(settings.allDebridToken), allDebridUseWebLogin: Boolean(settings.allDebridUseWebLogin), ddownloadLogin: asText(settings.ddownloadLogin), @@ -207,6 +208,7 @@ function sanitizeCredentialPersistence(settings: AppSettings): AppSettings { megaLogin: "", megaPassword: "", bestToken: "", + bestDebridUseWebLogin: settings.bestDebridUseWebLogin, allDebridToken: "", ddownloadLogin: "", ddownloadPassword: "", diff --git a/src/preload/preload.ts b/src/preload/preload.ts index 6429f9c..6a40051 100644 --- a/src/preload/preload.ts +++ b/src/preload/preload.ts @@ -54,6 +54,7 @@ const api: ElectronApi = { openSessionLog: (): Promise => ipcRenderer.invoke(IPC_CHANNELS.OPEN_SESSION_LOG), openRealDebridLogin: (): Promise => ipcRenderer.invoke(IPC_CHANNELS.OPEN_REALDEBRID_LOGIN), openAllDebridLogin: (): Promise => ipcRenderer.invoke(IPC_CHANNELS.OPEN_ALLDEBRID_LOGIN), + openBestDebridLogin: (): Promise => ipcRenderer.invoke(IPC_CHANNELS.OPEN_BESTDEBRID_LOGIN), getAllDebridHostInfo: (): Promise => ipcRenderer.invoke(IPC_CHANNELS.GET_ALLDEBRID_HOST_INFO), retryExtraction: (packageId: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.RETRY_EXTRACTION, packageId), extractNow: (packageId: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.EXTRACT_NOW, packageId), diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 24fd6be..db6acb8 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -63,7 +63,7 @@ const emptyStats = (): DownloadStats => ({ const emptySnapshot = (): UiSnapshot => ({ settings: { - token: "", realDebridUseWebLogin: false, megaLogin: "", megaPassword: "", megaDebridPreferApi: true, bestToken: "", allDebridToken: "", allDebridUseWebLogin: false, ddownloadLogin: "", ddownloadPassword: "", oneFichierApiKey: "", + token: "", realDebridUseWebLogin: false, megaLogin: "", megaPassword: "", megaDebridPreferApi: true, bestToken: "", bestDebridUseWebLogin: false, allDebridToken: "", allDebridUseWebLogin: false, ddownloadLogin: "", ddownloadPassword: "", oneFichierApiKey: "", archivePasswordList: "", rememberToken: true, providerPrimary: "realdebrid", providerSecondary: "megadebrid", providerTertiary: "bestdebrid", autoProviderFallback: true, outputDir: "", packageName: "", @@ -988,14 +988,14 @@ export function App(): ReactElement { if (settingsDraft.megaLogin.trim() && settingsDraft.megaPassword.trim()) { list.push("megadebrid"); } - if (settingsDraft.bestToken.trim()) { + if (settingsDraft.bestDebridUseWebLogin || settingsDraft.bestToken.trim()) { list.push("bestdebrid"); } if (settingsDraft.allDebridUseWebLogin || settingsDraft.allDebridToken.trim()) { list.push("alldebrid"); } return list; - }, [settingsDraft.token, settingsDraft.realDebridUseWebLogin, settingsDraft.megaLogin, settingsDraft.megaPassword, settingsDraft.bestToken, settingsDraft.allDebridToken, settingsDraft.allDebridUseWebLogin]); + }, [settingsDraft.token, settingsDraft.realDebridUseWebLogin, settingsDraft.megaLogin, settingsDraft.megaPassword, settingsDraft.bestToken, settingsDraft.bestDebridUseWebLogin, settingsDraft.allDebridToken, settingsDraft.allDebridUseWebLogin]); // DDownload is a direct file hoster (not a debrid service) and is used automatically // for ddownload.com/ddl.to URLs. It counts as a configured account but does not @@ -1156,6 +1156,16 @@ export function App(): ReactElement { }); }; + const onOpenBestDebridLogin = async (): Promise => { + await performQuickAction(async () => { + await persistDraftSettings(); + await window.rd.openBestDebridLogin(); + showToast("BestDebrid Login-Fenster geöffnet", 2200); + }, (error) => { + showToast(`BestDebrid Login fehlgeschlagen: ${String(error)}`, 2800); + }); + }; + const onCheckUpdates = async (): Promise => { let updateResult: UpdateCheckResult | null = null; await performQuickAction(async () => { @@ -2844,6 +2854,13 @@ export function App(): ReactElement { setText("bestToken", e.target.value)} /> + + {settingsDraft.bestDebridUseWebLogin && ( + <> +
Beim ersten Link oder über den Button unten öffnet sich ein BestDebrid-Browserfenster. Der Login läuft dort manuell über die Website.
+ + + )} setText("allDebridToken", e.target.value)} /> diff --git a/src/shared/ipc.ts b/src/shared/ipc.ts index 41433e5..0efc8eb 100644 --- a/src/shared/ipc.ts +++ b/src/shared/ipc.ts @@ -36,6 +36,7 @@ export const IPC_CHANNELS = { OPEN_SESSION_LOG: "app:open-session-log", OPEN_REALDEBRID_LOGIN: "app:open-realdebrid-login", OPEN_ALLDEBRID_LOGIN: "app:open-alldebrid-login", + OPEN_BESTDEBRID_LOGIN: "app:open-bestdebrid-login", GET_ALLDEBRID_HOST_INFO: "app:get-alldebrid-host-info", RETRY_EXTRACTION: "queue:retry-extraction", EXTRACT_NOW: "queue:extract-now", diff --git a/src/shared/preload-api.ts b/src/shared/preload-api.ts index bf42ba5..fc52af7 100644 --- a/src/shared/preload-api.ts +++ b/src/shared/preload-api.ts @@ -49,6 +49,7 @@ export interface ElectronApi { openSessionLog: () => Promise; openRealDebridLogin: () => Promise; openAllDebridLogin: () => Promise; + openBestDebridLogin: () => Promise; getAllDebridHostInfo: () => Promise; retryExtraction: (packageId: string) => Promise; extractNow: (packageId: string) => Promise; diff --git a/src/shared/types.ts b/src/shared/types.ts index e48e9a2..7ab31ed 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -43,6 +43,7 @@ export interface AppSettings { megaPassword: string; megaDebridPreferApi: boolean; bestToken: string; + bestDebridUseWebLogin: boolean; allDebridToken: string; allDebridUseWebLogin: boolean; ddownloadLogin: string;