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:
parent
a131f4a11b
commit
8d0c110415
@ -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",
|
||||||
|
|||||||
@ -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"]);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user