feat: add Deepbrid, daily scheduling, and notification center

Add encrypted Deepbrid API accounts with account validation, provider routing, fallback, usage tracking, safe error handling, and verified 1Fichier downloads. Restore persistent recurring daily starts with local-calendar deduplication and legacy schedule compatibility. Add durable Discord package, run, remaining-volume, stall, and recovery notifications with privacy-safe telemetry and disk-failure recovery.
This commit is contained in:
Sucukdeluxe
2026-08-24 07:37:55 +02:00
parent 06e5bf4340
commit 1b7caba2eb
83 changed files with 12769 additions and 1098 deletions
+59 -4
View File
@@ -2,7 +2,8 @@ import type { AccountCheckScope, AppSettings, DebridAccountStatus, DebridProvide
import { getMegaDebridAccountsForMode, getMegaDebridDisabledAccountIdsForMode, parseMegaDebridAccounts, type MegaDebridAccountEntry } from "../shared/mega-debrid-accounts";
import { parseDebridLinkApiKeys, type DebridLinkApiKeyEntry } from "../shared/debrid-link-keys";
import { getRealDebridAccounts, type RealDebridAccountEntry } from "../shared/real-debrid-accounts";
import { logger } from "./logger";
import { DEEPBRID_ACCOUNT_ID, DeepbridApiError, DeepbridClient } from "./deepbrid";
import { logger } from "./logger";
import { compactErrorText } from "./utils";
const MEGA_DEBRID_API = "https://www.mega-debrid.eu/api.php";
@@ -175,6 +176,57 @@ export async function checkRealDebridAccount(
return { ...base, message: aborted ? "Prüfung abgebrochen" : `Prüfung fehlgeschlagen: ${errText}` };
}
}
export async function checkDeepbridAccount(
apiKey: string,
signal?: AbortSignal,
now = Date.now()
): Promise<DebridAccountStatus> {
const key = apiKey.trim();
const base: DebridAccountStatus = {
accountId: DEEPBRID_ACCOUNT_ID,
provider: "deepbrid",
label: "Deepbrid",
maskedLogin: maskSecret(key),
valid: false,
isPremium: false,
premiumUntilMs: null,
message: "",
checkedAt: now
};
if (!key) {
return { ...base, message: "Kein API-Key hinterlegt" };
}
try {
const user = await new DeepbridClient(key).getUser(signal);
const expiration = Date.parse(user.expiration);
const premiumUntilMs = Number.isFinite(expiration) ? expiration : null;
const isPremium = user.type.trim().toLowerCase() === "premium"
&& premiumUntilMs !== null
&& premiumUntilMs > now;
return {
...base,
valid: true,
isPremium,
premiumUntilMs,
username: user.username.trim() || undefined,
email: user.email.trim() || undefined,
message: isPremium
? formatRemaining(premiumUntilMs, now)
: user.type.trim().toLowerCase() === "premium" && premiumUntilMs !== null
? formatRemaining(premiumUntilMs, now)
: "Kein Premium (Free)"
};
} catch (error) {
if (signal?.aborted) {
return { ...base, message: "Prüfung abgebrochen" };
}
if (error instanceof DeepbridApiError && error.classification === "auth") {
return { ...base, message: error.status === 403 ? "Deepbrid API-Key gesperrt" : "Ungültiger Deepbrid API-Key" };
}
return { ...base, message: "Prüfung fehlgeschlagen" };
}
}
export async function checkMegaDebridAccount(
account: MegaDebridAccountEntry,
@@ -337,6 +389,8 @@ export async function checkAllDebridAccounts(
const realDebridAccounts = scope === "all"
? allRealDebridAccounts
: providerEnabled("realdebrid") ? allRealDebridAccounts.filter((account) => account.enabled) : [];
const deepbridApiKey = String(settings.deepbridApiKey || "").trim();
const checkDeepbrid = Boolean(deepbridApiKey) && (scope === "all" || providerEnabled("deepbrid"));
const taskFns: Array<() => Promise<DebridAccountStatus>> = [
...realDebridAccounts.map((account) => () => checkRealDebridAccount(
@@ -347,9 +401,10 @@ export async function checkAllDebridAccounts(
? (probeSignal) => probeRealDebridWebSession(account.id, probeSignal)
: undefined
)),
...megaAccounts.map((account) => () => checkMegaDebridAccount(account, signal, now)),
...debridLinkKeys.map((key) => () => checkDebridLinkKey(key, signal, now))
];
...megaAccounts.map((account) => () => checkMegaDebridAccount(account, signal, now)),
...debridLinkKeys.map((key) => () => checkDebridLinkKey(key, signal, now)),
...(checkDeepbrid ? [() => checkDeepbridAccount(deepbridApiKey, signal, now)] : [])
];
const results = await runWithConcurrency(taskFns, CHECK_CONCURRENCY);
logger.info(