real-debrid-downloader/src/shared/debrid-link-keys.ts
Sucukdeluxe e212ccc86f Add daily traffic limits, auto-sort packages, Debrid-Link multi-key improvements
Daily traffic limits:
- Per-provider daily download limit (configurable in GB per provider)
- Per Debrid-Link API key daily limit (individual limits per key)
- Usage tracking with automatic daily reset at midnight
- Provider is skipped when daily limit reached, falls back to next provider
- Reset button per provider and per Debrid-Link key in account settings
- Hoster routing skips daily-limited providers gracefully

Debrid-Link multi-key improvements:
- Keys now display with labels (#1, #2...) and masked tokens in account list
- Option to show detailed per-key view with individual usage stats
- Keys that hit their daily limit are automatically skipped
- providerAccountId/providerAccountLabel stored per download item

Auto-sort packages by progress:
- Active packages automatically sorted to top during downloads
- Sorted by completion ratio, then downloaded bytes
- Toggle in settings (autoSortPackagesByProgress)

UI polish:
- Package column headers: flatter, more transparent design
- LinkSnappy mode label: "Login" renamed to "Web"
- Account list: new toggle for detailed Debrid-Link key display
- Account usage stats section with warning styling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 02:29:48 +01:00

67 lines
1.7 KiB
TypeScript

export interface DebridLinkApiKeyEntry {
id: string;
token: string;
index: number;
label: string;
masked: string;
}
const FNV64_OFFSET_BASIS = 0xcbf29ce484222325n;
const FNV64_PRIME = 0x100000001b3n;
const FNV64_MASK = 0xffffffffffffffffn;
function fnv1a64(text: string): string {
let hash = FNV64_OFFSET_BASIS;
for (const char of text) {
hash ^= BigInt(char.codePointAt(0) || 0);
hash = (hash * FNV64_PRIME) & FNV64_MASK;
}
return hash.toString(36);
}
export function maskDebridLinkApiKey(token: string): string {
const trimmed = token.trim();
if (!trimmed) {
return "Nicht hinterlegt";
}
if (trimmed.length <= 6) {
return "*".repeat(trimmed.length);
}
return `${trimmed.slice(0, 3)}${"*".repeat(Math.max(4, trimmed.length - 6))}${trimmed.slice(-3)}`;
}
export function getDebridLinkApiKeyId(token: string): string {
return `dlk_${fnv1a64(token.trim())}`;
}
export function getDebridLinkApiKeyLabel(index: number): string {
return `Key ${index + 1}`;
}
export function parseDebridLinkApiKeys(raw: string): DebridLinkApiKeyEntry[] {
const seen = new Set<string>();
const tokens = String(raw || "")
.split(/[\n,]+/)
.map((entry) => entry.trim())
.filter(Boolean)
.filter((token) => {
if (seen.has(token)) {
return false;
}
seen.add(token);
return true;
});
return tokens.map((token, index) => ({
id: getDebridLinkApiKeyId(token),
token,
index,
label: getDebridLinkApiKeyLabel(index),
masked: maskDebridLinkApiKey(token)
}));
}
export function getDebridLinkApiKeyIds(raw: string): string[] {
return parseDebridLinkApiKeys(raw).map((entry) => entry.id);
}