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>
- Use dataTransfer.effectAllowed='none' instead of preventDefault() for dragstart
(preventDefault does not cancel dragstart events per HTML spec)
- Clear claimedFilenames Set in processOneQueueItem finally block to prevent
stale claims from blocking re-downloads of same VODs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Queue selector uses min-width instead of fixed width for double-digit numbers
- Drag-start handler validates item is still pending before allowing drag
- ensureUniqueFilename uses in-memory claim set to prevent TOCTOU race
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move streamlink/ffmpeg path discovery, bundled tool management,
auto-install logic, and related caches (~430 lines) into a
dedicated tools module. main.ts uses dependency injection for
debug logging and directory paths to keep the module decoupled.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add parallel_downloads config option (1 or 2) with Settings UI dropdown.
Refactor processQueue to run concurrent download slots using Promise.race,
extracting per-item logic into processOneQueueItem. Add per-item process
tracking via activeDownloads Map and cancelledItemIds Set so cancel/pause
correctly terminates all active downloads.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace checkboxes with numbered selectors (1, 2, 3...) that show the
merge order. Click order determines VOD sequence in the merged result.
Chronological auto-sort removed — user controls the order.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>