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>
This commit is contained in:
Sucukdeluxe
2026-03-07 02:29:48 +01:00
co-authored by Claude Opus 4.6
parent 71b3612e82
commit e212ccc86f
20 changed files with 1149 additions and 54 deletions
+32
View File
@@ -1,9 +1,11 @@
import path from "node:path";
import { app } from "electron";
import { getDebridLinkApiKeyIds } from "../shared/debrid-link-keys";
import {
AddLinksPayload,
AllDebridHostInfo,
AppSettings,
DebridProvider,
DuplicatePolicy,
HistoryEntry,
PackagePriority,
@@ -16,6 +18,7 @@ import {
UpdateInstallProgress,
UpdateInstallResult
} from "../shared/types";
import { resetDebridLinkApiKeyDailyUsage, resetProviderDailyUsage } from "../shared/provider-daily-limits";
import { importDlcContainers } from "./container";
import { APP_VERSION } from "./constants";
import { DownloadManager } from "./download-manager";
@@ -176,6 +179,11 @@ export class AppController {
// Preserve the live totalDownloadedAllTime from the download manager
const liveSettings = this.manager.getSettings();
nextSettings.totalDownloadedAllTime = Math.max(nextSettings.totalDownloadedAllTime || 0, liveSettings.totalDownloadedAllTime || 0);
nextSettings.providerDailyUsageDay = liveSettings.providerDailyUsageDay;
nextSettings.providerDailyUsageBytes = { ...(liveSettings.providerDailyUsageBytes || {}) };
nextSettings.debridLinkApiKeyDailyUsageBytes = Object.fromEntries(
Object.entries(liveSettings.debridLinkApiKeyDailyUsageBytes || {}).filter(([keyId]) => getDebridLinkApiKeyIds(nextSettings.debridLinkApiKeys).includes(keyId))
);
this.settings = nextSettings;
saveSettings(this.storagePaths, this.settings);
this.manager.setSettings(this.settings);
@@ -193,6 +201,30 @@ export class AppController {
return this.settings;
}
public resetProviderDailyUsage(provider: DebridProvider): AppSettings {
const liveSettings = this.manager.getSettings();
const nextSettings = normalizeSettings({
...liveSettings,
...resetProviderDailyUsage(liveSettings, provider)
});
this.settings = nextSettings;
saveSettings(this.storagePaths, this.settings);
this.manager.setSettings(this.settings);
return this.settings;
}
public resetDebridLinkApiKeyDailyUsage(keyId: string): AppSettings {
const liveSettings = this.manager.getSettings();
const nextSettings = normalizeSettings({
...liveSettings,
...resetDebridLinkApiKeyDailyUsage(liveSettings, keyId)
});
this.settings = nextSettings;
saveSettings(this.storagePaths, this.settings);
this.manager.setSettings(this.settings);
return this.settings;
}
public async openRealDebridLoginWindow(): Promise<void> {
await this.realDebridWebFallback.openLoginWindow();
}