Add Mega-Debrid multi-account support with automatic fallback

Multiple Mega-Debrid accounts can now be configured as login:password
pairs (one per line). When an account hits Fair-Use limits or errors,
the next account is tried automatically.

- New parser module mega-debrid-accounts.ts (parse, ID generation,
  masking, serialization)
- Per-account daily limits, usage tracking, enable/disable
- Account rotation with per-mode cooldowns (API failures don't
  block Web attempts)
- Backward compatible: existing single megaLogin/megaPassword
  is auto-migrated to the new format
- UI: textarea for credentials, account list with masked logins

Follows the existing Debrid-Link multi-key pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe
2026-03-23 20:12:51 +01:00
co-authored by Claude Opus 4.6
parent 672c74f98f
commit 6df0834b67
10 changed files with 3614 additions and 3001 deletions
+32 -27
View File
@@ -21,8 +21,8 @@ export const WRITE_BUFFER_SIZE = 512 * 1024; // 512 KB write buffer (JDown
export const WRITE_FLUSH_TIMEOUT_MS = 2000; // 2s flush timeout
export const ALLOCATION_UNIT_SIZE = 4096; // 4 KB NTFS alignment
export const STREAM_HIGH_WATER_MARK = 512 * 1024; // 512 KB stream buffer — lower than before (2 MB) so backpressure triggers sooner when disk is slow
export const DISK_BUSY_THRESHOLD_MS = 300; // Internal detection threshold for disk backpressure
export const DISK_BUSY_STATUS_THRESHOLD_MS = 500; // Delay UI/log display for brief disk-write spikes
export const DISK_BUSY_THRESHOLD_MS = 300; // Internal detection threshold for disk backpressure
export const DISK_BUSY_STATUS_THRESHOLD_MS = 500; // Delay UI/log display for brief disk-write spikes
export const SAMPLE_DIR_NAMES = new Set(["sample", "samples"]);
export const SAMPLE_VIDEO_EXTENSIONS = new Set([".mkv", ".mp4", ".avi", ".mov", ".wmv", ".m4v", ".ts", ".m2ts", ".webm"]);
@@ -46,6 +46,7 @@ export function defaultSettings(): AppSettings {
realDebridUseWebLogin: false,
megaLogin: "",
megaPassword: "",
megaCredentials: "",
megaDebridApiEnabled: false,
megaDebridWebEnabled: false,
megaDebridPreferApi: true,
@@ -93,32 +94,36 @@ export function defaultSettings(): AppSettings {
speedLimitMode: "global",
updateRepo: DEFAULT_UPDATE_REPO,
autoUpdateCheck: true,
clipboardWatch: false,
minimizeToTray: false,
theme: "dark" as const,
collapseNewPackages: true,
historyRetentionMode: "permanent",
accountListShowDetailedDebridLinkKeys: false,
autoSortPackagesByProgress: true,
autoSkipExtracted: false,
hideExtractedItems: true,
confirmDeleteSelection: true,
totalDownloadedAllTime: 0,
totalCompletedFilesAllTime: 0,
totalRuntimeAllTimeMs: 0,
bandwidthSchedules: [],
clipboardWatch: false,
minimizeToTray: false,
theme: "dark" as const,
collapseNewPackages: true,
historyRetentionMode: "permanent",
accountListShowDetailedDebridLinkKeys: false,
autoSortPackagesByProgress: true,
autoSkipExtracted: false,
hideExtractedItems: true,
confirmDeleteSelection: true,
totalDownloadedAllTime: 0,
totalCompletedFilesAllTime: 0,
totalRuntimeAllTimeMs: 0,
bandwidthSchedules: [],
columnOrder: ["name", "size", "progress", "hoster", "account", "prio", "status", "speed"],
extractCpuPriority: "high",
autoExtractWhenStopped: true,
disabledProviders: [],
hosterRouting: {},
providerDailyLimitBytes: {},
providerDailyUsageBytes: {},
providerTotalUsageBytes: {},
debridLinkApiKeyDailyLimitBytes: {},
debridLinkApiKeyDailyUsageBytes: {},
debridLinkApiKeyTotalUsageBytes: {},
providerDailyUsageDay: getProviderUsageDayKey(),
scheduledStartEpochMs: 0
};
}
hosterRouting: {},
providerDailyLimitBytes: {},
providerDailyUsageBytes: {},
providerTotalUsageBytes: {},
debridLinkApiKeyDailyLimitBytes: {},
debridLinkApiKeyDailyUsageBytes: {},
debridLinkApiKeyTotalUsageBytes: {},
megaDebridDisabledAccountIds: [],
megaDebridAccountDailyLimitBytes: {},
megaDebridAccountDailyUsageBytes: {},
megaDebridAccountTotalUsageBytes: {},
providerDailyUsageDay: getProviderUsageDayKey(),
scheduledStartEpochMs: 0
};
}
+2502 -2240
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -2,6 +2,7 @@ import fs from "node:fs";
import fsp from "node:fs/promises";
import path from "node:path";
import { getDebridLinkApiKeyIds } from "../shared/debrid-link-keys";
import { getMegaDebridAccountIds } from "../shared/mega-debrid-accounts";
import { AppSettings, BandwidthScheduleEntry, DebridFallbackProvider, DebridProvider, DownloadItem, DownloadStatus, HistoryEntry, HistoryRetentionMode, PackageEntry, PackagePriority, SessionState } from "../shared/types";
import { getProviderUsageDayKey } from "../shared/provider-daily-limits";
import { defaultSettings } from "./constants";
@@ -281,6 +282,12 @@ export function normalizeSettings(settings: AppSettings): AppSettings {
const currentUsageDay = getProviderUsageDayKey();
const megaLogin = asText(settings.megaLogin);
const megaPassword = asText(settings.megaPassword);
// Migrate legacy single-account to multi-account format
let megaCredentials = String(settings.megaCredentials ?? "").replace(/\r\n|\r/g, "\n").trim();
if (!megaCredentials && megaLogin && megaPassword) {
megaCredentials = `${megaLogin}:${megaPassword}`;
}
const megaDebridAccountIds = getMegaDebridAccountIds(megaCredentials);
const megaDebridPreferApi = settings.megaDebridPreferApi !== undefined ? Boolean(settings.megaDebridPreferApi) : true;
const hasMegaCreds = Boolean(megaLogin && megaPassword);
const megaDebridApiEnabled = settings.megaDebridApiEnabled !== undefined
@@ -322,6 +329,7 @@ export function normalizeSettings(settings: AppSettings): AppSettings {
realDebridUseWebLogin: Boolean(settings.realDebridUseWebLogin),
megaLogin,
megaPassword,
megaCredentials,
megaDebridApiEnabled,
megaDebridWebEnabled,
megaDebridPreferApi,
@@ -406,6 +414,12 @@ export function normalizeSettings(settings: AppSettings): AppSettings {
debridLinkApiKeyDailyLimitBytes,
debridLinkApiKeyDailyUsageBytes: providerDailyUsageDay === currentUsageDay ? debridLinkApiKeyDailyUsageBytes : {},
debridLinkApiKeyTotalUsageBytes,
megaDebridDisabledAccountIds: normalizeStringList(settings.megaDebridDisabledAccountIds, megaDebridAccountIds),
megaDebridAccountDailyLimitBytes: normalizeNamedByteMap(settings.megaDebridAccountDailyLimitBytes, megaDebridAccountIds),
megaDebridAccountDailyUsageBytes: providerDailyUsageDay === currentUsageDay
? normalizeNamedByteMap(settings.megaDebridAccountDailyUsageBytes, megaDebridAccountIds)
: {},
megaDebridAccountTotalUsageBytes: normalizeNamedByteMap(settings.megaDebridAccountTotalUsageBytes, megaDebridAccountIds),
providerDailyUsageDay: providerDailyUsageDay === currentUsageDay ? providerDailyUsageDay : currentUsageDay,
scheduledStartEpochMs: clampNumber(settings.scheduledStartEpochMs, defaults.scheduledStartEpochMs, 0, Number.MAX_SAFE_INTEGER)
};
@@ -454,6 +468,7 @@ function sanitizeCredentialPersistence(settings: AppSettings): AppSettings {
realDebridUseWebLogin: settings.realDebridUseWebLogin,
megaLogin: "",
megaPassword: "",
megaCredentials: "",
bestToken: "",
bestDebridUseWebLogin: settings.bestDebridUseWebLogin,
allDebridToken: "",