Fix extraction speed and UI label updates

- Change OS priority from IDLE/BELOW_NORMAL to NORMAL/BELOW_NORMAL so
  extraction runs at full speed (matching manual 7-Zip/WinRAR performance)
- Use "high" priority in both hybrid and full extraction paths
- Increase hybrid extraction threads from hardcoded 2 to dynamic
  calculation (half CPU count, min 2, max 8)
- Fix emitState forced emit being silently dropped when a non-forced
  timer was already pending — forced emits now always replace pending
  timers to ensure immediate UI feedback during extraction transitions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe 2026-03-05 05:28:42 +01:00
parent 375ec36781
commit 1cda391dfe
2 changed files with 25 additions and 18 deletions

View File

@ -3570,14 +3570,16 @@ export class DownloadManager extends EventEmitter {
this.emit("state", this.getSnapshot());
return;
}
// Too soon — schedule deferred forced emit
if (!this.stateEmitTimer) {
this.stateEmitTimer = setTimeout(() => {
this.stateEmitTimer = null;
this.lastStateEmitAt = nowMs();
this.emit("state", this.getSnapshot());
}, MIN_FORCE_GAP_MS - sinceLastEmit);
// Too soon — replace any pending timer with a shorter forced-emit timer
if (this.stateEmitTimer) {
clearTimeout(this.stateEmitTimer);
this.stateEmitTimer = null;
}
this.stateEmitTimer = setTimeout(() => {
this.stateEmitTimer = null;
this.lastStateEmitAt = nowMs();
this.emit("state", this.getSnapshot());
}, MIN_FORCE_GAP_MS - sinceLastEmit);
return;
}
if (this.stateEmitTimer) {
@ -6400,7 +6402,7 @@ export class DownloadManager extends EventEmitter {
packageId,
hybridMode: true,
maxParallel: this.settings.maxParallelExtract || 2,
extractCpuPriority: this.settings.extractCpuPriority,
extractCpuPriority: "high",
onProgress: (progress) => {
if (progress.phase === "preparing") {
pkg.postProcessLabel = progress.archiveName || "Vorbereiten...";
@ -6762,8 +6764,8 @@ export class DownloadManager extends EventEmitter {
packageId,
skipPostCleanup: true,
maxParallel: this.settings.maxParallelExtract || 2,
// All downloads finished — use highest configured priority so extraction
// isn't starved. "high" maps to BELOW_NORMAL instead of the default IDLE.
// All downloads finished — use NORMAL OS priority so extraction runs at
// full speed (matching manual 7-Zip/WinRAR speed).
extractCpuPriority: "high",
onProgress: (progress) => {
if (progress.phase === "preparing") {

View File

@ -600,8 +600,8 @@ function extractCpuBudgetFromPriority(priority?: string): number {
function extractOsPriority(priority?: string): number {
switch (priority) {
case "high": return os.constants.priority.PRIORITY_BELOW_NORMAL;
default: return os.constants.priority.PRIORITY_LOW;
case "high": return os.constants.priority.PRIORITY_NORMAL;
default: return os.constants.priority.PRIORITY_BELOW_NORMAL;
}
}
@ -615,10 +615,15 @@ function extractCpuBudgetPercent(priority?: string): number {
function extractorThreadSwitch(hybridMode = false, priority?: string): string {
if (hybridMode) {
// 2 threads during hybrid extraction (download + extract simultaneously).
// JDownloader 2 uses in-process 7-Zip-JBinding which naturally limits throughput
// to ~16 MB/s write. 2 UnRAR threads produce similar controlled disk load.
return "-mt2";
// Use half the CPU budget during hybrid extraction to leave headroom for
// concurrent downloads. Falls back to at least 2 threads.
const envValue = Number(process.env.RD_EXTRACT_THREADS ?? NaN);
if (Number.isFinite(envValue) && envValue >= 1 && envValue <= 32) {
return `-mt${Math.floor(envValue)}`;
}
const cpuCount = Math.max(1, os.cpus().length || 1);
const hybridThreads = Math.max(2, Math.min(8, Math.floor(cpuCount / 2)));
return `-mt${hybridThreads}`;
}
const envValue = Number(process.env.RD_EXTRACT_THREADS ?? NaN);
if (Number.isFinite(envValue) && envValue >= 1 && envValue <= 32) {
@ -640,8 +645,8 @@ function lowerExtractProcessPriority(childPid: number | undefined, cpuPriority?:
return;
}
try {
// Lowers CPU scheduling priority so extraction doesn't starve other processes.
// high → BELOW_NORMAL, middle/low → IDLE. I/O priority stays Normal (like JDownloader 2).
// Sets CPU scheduling priority for the extraction process.
// high → NORMAL (full speed), default → BELOW_NORMAL. I/O priority stays Normal.
os.setPriority(pid, extractOsPriority(cpuPriority));
} catch {
// ignore: priority lowering is best-effort