fix: ETA calculation was using video duration instead of download progress

The old formula (avgSpeed * expectedDurationSeconds) simplified to just
(videoDuration - elapsedTime), showing 59min ETA for a 60min part after
1min of downloading. Now uses streamlink's actual progress percentage:
ETA = (elapsed / percent) * (100 - percent), which reflects real download
speed rather than video length.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-03-21 15:47:19 +01:00
parent a07ec1f958
commit d8f0836165

View File

@ -2203,6 +2203,7 @@ function downloadVODPart(
const args = [...streamlinkCmd.prefixArgs, url, 'best', '-o', filename, '--force']; const args = [...streamlinkCmd.prefixArgs, url, 'best', '-o', filename, '--force'];
let lastErrorLine = ''; let lastErrorLine = '';
const expectedDurationSeconds = parseClockDurationSeconds(endTime); const expectedDurationSeconds = parseClockDurationSeconds(endTime);
let lastStreamlinkPercent = 0;
if (startTime) { if (startTime) {
args.push('--hls-start-offset', startTime); args.push('--hls-start-offset', startTime);
@ -2250,22 +2251,16 @@ function downloadVODPart(
lastTime = now; lastTime = now;
let etaStr = ''; let etaStr = '';
if (speed > 0 && downloadedBytes > 0) { if (downloadedBytes > 0) {
const itemStartTime = itemTracking.startTime; const elapsedSec = (Date.now() - (itemTracking?.startTime || Date.now())) / 1000;
const elapsedSec = (Date.now() - itemStartTime) / 1000; if (elapsedSec > 5 && lastStreamlinkPercent > 1) {
if (elapsedSec > 5) { // Wait at least 5 seconds before showing ETA // Use streamlink's reported progress for accurate ETA
const avgSpeed = downloadedBytes / elapsedSec; const remainingSec = (elapsedSec / lastStreamlinkPercent) * (100 - lastStreamlinkPercent);
if (expectedDurationSeconds && expectedDurationSeconds > 0) { if (remainingSec > 0 && remainingSec < 86400) {
const estimatedTotalBytes = avgSpeed * expectedDurationSeconds;
if (estimatedTotalBytes > downloadedBytes) {
const remainingSec = (estimatedTotalBytes - downloadedBytes) / avgSpeed;
if (remainingSec > 0 && remainingSec < 86400) { // Between 0 and 24 hours
etaStr = formatETA(remainingSec); etaStr = formatETA(remainingSec);
} }
} }
} }
}
}
onProgress({ onProgress({
id: itemId, id: itemId,
@ -2290,6 +2285,7 @@ function downloadVODPart(
const match = line.match(/(\d+\.\d+)%/); const match = line.match(/(\d+\.\d+)%/);
if (match) { if (match) {
const percent = parseFloat(match[1]); const percent = parseFloat(match[1]);
lastStreamlinkPercent = percent;
onProgress({ onProgress({
id: itemId, id: itemId,
progress: percent, progress: percent,