Fix: Statistik-Doppelzaehlung bei Retry + Debrid-Link Key-Cooldown bei Abbruch (Nutzer-Nachforderung Audit)
BYTE-DROP-RETRY-1 (MED): Integritaets-/Zu-klein-/Tiny-Neuversuche zaehlten die volle Dateigroesse pro Versuch erneut in die Byte-Statistik. Ursache: die 3 rm-dann-frisch-Sites (Integrity-Fail 8979, too-small 9020, tiny 10486) riefen dropItemContribution() auf, das den itemContributedBytes-Eintrag loescht, den die einzige Reconciliation (9991, writeMode 'w') zum Subtrahieren braucht -> Subtraktion tot -> Re-Download addiert erneut. Fix (Mechanismus a): dropItemContribution an diesen 3 Sites entfernt, der Eintrag ueberlebt, 9991 subtrahiert ihn korrekt (selbst-korrigierend nach writeMode). Zusaetzlich am selben Punkt totalDownloadedAllTime subtrahiert (wurde nie subtrahiert -> doppelte bei JEDEM frischen Re-Download). BEWUSST ausgeklammert: recordProviderDownloadedBytes/ providerDailyUsageBytes, da diese isProviderDailyLimited (= Verhalten) steuern und nicht provider-keyed sind. Reine Telemetrie-Korrektur, kein Slot/Admission betroffen. DL-1 (LOW): Ein abort-ohne-timeout (User-Cancel) setzte am DebridLink-Rotations- Catch via classifyKeyFailure einen 15s-Key-Cooldown (und konnte ueber Keys zu einer providerweiten Kaskade fuehren). Fix: Mega-Gate (2072) am Catch (2789) gespiegelt - abort-ohne-timeout + elapsedMs < getMegaDebridAbortMinRunMs() -> kein Cooldown; ran-long-enough -> 120s (Retry rotiert); throw bailt die Rotation. DL-CONCURRENCY-PILEUP (MED): untersucht -> bereits strukturell geloest (getSerializedValidatingLimit = nutzbare Accounts + shouldDelayStartForItem 8526; MW-1 haelt das Limit hoch). Kein Eingriff (Nutzer-Entscheidung). Je rot-bewiesener Test (per Temp-Revert, nicht-vakuum; BYTE-DROP beide Beine einzeln). Volle Suite 893 gruen, tsc unveraendert (6 vorbestehende Fehler).
This commit is contained in:
@@ -123,6 +123,11 @@ export function getDebridLinkKeyRuntimeStateForTests(keyId: string): DebridLinkR
|
||||
return status ? status.state : null;
|
||||
}
|
||||
|
||||
export function getDebridLinkKeyCooldownStateForTests(keyId: string, now = Date.now()): { remainingMs: number; message: string } | null {
|
||||
const state = getDebridLinkKeyCooldownState(keyId, now);
|
||||
return state ? { remainingMs: state.remainingMs, message: state.message } : null;
|
||||
}
|
||||
|
||||
function clearDebridLinkKeyCooldownState(keyId: string): void {
|
||||
debridLinkKeyCooldowns.delete(keyId);
|
||||
debridLinkKeyCooldownDetails.delete(keyId);
|
||||
@@ -2787,6 +2792,23 @@ class DebridLinkClient {
|
||||
} catch (error) {
|
||||
const failure = await this.classifyKeyFailure(error, apiKey, link, signal);
|
||||
const elapsedMs = Date.now() - testStartedAt;
|
||||
const abortText = compactErrorText(error).replace(/^Error:\s*/i, "");
|
||||
if (/aborted/i.test(abortText) && !/timeout/i.test(abortText)) {
|
||||
const ranLongEnough = elapsedMs >= getMegaDebridAbortMinRunMs();
|
||||
if (ranLongEnough) {
|
||||
setDebridLinkKeyCooldownState(apiKey.id, DEBRID_LINK_KEY_COOLDOWN_MS, `Abbruch/Timeout nach ${Math.ceil(elapsedMs / 1000)}s`, "temporary");
|
||||
} else {
|
||||
clearDebridLinkKeyCooldownState(apiKey.id);
|
||||
}
|
||||
failures.push(`Debrid-Link${keyLabel}: ${abortText}`);
|
||||
logAccountRotation("WARN", providerName, rotationLabel, "TIMEOUT_COOLDOWN", {
|
||||
elapsedMs,
|
||||
reason: abortText,
|
||||
cooldownSec: ranLongEnough ? Math.ceil(DEBRID_LINK_KEY_COOLDOWN_MS / 1000) : 0,
|
||||
next: "naechster Key beim Retry"
|
||||
});
|
||||
throw new Error(`Debrid-Link${keyLabel}: ${abortText}`);
|
||||
}
|
||||
attemptedKeyFailures.push({
|
||||
message: `Debrid-Link${keyLabel}: ${failure.message}`,
|
||||
cooldownMs: failure.cooldownMs,
|
||||
|
||||
@@ -8976,7 +8976,6 @@ export class DownloadManager extends EventEmitter {
|
||||
if (item.attempts < maxAttempts) {
|
||||
item.status = "integrity_check";
|
||||
item.progressPercent = 0;
|
||||
this.dropItemContribution(item.id);
|
||||
item.downloadedBytes = 0;
|
||||
item.totalBytes = unrestricted.fileSize;
|
||||
this.emitState();
|
||||
@@ -9017,7 +9016,6 @@ export class DownloadManager extends EventEmitter {
|
||||
} catch {
|
||||
}
|
||||
this.releaseTargetPath(item.id);
|
||||
this.dropItemContribution(item.id);
|
||||
item.downloadedBytes = 0;
|
||||
item.progressPercent = 0;
|
||||
item.totalBytes = (item.totalBytes || 0) > 0 ? item.totalBytes : null;
|
||||
@@ -9993,6 +9991,7 @@ export class DownloadManager extends EventEmitter {
|
||||
if (previouslyContributed > 0) {
|
||||
this.session.totalDownloadedBytes = Math.max(0, this.session.totalDownloadedBytes - previouslyContributed);
|
||||
this.sessionDownloadedBytes = Math.max(0, this.sessionDownloadedBytes - previouslyContributed);
|
||||
this.settings.totalDownloadedAllTime = Math.max(0, Number(this.settings.totalDownloadedAllTime || 0) - previouslyContributed);
|
||||
this.itemContributedBytes.set(active.itemId, 0);
|
||||
}
|
||||
if (existingBytes > 0) {
|
||||
@@ -10483,7 +10482,6 @@ export class DownloadManager extends EventEmitter {
|
||||
await fs.promises.rm(effectiveTargetPath, { force: true });
|
||||
} catch { }
|
||||
this.releaseTargetPath(active.itemId);
|
||||
this.dropItemContribution(active.itemId);
|
||||
item.downloadedBytes = 0;
|
||||
item.progressPercent = 0;
|
||||
throw new Error(`Download zu klein (${written} B) – Hoster-Fehlerseite?${snippet ? ` Inhalt: "${snippet}"` : ""}`);
|
||||
|
||||
Reference in New Issue
Block a user