diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 594bdb3..dfc8363 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,6 +1,6 @@ import { CSSProperties, DragEvent, KeyboardEvent as ReactKeyboardEvent, ReactElement, memo, useCallback, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { parseDebridLinkApiKeys } from "../shared/debrid-link-keys"; -import { parseMegaDebridAccounts } from "../shared/mega-debrid-accounts"; +import { parseMegaDebridAccounts, serializeMegaDebridAccounts, maskMegaDebridLogin } from "../shared/mega-debrid-accounts"; import type { AllDebridHostInfo, AppSettings, @@ -99,6 +99,11 @@ interface AccountOption { needsCredentials?: boolean; } +interface MegaDialogAccount { + login: string; + password: string; +} + interface AccountDialogState { mode: "create" | "edit"; kind: AccountKind | null; @@ -107,6 +112,9 @@ interface AccountDialogState { password: string; dailyLimitGb: string; keyDailyLimitGbById: Record; + megaAccounts: MegaDialogAccount[]; + megaNewLogin: string; + megaNewPassword: string; } interface DebridLinkAccountKeyEntry { @@ -576,6 +584,7 @@ function summarizeAccountLines(kind: AccountKind, settings: AppSettings): string } function createAccountDialogState(mode: "create" | "edit", kind: AccountKind | null, settings: AppSettings): AccountDialogState { + const baseMega: Pick = { megaAccounts: [], megaNewLogin: "", megaNewPassword: "" }; if (!kind) { return { mode, @@ -584,37 +593,40 @@ function createAccountDialogState(mode: "create" | "edit", kind: AccountKind | n login: "", password: "", dailyLimitGb: "", - keyDailyLimitGbById: {} + keyDailyLimitGbById: {}, + ...baseMega }; } const provider = getAccountServiceProvider(findAccountOption(kind).service); const dailyLimitGb = formatAccountDailyLimitInput(getProviderDailyLimitBytes(settings, provider)); switch (kind) { case "realdebrid-api": - return { mode, kind, token: settings.token, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: settings.token, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "realdebrid-web": - return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "megadebrid-api": case "megadebrid-web": { - // Populate token field with megaCredentials, or build from legacy megaLogin/megaPassword + // Populate megaAccounts from megaCredentials, or build from legacy megaLogin/megaPassword let megaToken = (settings.megaCredentials || "").trim(); if (!megaToken && settings.megaLogin.trim() && settings.megaPassword.trim()) { megaToken = `${settings.megaLogin.trim()}:${settings.megaPassword.trim()}`; } - return { mode, kind, token: megaToken, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + const parsed = parseMegaDebridAccounts(megaToken); + const megaAccounts = parsed.map((a) => ({ login: a.login, password: a.password })); + return { mode, kind, token: megaToken, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, megaAccounts, megaNewLogin: "", megaNewPassword: "" }; } case "bestdebrid-api": - return { mode, kind, token: settings.bestToken, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: settings.bestToken, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "bestdebrid-web": - return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "alldebrid-api": - return { mode, kind, token: settings.allDebridToken, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: settings.allDebridToken, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "alldebrid-web": - return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "ddownload-login": - return { mode, kind, token: "", login: settings.ddownloadLogin, password: settings.ddownloadPassword, dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: "", login: settings.ddownloadLogin, password: settings.ddownloadPassword, dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "onefichier-api": - return { mode, kind, token: settings.oneFichierApiKey, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: settings.oneFichierApiKey, login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; case "debridlink-api": return { mode, @@ -623,12 +635,13 @@ function createAccountDialogState(mode: "create" | "edit", kind: AccountKind | n login: "", password: "", dailyLimitGb, - keyDailyLimitGbById: buildDebridLinkKeyLimitInputs(settings.debridLinkApiKeys || "", undefined, settings) + keyDailyLimitGbById: buildDebridLinkKeyLimitInputs(settings.debridLinkApiKeys || "", undefined, settings), + ...baseMega }; case "linksnappy-login": - return { mode, kind, token: "", login: settings.linkSnappyLogin || "", password: settings.linkSnappyPassword || "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: "", login: settings.linkSnappyLogin || "", password: settings.linkSnappyPassword || "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; default: - return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {} }; + return { mode, kind, token: "", login: "", password: "", dailyLimitGb, keyDailyLimitGbById: {}, ...baseMega }; } } @@ -661,16 +674,18 @@ function applyAccountDialogToSettings(settings: AppSettings, dialog: AccountDial case "realdebrid-web": return { ...settings, token: "", realDebridUseWebLogin: true, providerDailyLimitBytes: nextProviderDailyLimitBytes }; case "megadebrid-api": { - const megaAccounts = parseMegaDebridAccounts(token); - const firstLogin = megaAccounts.length > 0 ? megaAccounts[0].login : ""; - const firstPassword = megaAccounts.length > 0 ? megaAccounts[0].password : ""; - return { ...settings, megaCredentials: token, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridApiEnabled: true, megaDebridPreferApi: true, providerDailyLimitBytes: nextProviderDailyLimitBytes }; + const megaSerialized = serializeMegaDebridAccounts(dialog.megaAccounts); + const megaParsed = parseMegaDebridAccounts(megaSerialized); + const firstLogin = megaParsed.length > 0 ? megaParsed[0].login : ""; + const firstPassword = megaParsed.length > 0 ? megaParsed[0].password : ""; + return { ...settings, megaCredentials: megaSerialized, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridApiEnabled: true, megaDebridPreferApi: true, providerDailyLimitBytes: nextProviderDailyLimitBytes }; } case "megadebrid-web": { - const megaAccounts = parseMegaDebridAccounts(token); - const firstLogin = megaAccounts.length > 0 ? megaAccounts[0].login : ""; - const firstPassword = megaAccounts.length > 0 ? megaAccounts[0].password : ""; - return { ...settings, megaCredentials: token, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridWebEnabled: true, megaDebridPreferApi: false, providerDailyLimitBytes: nextProviderDailyLimitBytes }; + const megaSerialized = serializeMegaDebridAccounts(dialog.megaAccounts); + const megaParsed = parseMegaDebridAccounts(megaSerialized); + const firstLogin = megaParsed.length > 0 ? megaParsed[0].login : ""; + const firstPassword = megaParsed.length > 0 ? megaParsed[0].password : ""; + return { ...settings, megaCredentials: megaSerialized, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridWebEnabled: true, megaDebridPreferApi: false, providerDailyLimitBytes: nextProviderDailyLimitBytes }; } case "bestdebrid-api": return { ...settings, bestToken: token, bestDebridUseWebLogin: false, providerDailyLimitBytes: nextProviderDailyLimitBytes }; @@ -752,13 +767,12 @@ function validateAccountDialog(dialog: AccountDialogState): string | null { return "Bitte zuerst einen Account-Typ auswählen."; } const option = findAccountOption(dialog.kind); - if (option.needsToken && !dialog.token.trim()) { - return `${option.title}: Bitte Zugangstoken eintragen.`; - } - if ((dialog.kind === "megadebrid-api" || dialog.kind === "megadebrid-web") && dialog.token.trim()) { - if (parseMegaDebridAccounts(dialog.token).length === 0) { - return `${option.title}: Mindestens ein gültiges Login:Passwort-Paar eintragen (Format: login:passwort, pro Zeile).`; + if (dialog.kind === "megadebrid-api" || dialog.kind === "megadebrid-web") { + if (dialog.megaAccounts.length === 0) { + return `${option.title}: Mindestens einen Account hinzufügen.`; } + } else if (option.needsToken && !dialog.token.trim()) { + return `${option.title}: Bitte Zugangstoken eintragen.`; } if (option.needsCredentials) { if (!dialog.login.trim()) { @@ -2477,6 +2491,11 @@ export function App(): ReactElement { next.login = prev.login; next.password = prev.password; } + if (kind === "megadebrid-api" || kind === "megadebrid-web") { + next.megaAccounts = prev.megaAccounts; + next.megaNewLogin = prev.megaNewLogin; + next.megaNewPassword = prev.megaNewPassword; + } next.dailyLimitGb = prev.dailyLimitGb; return next; }); @@ -5410,9 +5429,69 @@ export function App(): ReactElement {
- {accountDialogOption.needsToken && ( + {(accountDialogOption.service === "megadebrid-api" || accountDialogOption.service === "megadebrid-web") && (
- + +
+
+ setAccountDialog((prev) => prev ? { ...prev, megaNewLogin: event.target.value } : prev)} + /> +
+
+ setAccountDialog((prev) => prev ? { ...prev, megaNewPassword: event.target.value } : prev)} + /> +
+
+ + {accountDialog.megaAccounts.length > 0 && ( +
+ +
+ {accountDialog.megaAccounts.map((account, index) => ( +
+
+ Account {index + 1} + {maskMegaDebridLogin(account.login)} +
+ +
+ ))} +
+
+ )} +
+ )} + {accountDialogOption.needsToken && accountDialogOption.service !== "megadebrid-api" && accountDialogOption.service !== "megadebrid-web" && ( +
+ {accountDialogOption.service === "debridlink" ? (