Release v1.6.7

Add proactive disk-busy detection: lower STREAM_HIGH_WATER_MARK from 2 MB
to 512 KB so backpressure triggers sooner, and monitor stream.writableLength
to show "Warte auf Festplatte" after 300 ms of undrained writes — before
actual backpressure hits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe 2026-03-04 14:13:14 +01:00
parent a131f4a11b
commit 8d0c110415
3 changed files with 37 additions and 5 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "real-debrid-downloader", "name": "real-debrid-downloader",
"version": "1.6.6", "version": "1.6.7",
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)", "description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
"main": "build/main/main/main.js", "main": "build/main/main/main.js",
"author": "Sucukdeluxe", "author": "Sucukdeluxe",

View File

@ -19,7 +19,8 @@ export const CHUNK_SIZE = 512 * 1024;
export const WRITE_BUFFER_SIZE = 512 * 1024; // 512 KB write buffer (JDownloader: 500 KB) export const WRITE_BUFFER_SIZE = 512 * 1024; // 512 KB write buffer (JDownloader: 500 KB)
export const WRITE_FLUSH_TIMEOUT_MS = 2000; // 2s flush timeout export const WRITE_FLUSH_TIMEOUT_MS = 2000; // 2s flush timeout
export const ALLOCATION_UNIT_SIZE = 4096; // 4 KB NTFS alignment export const ALLOCATION_UNIT_SIZE = 4096; // 4 KB NTFS alignment
export const STREAM_HIGH_WATER_MARK = 2 * 1024 * 1024; // 2 MB stream buffer (JDownloader: Java NIO FileChannel default ~8 MB) export const STREAM_HIGH_WATER_MARK = 512 * 1024; // 512 KB stream buffer — lower than before (2 MB) so backpressure triggers sooner when disk is slow
export const DISK_BUSY_THRESHOLD_MS = 300; // Show "Warte auf Festplatte" if writableLength > 0 for this long
export const SAMPLE_DIR_NAMES = new Set(["sample", "samples"]); export const SAMPLE_DIR_NAMES = new Set(["sample", "samples"]);
export const SAMPLE_VIDEO_EXTENSIONS = new Set([".mkv", ".mp4", ".avi", ".mov", ".wmv", ".m4v", ".ts", ".m2ts", ".webm"]); export const SAMPLE_VIDEO_EXTENSIONS = new Set([".mkv", ".mp4", ".avi", ".mov", ".wmv", ".m4v", ".ts", ".m2ts", ".webm"]);

View File

