fix: align live speed and split package reset
This commit is contained in:
@@ -1874,7 +1874,7 @@ export class DownloadManager extends EventEmitter {
|
||||
|
||||
private persistTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
private speedEvents: Array<{ at: number; bytes: number; pid: string }> = [];
|
||||
private speedEvents: Array<{ at: number; bytes: number; pid: string; itemId: string }> = [];
|
||||
|
||||
private summary: DownloadSummary | null = null;
|
||||
|
||||
@@ -1883,7 +1883,9 @@ export class DownloadManager extends EventEmitter {
|
||||
private stateEmitTimer: NodeJS.Timeout | null = null;
|
||||
private lastStateEmitAt = 0;
|
||||
|
||||
private speedBytesLastWindow = 0;
|
||||
private speedBytesLastWindow = 0;
|
||||
|
||||
private speedBytesPerItem = new Map<string, number>();
|
||||
|
||||
private sessionDownloadedBytes = 0;
|
||||
private sessionCompletedFiles = 0;
|
||||
@@ -2808,7 +2810,15 @@ export class DownloadManager extends EventEmitter {
|
||||
|
||||
const reconnectMs = Math.max(0, this.session.reconnectUntil - now);
|
||||
|
||||
const snapshotSession = cloneSession(this.session);
|
||||
const snapshotSession = cloneSession(this.session);
|
||||
for (const [itemId, item] of Object.entries(snapshotSession.items)) {
|
||||
const speedBps = this.session.running && !paused && item.status === "downloading"
|
||||
? Math.floor((this.speedBytesPerItem.get(item.id) ?? 0) / SPEED_WINDOW_SECONDS)
|
||||
: 0;
|
||||
if (item.speedBps !== speedBps) {
|
||||
snapshotSession.items[itemId] = { ...item, speedBps };
|
||||
}
|
||||
}
|
||||
let rendererState: ReturnType<typeof createRendererState>;
|
||||
if (this.settingsSnapshotCache && now - this.settingsSnapshotCacheAt < 400) {
|
||||
rendererState = this.settingsSnapshotCache;
|
||||
@@ -2964,10 +2974,11 @@ export class DownloadManager extends EventEmitter {
|
||||
changed = true;
|
||||
}
|
||||
if (this.speedEvents.length > 0 || this.speedBytesLastWindow !== 0 || this.speedBytesPerPackage.size > 0) {
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
@@ -3070,10 +3081,11 @@ export class DownloadManager extends EventEmitter {
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.lastGlobalProgressAt = now;
|
||||
this.speedEvents = [];
|
||||
this.speedEventsHead = 0;
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.summary = null;
|
||||
this.speedEventsHead = 0;
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.summary = null;
|
||||
this.invalidateStatsCache();
|
||||
this.persistSoon();
|
||||
this.emitState(true);
|
||||
@@ -3390,10 +3402,11 @@ export class DownloadManager extends EventEmitter {
|
||||
this.claimedTargetPathByItem.clear();
|
||||
this.itemContributedBytes.clear();
|
||||
this.speedEvents = [];
|
||||
this.speedEventsHead = 0;
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.packagePostProcessTasks.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.packagePostProcessTasks.clear();
|
||||
this.packagePostProcessAbortControllers.clear();
|
||||
this.packageDeferredPostProcessAbortControllers.clear();
|
||||
this.packageDeferredPostProcessTasks.clear();
|
||||
@@ -6339,11 +6352,12 @@ export class DownloadManager extends EventEmitter {
|
||||
this.session.summaryText = "";
|
||||
this.session.reconnectUntil = 0;
|
||||
this.session.reconnectReason = "";
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.lastGlobalProgressAt = nowMs();
|
||||
this.lastReconnectMarkAt = 0;
|
||||
this.consecutiveReconnects = 0;
|
||||
@@ -6452,11 +6466,12 @@ export class DownloadManager extends EventEmitter {
|
||||
this.session.summaryText = "";
|
||||
this.session.reconnectUntil = 0;
|
||||
this.session.reconnectReason = "";
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.lastGlobalProgressAt = nowMs();
|
||||
this.lastReconnectMarkAt = 0;
|
||||
this.consecutiveReconnects = 0;
|
||||
@@ -6569,11 +6584,12 @@ export class DownloadManager extends EventEmitter {
|
||||
this.session.summaryText = "";
|
||||
this.session.reconnectUntil = 0;
|
||||
this.session.reconnectReason = "";
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.lastGlobalProgressAt = nowMs();
|
||||
this.summary = null;
|
||||
this.nonResumableActive = 0;
|
||||
@@ -6610,9 +6626,10 @@ export class DownloadManager extends EventEmitter {
|
||||
this.session.reconnectReason = "";
|
||||
this.lastReconnectMarkAt = 0;
|
||||
this.consecutiveReconnects = 0;
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.lastGlobalProgressBytes = 0;
|
||||
this.lastGlobalProgressAt = nowMs();
|
||||
@@ -6652,11 +6669,12 @@ export class DownloadManager extends EventEmitter {
|
||||
this.retryStateByItem.clear();
|
||||
this.lastGlobalProgressBytes = this.session.totalDownloadedBytes;
|
||||
this.lastGlobalProgressAt = nowMs();
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEventsHead = 0;
|
||||
if (!keepExtraction) {
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
if (!keepExtraction) {
|
||||
this.abortPostProcessing("stop");
|
||||
for (const waiter of this.packagePostProcessWaiters) { waiter.resolve(); }
|
||||
this.packagePostProcessWaiters = [];
|
||||
@@ -6774,11 +6792,12 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.runItemIds.clear();
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.runItemIds.clear();
|
||||
this.runPackageIds.clear();
|
||||
this.runOutcomes.clear();
|
||||
this.runCompletedPackages.clear();
|
||||
@@ -6815,10 +6834,11 @@ export class DownloadManager extends EventEmitter {
|
||||
this.session.paused = !this.session.paused;
|
||||
|
||||
if (!wasPaused && this.session.paused) {
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedEventsHead = 0;
|
||||
this.speedEvents = [];
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.speedEventsHead = 0;
|
||||
}
|
||||
|
||||
if (wasPaused && !this.session.paused) {
|
||||
@@ -7198,17 +7218,20 @@ export class DownloadManager extends EventEmitter {
|
||||
}, emitDelay);
|
||||
}
|
||||
|
||||
private speedEventsHead = 0;
|
||||
private speedBytesPerPackage = new Map<string, number>();
|
||||
private speedEventsHead = 0;
|
||||
private speedBytesPerPackage = new Map<string, number>();
|
||||
|
||||
private pruneSpeedEvents(now: number): void {
|
||||
const cutoff = now - SPEED_WINDOW_SECONDS * 1000;
|
||||
while (this.speedEventsHead < this.speedEvents.length && this.speedEvents[this.speedEventsHead].at < cutoff) {
|
||||
const ev = this.speedEvents[this.speedEventsHead];
|
||||
this.speedBytesLastWindow = Math.max(0, this.speedBytesLastWindow - ev.bytes);
|
||||
const pkgBytes = (this.speedBytesPerPackage.get(ev.pid) ?? 0) - ev.bytes;
|
||||
if (pkgBytes <= 0) this.speedBytesPerPackage.delete(ev.pid);
|
||||
else this.speedBytesPerPackage.set(ev.pid, pkgBytes);
|
||||
const pkgBytes = (this.speedBytesPerPackage.get(ev.pid) ?? 0) - ev.bytes;
|
||||
if (pkgBytes <= 0) this.speedBytesPerPackage.delete(ev.pid);
|
||||
else this.speedBytesPerPackage.set(ev.pid, pkgBytes);
|
||||
const itemBytes = (this.speedBytesPerItem.get(ev.itemId) ?? 0) - ev.bytes;
|
||||
if (itemBytes <= 0) this.speedBytesPerItem.delete(ev.itemId);
|
||||
else this.speedBytesPerItem.set(ev.itemId, itemBytes);
|
||||
this.speedEventsHead += 1;
|
||||
}
|
||||
if (this.speedEventsHead > 200) {
|
||||
@@ -7219,7 +7242,7 @@ export class DownloadManager extends EventEmitter {
|
||||
|
||||
private lastSpeedPruneAt = 0;
|
||||
|
||||
private recordSpeed(bytes: number, packageId: string = ""): void {
|
||||
private recordSpeed(bytes: number, packageId: string = "", itemId: string = ""): void {
|
||||
const now = nowMs();
|
||||
if (!Number.isFinite(bytes) || bytes <= 0) {
|
||||
return;
|
||||
@@ -7231,13 +7254,14 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
const bucket = now - (now % 120);
|
||||
const last = this.speedEvents[this.speedEvents.length - 1];
|
||||
if (last && last.at === bucket && last.pid === packageId) {
|
||||
last.bytes += bytes;
|
||||
} else {
|
||||
this.speedEvents.push({ at: bucket, bytes, pid: packageId });
|
||||
}
|
||||
this.speedBytesLastWindow += bytes;
|
||||
this.speedBytesPerPackage.set(packageId, (this.speedBytesPerPackage.get(packageId) ?? 0) + bytes);
|
||||
if (last && last.at === bucket && last.pid === packageId && last.itemId === itemId) {
|
||||
last.bytes += bytes;
|
||||
} else {
|
||||
this.speedEvents.push({ at: bucket, bytes, pid: packageId, itemId });
|
||||
}
|
||||
this.speedBytesLastWindow += bytes;
|
||||
this.speedBytesPerPackage.set(packageId, (this.speedBytesPerPackage.get(packageId) ?? 0) + bytes);
|
||||
this.speedBytesPerItem.set(itemId, (this.speedBytesPerItem.get(itemId) ?? 0) + bytes);
|
||||
if (now - this.lastSpeedPruneAt >= 1500) {
|
||||
this.pruneSpeedEvents(now);
|
||||
this.lastSpeedPruneAt = now;
|
||||
@@ -11024,8 +11048,6 @@ export class DownloadManager extends EventEmitter {
|
||||
origin_http_error: "Downloadserver hat die Segmentanfrage abgelehnt",
|
||||
segment_failed: "Proxy-Segmente konnten nicht vollständig geladen werden"
|
||||
};
|
||||
let proxyWindowBytes = 0;
|
||||
let proxyWindowStartedAt = nowMs();
|
||||
let lastProxyUiEmitAt = 0;
|
||||
item.fullStatus = `Proxy-Download startet (Gesamtlimit ${this.settings.proxyConnectionsPerDownload})`;
|
||||
item.updatedAt = nowMs();
|
||||
@@ -11058,14 +11080,13 @@ export class DownloadManager extends EventEmitter {
|
||||
}
|
||||
},
|
||||
onTrafficBytes: (bytes) => {
|
||||
proxyWindowBytes += bytes;
|
||||
this.recordProviderDownloadedBytes(
|
||||
item.provider,
|
||||
bytes,
|
||||
item.providerAccountId,
|
||||
item.providerAccountLabel
|
||||
);
|
||||
this.recordSpeed(bytes, item.packageId);
|
||||
this.recordSpeed(bytes, item.packageId, active.itemId);
|
||||
},
|
||||
onProgress: (deltaBytes, downloadedBytes, totalBytes) => {
|
||||
this.session.totalDownloadedBytes = Math.max(0, this.session.totalDownloadedBytes + deltaBytes);
|
||||
@@ -11076,19 +11097,16 @@ export class DownloadManager extends EventEmitter {
|
||||
Math.max(0, (this.itemContributedBytes.get(active.itemId) || 0) + deltaBytes)
|
||||
);
|
||||
const nowTick = nowMs();
|
||||
const elapsed = Math.max((nowTick - proxyWindowStartedAt) / 1000, 0.2);
|
||||
item.status = this.session.paused ? "paused" : "downloading";
|
||||
item.downloadedBytes = downloadedBytes;
|
||||
item.totalBytes = totalBytes;
|
||||
item.progressPercent = Math.max(0, Math.min(100, Math.floor((downloadedBytes / totalBytes) * 100)));
|
||||
item.speedBps = this.session.paused ? 0 : Math.max(0, Math.floor(proxyWindowBytes / elapsed));
|
||||
item.speedBps = this.session.paused
|
||||
? 0
|
||||
: Math.floor((this.speedBytesPerItem.get(active.itemId) ?? 0) / SPEED_WINDOW_SECONDS);
|
||||
item.fullStatus = this.session.paused
|
||||
? "Pausiert"
|
||||
: `Proxy-Download läuft (Gesamtlimit ${this.settings.proxyConnectionsPerDownload})`;
|
||||
if (elapsed >= 0.5) {
|
||||
proxyWindowStartedAt = nowTick;
|
||||
proxyWindowBytes = 0;
|
||||
}
|
||||
if (nowTick - lastProxyUiEmitAt >= DOWNLOAD_LIVE_UPDATE_INTERVAL_MS || deltaBytes < 0) {
|
||||
item.updatedAt = nowTick;
|
||||
this.emitState();
|
||||
@@ -11751,7 +11769,7 @@ export class DownloadManager extends EventEmitter {
|
||||
item.providerAccountLabel
|
||||
);
|
||||
this.itemContributedBytes.set(active.itemId, (this.itemContributedBytes.get(active.itemId) || 0) + buffer.length);
|
||||
this.recordSpeed(buffer.length, item.packageId);
|
||||
this.recordSpeed(buffer.length, item.packageId, active.itemId);
|
||||
throughputWindowBytes += buffer.length;
|
||||
|
||||
if (completionPlan.canFinishEarly && completionPlan.expectedTotal && written >= completionPlan.expectedTotal) {
|
||||
@@ -14630,6 +14648,7 @@ export class DownloadManager extends EventEmitter {
|
||||
this.speedEventsHead = 0;
|
||||
this.speedBytesLastWindow = 0;
|
||||
this.speedBytesPerPackage.clear();
|
||||
this.speedBytesPerItem.clear();
|
||||
this.resetGlobalSpeedLimitState();
|
||||
this.nonResumableActive = 0;
|
||||
this.lastGlobalProgressBytes = this.session.totalDownloadedBytes;
|
||||
|
||||
Reference in New Issue
Block a user