Feature: Mega-Debrid-Account beim Hinzufuegen sofort pruefen (Gueltigkeit + Premium)
Beim "Hinzufuegen" eines Mega-Debrid-Accounts im Bearbeiten-Dialog wird der Account jetzt sofort einzeln geprueft (connectUser) — Login-Gueltigkeit + Premium-Restlaufzeit erscheinen direkt als Badge, ohne den Tab schliessen und "Alle pruefen" klicken zu muessen. Waehrend der Pruefung zeigt das Badge "Pruefe…". Neue Einzel-Check-IPC (Spiegel von checkDebridAccounts): CHECK_MEGA_DEBRID_ACCOUNT -> app-controller.checkSingleMegaDebridAccount(login, password) baut den Account-Entry (id via getMegaDebridAccountId), ruft die bestehende checkMegaDebridAccount() und merged das Ergebnis via applyDebridAccountStatuses -> Snapshot -> Badge (gleicher Pfad wie "Alle pruefen"). Funktioniert fuer den noch nicht gespeicherten Draft-Account, weil applyDebridAccountStatuses merged (kein Pruning) + emitState. Kern-Check-Logik unveraendert + weiterhin durch account-check.test.ts gedeckt. 643 Tests gruen, tsc 9 (unveraendert), Build sauber. GUI compile-/build-verifiziert, im laufenden Electron noch nicht click-getestet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4a883fb93f
commit
efb5696c13
@ -24,7 +24,8 @@ import { importDlcContainers } from "./container";
|
|||||||
import { APP_VERSION } from "./constants";
|
import { APP_VERSION } from "./constants";
|
||||||
import { DownloadManager } from "./download-manager";
|
import { DownloadManager } from "./download-manager";
|
||||||
import { fetchAllDebridHostInfo, fetchDebridLinkHostLimits } from "./debrid";
|
import { fetchAllDebridHostInfo, fetchDebridLinkHostLimits } from "./debrid";
|
||||||
import { checkAllDebridAccounts } from "./account-check";
|
import { checkAllDebridAccounts, checkMegaDebridAccount } from "./account-check";
|
||||||
|
import { parseMegaDebridAccounts } from "../shared/mega-debrid-accounts";
|
||||||
import { parseCollectorInput } from "./link-parser";
|
import { parseCollectorInput } from "./link-parser";
|
||||||
import { configureLogger, getLogFilePath, logger } from "./logger";
|
import { configureLogger, getLogFilePath, logger } from "./logger";
|
||||||
import { AllDebridWebFallback } from "./all-debrid-web";
|
import { AllDebridWebFallback } from "./all-debrid-web";
|
||||||
@ -389,6 +390,20 @@ public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
|
|||||||
});
|
});
|
||||||
return statuses;
|
return statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check a SINGLE Mega-Debrid account by raw credentials (used when an account
|
||||||
|
* is added in the dialog, before it is saved) and merge the result so the
|
||||||
|
* validity/premium badge updates immediately — without a full "Alle pruefen". */
|
||||||
|
public async checkSingleMegaDebridAccount(login: string, password: string): Promise<DebridAccountStatus | null> {
|
||||||
|
const entry = parseMegaDebridAccounts(`${login.trim()}:${password.trim()}`)[0];
|
||||||
|
if (!entry) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const status = await checkMegaDebridAccount(entry);
|
||||||
|
this.manager.applyDebridAccountStatuses([status]);
|
||||||
|
this.audit("INFO", "Mega-Debrid-Account einzeln geprueft", { valid: status.valid, premium: status.isPremium });
|
||||||
|
return status;
|
||||||
|
}
|
||||||
public async checkUpdates(): Promise<UpdateCheckResult> {
|
public async checkUpdates(): Promise<UpdateCheckResult> {
|
||||||
const result = await checkGitHubUpdate(this.settings.updateRepo);
|
const result = await checkGitHubUpdate(this.settings.updateRepo);
|
||||||
if (!result.error) {
|
if (!result.error) {
|
||||||
|
|||||||
@ -648,6 +648,10 @@ function registerIpcHandlers(): void {
|
|||||||
return controller.checkDebridAccounts();
|
return controller.checkDebridAccounts();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle(IPC_CHANNELS.CHECK_MEGA_DEBRID_ACCOUNT, async (_event, login: string, password: string) => {
|
||||||
|
return controller.checkSingleMegaDebridAccount(String(login || ""), String(password || ""));
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle(IPC_CHANNELS.IMPORT_BACKUP, async () => {
|
ipcMain.handle(IPC_CHANNELS.IMPORT_BACKUP, async () => {
|
||||||
const options = {
|
const options = {
|
||||||
properties: ["openFile"] as Array<"openFile">,
|
properties: ["openFile"] as Array<"openFile">,
|
||||||
|
|||||||
@ -77,6 +77,7 @@ const api: ElectronApi = {
|
|||||||
getAllDebridHostInfo: (): Promise<AllDebridHostInfo> => ipcRenderer.invoke(IPC_CHANNELS.GET_ALLDEBRID_HOST_INFO),
|
getAllDebridHostInfo: (): Promise<AllDebridHostInfo> => ipcRenderer.invoke(IPC_CHANNELS.GET_ALLDEBRID_HOST_INFO),
|
||||||
getDebridLinkHostLimits: (): Promise<DebridLinkHostLimitInfo[]> => ipcRenderer.invoke(IPC_CHANNELS.GET_DEBRIDLINK_HOST_LIMITS),
|
getDebridLinkHostLimits: (): Promise<DebridLinkHostLimitInfo[]> => ipcRenderer.invoke(IPC_CHANNELS.GET_DEBRIDLINK_HOST_LIMITS),
|
||||||
checkDebridAccounts: (): Promise<DebridAccountStatus[]> => ipcRenderer.invoke(IPC_CHANNELS.CHECK_DEBRID_ACCOUNTS),
|
checkDebridAccounts: (): Promise<DebridAccountStatus[]> => ipcRenderer.invoke(IPC_CHANNELS.CHECK_DEBRID_ACCOUNTS),
|
||||||
|
checkMegaDebridAccount: (login: string, password: string): Promise<DebridAccountStatus | null> => ipcRenderer.invoke(IPC_CHANNELS.CHECK_MEGA_DEBRID_ACCOUNT, login, password),
|
||||||
retryExtraction: (packageId: string): Promise<void> => ipcRenderer.invoke(IPC_CHANNELS.RETRY_EXTRACTION, packageId),
|
retryExtraction: (packageId: string): Promise<void> => ipcRenderer.invoke(IPC_CHANNELS.RETRY_EXTRACTION, packageId),
|
||||||
extractNow: (packageId: string): Promise<void> => ipcRenderer.invoke(IPC_CHANNELS.EXTRACT_NOW, packageId),
|
extractNow: (packageId: string): Promise<void> => ipcRenderer.invoke(IPC_CHANNELS.EXTRACT_NOW, packageId),
|
||||||
resetPackage: (packageId: string): Promise<void> => ipcRenderer.invoke(IPC_CHANNELS.RESET_PACKAGE, packageId),
|
resetPackage: (packageId: string): Promise<void> => ipcRenderer.invoke(IPC_CHANNELS.RESET_PACKAGE, packageId),
|
||||||
|
|||||||
@ -1611,6 +1611,8 @@ export function App(): ReactElement {
|
|||||||
const [showAllPackages, setShowAllPackages] = useState(false);
|
const [showAllPackages, setShowAllPackages] = useState(false);
|
||||||
const [actionBusy, setActionBusy] = useState(false);
|
const [actionBusy, setActionBusy] = useState(false);
|
||||||
const [accountCheckBusy, setAccountCheckBusy] = useState(false);
|
const [accountCheckBusy, setAccountCheckBusy] = useState(false);
|
||||||
|
// Account-IDs, die gerade beim Hinzufügen einzeln geprüft werden (Mega-Debrid).
|
||||||
|
const [megaCheckingIds, setMegaCheckingIds] = useState<Set<string>>(() => new Set());
|
||||||
const actionBusyRef = useRef(false);
|
const actionBusyRef = useRef(false);
|
||||||
const actionUnlockTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const actionUnlockTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
const mountedRef = useRef(true);
|
const mountedRef = useRef(true);
|
||||||
@ -2663,6 +2665,24 @@ export function App(): ReactElement {
|
|||||||
}
|
}
|
||||||
}, [showToast]);
|
}, [showToast]);
|
||||||
|
|
||||||
|
const runMegaAccountCheck = useCallback(async (login: string, password: string): Promise<void> => {
|
||||||
|
const trimmedLogin = login.trim();
|
||||||
|
const trimmedPassword = password.trim();
|
||||||
|
if (!trimmedLogin || !trimmedPassword) return;
|
||||||
|
const accId = getMegaDebridAccountId(trimmedLogin);
|
||||||
|
setMegaCheckingIds((prev) => { const next = new Set(prev); next.add(accId); return next; });
|
||||||
|
try {
|
||||||
|
const status = await window.rd.checkMegaDebridAccount(trimmedLogin, trimmedPassword);
|
||||||
|
if (status) {
|
||||||
|
showToast(status.valid ? `Account geprüft — ${status.message}` : `Account ungültig — ${status.message}`, 3200);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showToast(`Account-Check fehlgeschlagen: ${String(error)}`, 3200);
|
||||||
|
} finally {
|
||||||
|
setMegaCheckingIds((prev) => { const next = new Set(prev); next.delete(accId); return next; });
|
||||||
|
}
|
||||||
|
}, [showToast]);
|
||||||
|
|
||||||
const openCreateAccountDialog = (): void => {
|
const openCreateAccountDialog = (): void => {
|
||||||
setAccountDialogSearch("");
|
setAccountDialogSearch("");
|
||||||
setAccountDialog(createAccountDialogState("create", null, settingsDraft));
|
setAccountDialog(createAccountDialogState("create", null, settingsDraft));
|
||||||
@ -5677,13 +5697,23 @@ export function App(): ReactElement {
|
|||||||
<button
|
<button
|
||||||
className="btn"
|
className="btn"
|
||||||
disabled={!accountDialog.megaNewLogin.trim() || !accountDialog.megaNewPassword.trim()}
|
disabled={!accountDialog.megaNewLogin.trim() || !accountDialog.megaNewPassword.trim()}
|
||||||
onClick={() => setAccountDialog((prev) => {
|
onClick={() => {
|
||||||
if (!prev || !prev.megaNewLogin.trim() || !prev.megaNewPassword.trim()) return prev;
|
const login = accountDialog.megaNewLogin.trim();
|
||||||
const exists = prev.megaAccounts.some((a) => a.login.trim().toLowerCase() === prev.megaNewLogin.trim().toLowerCase());
|
const password = accountDialog.megaNewPassword.trim();
|
||||||
if (exists) return prev;
|
if (!login || !password) return;
|
||||||
const nextAccounts = [...prev.megaAccounts, { login: prev.megaNewLogin.trim(), password: prev.megaNewPassword.trim() }];
|
const exists = accountDialog.megaAccounts.some((a) => a.login.trim().toLowerCase() === login.toLowerCase());
|
||||||
return { ...prev, megaAccounts: nextAccounts, megaNewLogin: "", megaNewPassword: "", token: serializeMegaDebridAccounts(nextAccounts) };
|
setAccountDialog((prev) => {
|
||||||
})}
|
if (!prev || !prev.megaNewLogin.trim() || !prev.megaNewPassword.trim()) return prev;
|
||||||
|
if (prev.megaAccounts.some((a) => a.login.trim().toLowerCase() === login.toLowerCase())) return prev;
|
||||||
|
const nextAccounts = [...prev.megaAccounts, { login, password }];
|
||||||
|
return { ...prev, megaAccounts: nextAccounts, megaNewLogin: "", megaNewPassword: "", token: serializeMegaDebridAccounts(nextAccounts) };
|
||||||
|
});
|
||||||
|
// Sofort beim Anlegen pruefen (Gueltigkeit + Premium-Restlaufzeit) —
|
||||||
|
// Badge aktualisiert sich via Snapshot, ohne Tab schliessen / "Alle pruefen".
|
||||||
|
if (!exists) {
|
||||||
|
void runMegaAccountCheck(login, password);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Hinzufügen
|
Hinzufügen
|
||||||
</button>
|
</button>
|
||||||
@ -5700,7 +5730,9 @@ export function App(): ReactElement {
|
|||||||
<strong>Account {index + 1}</strong>
|
<strong>Account {index + 1}</strong>
|
||||||
<span>{maskMegaDebridLogin(account.login)}</span>
|
<span>{maskMegaDebridLogin(account.login)}</span>
|
||||||
{accDisabled && <span className="account-validity-badge invalid" title="Dieser Account wird beim Download übersprungen, bleibt aber gespeichert.">Deaktiviert</span>}
|
{accDisabled && <span className="account-validity-badge invalid" title="Dieser Account wird beim Download übersprungen, bleibt aber gespeichert.">Deaktiviert</span>}
|
||||||
{(() => {
|
{megaCheckingIds.has(accId)
|
||||||
|
? <span className="account-validity-badge unknown" title="Account wird gerade geprüft…">Prüfe…</span>
|
||||||
|
: (() => {
|
||||||
const st = snapshot?.settings?.debridAccountStatuses?.[accId];
|
const st = snapshot?.settings?.debridAccountStatuses?.[accId];
|
||||||
if (!st) return <span className="account-validity-badge unknown" title="Noch nicht geprüft – auf „Alle prüfen“ klicken">Noch nicht geprüft</span>;
|
if (!st) return <span className="account-validity-badge unknown" title="Noch nicht geprüft – auf „Alle prüfen“ klicken">Noch nicht geprüft</span>;
|
||||||
const checkedAgo = formatCheckedAgo(st.checkedAt);
|
const checkedAgo = formatCheckedAgo(st.checkedAt);
|
||||||
|
|||||||
@ -56,6 +56,7 @@ export const IPC_CHANNELS = {
|
|||||||
GET_ALLDEBRID_HOST_INFO: "app:get-alldebrid-host-info",
|
GET_ALLDEBRID_HOST_INFO: "app:get-alldebrid-host-info",
|
||||||
GET_DEBRIDLINK_HOST_LIMITS: "app:get-debridlink-host-limits",
|
GET_DEBRIDLINK_HOST_LIMITS: "app:get-debridlink-host-limits",
|
||||||
CHECK_DEBRID_ACCOUNTS: "app:check-debrid-accounts",
|
CHECK_DEBRID_ACCOUNTS: "app:check-debrid-accounts",
|
||||||
|
CHECK_MEGA_DEBRID_ACCOUNT: "app:check-mega-debrid-account",
|
||||||
RETRY_EXTRACTION: "queue:retry-extraction",
|
RETRY_EXTRACTION: "queue:retry-extraction",
|
||||||
EXTRACT_NOW: "queue:extract-now",
|
EXTRACT_NOW: "queue:extract-now",
|
||||||
RESET_PACKAGE: "queue:reset-package",
|
RESET_PACKAGE: "queue:reset-package",
|
||||||
|
|||||||
@ -74,6 +74,7 @@ export interface ElectronApi {
|
|||||||
getAllDebridHostInfo: () => Promise<AllDebridHostInfo>;
|
getAllDebridHostInfo: () => Promise<AllDebridHostInfo>;
|
||||||
getDebridLinkHostLimits: () => Promise<DebridLinkHostLimitInfo[]>;
|
getDebridLinkHostLimits: () => Promise<DebridLinkHostLimitInfo[]>;
|
||||||
checkDebridAccounts: () => Promise<DebridAccountStatus[]>;
|
checkDebridAccounts: () => Promise<DebridAccountStatus[]>;
|
||||||
|
checkMegaDebridAccount: (login: string, password: string) => Promise<DebridAccountStatus | null>;
|
||||||
retryExtraction: (packageId: string) => Promise<void>;
|
retryExtraction: (packageId: string) => Promise<void>;
|
||||||
extractNow: (packageId: string) => Promise<void>;
|
extractNow: (packageId: string) => Promise<void>;
|
||||||
resetPackage: (packageId: string) => Promise<void>;
|
resetPackage: (packageId: string) => Promise<void>;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user