@ -19,7 +19,7 @@ import {
StartConflictResolutionResult, StartConflictResolutionResult,
UiSnapshot UiSnapshot
} from "../shared/types"; } from "../shared/types";
import { REQUEST_RETRIES, SAMPLE_VIDEO_EXTENSIONS, SPEED_WINDOW_SECONDS, WRITE_BUFFER_SIZE, WRITE_FLUSH_TIMEOUT_MS, ALLOCATION_UNIT_SIZE, STREAM_HIGH_WATER_MARK } from "./constants"; import { REQUEST_RETRIES, SAMPLE_VIDEO_EXTENSIONS, SPEED_WINDOW_SECONDS, WRITE_BUFFER_SIZE, WRITE_FLUSH_TIMEOUT_MS, ALLOCATION_UNIT_SIZE, STREAM_HIGH_WATER_MARK, DISK_BUSY_THRESHOLD_MS } from "./constants";
import { cleanupCancelledPackageArtifactsAsync } from "./cleanup"; import { cleanupCancelledPackageArtifactsAsync } from "./cleanup";
import { DebridService, MegaWebUnrestrictor, checkRapidgatorOnline } from "./debrid"; import { DebridService, MegaWebUnrestrictor, checkRapidgatorOnline } from "./debrid";
import { clearExtractResumeState, collectArchiveCleanupTargets, extractPackageArchives, findArchiveCandidates } from "./extractor"; import { clearExtractResumeState, collectArchiveCleanupTargets, extractPackageArchives, findArchiveCandidates } from "./extractor";
@ -4984,6 +4984,7 @@ export class DownloadManager extends EventEmitter {
const stallTimeoutMs = getDownloadStallTimeoutMs(); const stallTimeoutMs = getDownloadStallTimeoutMs();
const drainTimeoutMs = Math.max(30000, Math.min(300000, stallTimeoutMs > 0 ? stallTimeoutMs * 12 : 120000)); const drainTimeoutMs = Math.max(30000, Math.min(300000, stallTimeoutMs > 0 ? stallTimeoutMs * 12 : 120000));
let lastDiskBusyEmitAt = 0; let lastDiskBusyEmitAt = 0;
let diskBusySince = 0; // timestamp when writableLength first became > 0
const waitDrain = (): Promise<void> => new Promise((resolve, reject) => { const waitDrain = (): Promise<void> => new Promise((resolve, reject) => {
if (active.abortController.signal.aborted) { if (active.abortController.signal.aborted) {
@ -5221,6 +5222,29 @@ export class DownloadManager extends EventEmitter {
await alignedFlush(false); await alignedFlush(false);
} }
// Proactive disk-busy detection: if the stream's internal buffer
// hasn't drained for DISK_BUSY_THRESHOLD_MS, the OS write calls are
// lagging — typically because the physical disk can't keep up. Show
// "Warte auf Festplatte" immediately instead of waiting for full
// backpressure (stream.write returning false).
if (stream.writableLength > 0) {
if (diskBusySince === 0) diskBusySince = nowMs();
const busyMs = nowMs() - diskBusySince;
if (busyMs >= DISK_BUSY_THRESHOLD_MS && item.status !== "paused" && !this.session.paused) {
const nowTick = nowMs();
if (nowTick - lastDiskBusyEmitAt >= 1200) {
item.status = "downloading";
item.speedBps = 0;
item.fullStatus = `Warte auf Festplatte (${providerLabel(item.provider)})`;
item.updatedAt = nowTick;
this.emitState();
lastDiskBusyEmitAt = nowTick;
}
}
} else {
diskBusySince = 0;
}
written += buffer.length; written += buffer.length;
windowBytes += buffer.length; windowBytes += buffer.length;
this.session.totalDownloadedBytes += buffer.length; this.session.totalDownloadedBytes += buffer.length;
@ -5249,10 +5273,17 @@ export class DownloadManager extends EventEmitter {
} }
item.status = "downloading"; item.status = "downloading";
item.speedBps = Math.max(0, Math.floor(speed));
item.downloadedBytes = written; item.downloadedBytes = written;
item.progressPercent = item.totalBytes ? Math.max(0, Math.min(100, Math.floor((written / item.totalBytes) * 100))) : 0; item.progressPercent = item.totalBytes ? Math.max(0, Math.min(100, Math.floor((written / item.totalBytes) * 100))) : 0;
item.fullStatus = `Download läuft (${providerLabel(item.provider)})`; // Keep "Warte auf Festplatte" label if disk is busy; otherwise show normal status
const diskBusy = diskBusySince > 0 && nowMs() - diskBusySince >= DISK_BUSY_THRESHOLD_MS;
if (diskBusy) {
item.speedBps = 0;
item.fullStatus = `Warte auf Festplatte (${providerLabel(item.provider)})`;
} else {
item.speedBps = Math.max(0, Math.floor(speed));
item.fullStatus = `Download läuft (${providerLabel(item.provider)})`;
}
const nowTick = nowMs(); const nowTick = nowMs();
if (nowTick - lastUiEmitAt >= uiUpdateIntervalMs) { if (nowTick - lastUiEmitAt >= uiUpdateIntervalMs) {
item.updatedAt = nowTick; item.updatedAt = nowTick;