release: ship v2.0.23 queue and account reliability fixes

Normalize RapidGator aliases, stabilize active queue ordering, preserve user-controlled package expansion, and align compact queue status presentation.

Separate Mega-Debrid API and Web credential pools, migrate legacy credentials and disabled states safely, refresh live account availability without restart, honor disabled credential persistence, and invalidate Web sessions without allowing stale in-flight, retry, or queued logins to restore old cookies.

Expand regression coverage for account isolation, legacy migration, session races, live scheduler refresh, update notes, and responsive queue behavior.
This commit is contained in:
Sucukdeluxe
2026-08-11 18:31:01 +02:00
parent b5b1ff88e5
commit a6e6d2d439
36 changed files with 2343 additions and 1376 deletions
+34 -27
View File
@@ -1,6 +1,13 @@
import type { DownloadItem, DownloadStatus, PackageEntry } from "../shared/types";
const ACTIVE_PACKAGE_STATUSES = new Set<DownloadStatus>(["downloading", "validating", "integrity_check", "extracting"]);
const ACTIVE_PACKAGE_STATUSES = new Set<DownloadStatus>(["downloading", "validating", "integrity_check", "extracting"]);
function isPackageActive(pkg: PackageEntry, itemsById: Record<string, DownloadItem>): boolean {
return pkg.itemIds.some((id) => {
const item = itemsById[id];
return item != null && ACTIVE_PACKAGE_STATUSES.has(item.status);
});
}
export function reorderPackageOrderByDrop(order: string[], draggedPackageId: string, targetPackageId: string): string[] {
const fromIndex = order.indexOf(draggedPackageId);
@@ -26,36 +33,36 @@ export function sortPackageOrderByName(order: string[], packages: Record<string,
return sorted;
}
export function sortPackagesForDisplay(
packages: PackageEntry[],
itemsById: Record<string, DownloadItem>,
running: boolean,
autoSortPackagesByProgress: boolean
): PackageEntry[] {
export function sortPackagesForDisplay(
packages: PackageEntry[],
itemsById: Record<string, DownloadItem>,
running: boolean,
autoSortPackagesByProgress: boolean
): PackageEntry[] {
if (!running || !autoSortPackagesByProgress || packages.length <= 1) {
return packages;
}
const active: PackageEntry[] = [];
const rest: PackageEntry[] = [];
// Float packages that have an active item to the top, but keep BOTH groups in
// their original (queue) order. Earlier this sorted the active group by live
// completedRatio/downloadedBytes — which change on every progress tick (every
// 150-700ms), so active packages visibly reshuffled the whole time. A package
// entering/leaving the active bucket is a real, discrete event (start/finish);
// ranking *within* the bucket by live bytes was pure jitter nobody needs.
for (const pkg of packages) {
const hasActive = pkg.itemIds.some((id) => {
const item = itemsById[id];
return item != null && ACTIVE_PACKAGE_STATUSES.has(item.status);
});
(hasActive ? active : rest).push(pkg);
}
if (active.length === 0 || active.length === packages.length) {
return packages;
}
const active = packages
.map((pkg, index) => ({ pkg, index }))
.filter(({ pkg }) => isPackageActive(pkg, itemsById))
.sort((left, right) => {
const leftStartedAt = left.pkg.downloadStartedAt || 0;
const rightStartedAt = right.pkg.downloadStartedAt || 0;
if (leftStartedAt > 0 && rightStartedAt > 0 && leftStartedAt !== rightStartedAt) {
return leftStartedAt - rightStartedAt;
}
if (leftStartedAt > 0 && rightStartedAt <= 0) return -1;
if (leftStartedAt <= 0 && rightStartedAt > 0) return 1;
return left.index - right.index;
})
.map(({ pkg }) => pkg);
const activeSet = new Set(active.map((pkg) => pkg.id));
const rest = packages.filter((pkg) => !activeSet.has(pkg.id));
if (active.length === 0) {
return packages;
}
return [...active, ...rest];
}