Skip to content

feat(downloader): stderr progress indicator for long-running fetches (#108)#165

Merged
malaquiasdev merged 1 commit into
mainfrom
feat/108-progress-indicator
Jul 3, 2026
Merged

feat(downloader): stderr progress indicator for long-running fetches (#108)#165
malaquiasdev merged 1 commit into
mainfrom
feat/108-progress-indicator

Conversation

@malaquiasdev

Copy link
Copy Markdown
Owner

What this PR does

Adds a single-line stderr progress bar for chapter/page downloads, so long throttled sessions show real progress instead of a wall of retrying after Xms lines.

Chapter 2/4 (page 17/35) [=====>            ] 47% • avg 24s/page • ETA ~12min
  • src/walkthrough/progress.ts (new): createProgress({ enabled, totalChapters, write?, now? })updateChapter / updatePage / finish. Single-line \r redraw, throttled to ~5/sec (200ms), finish() forces the true final frame then a newline. ETA = moving average of page durations; shows ~-- until a sample exists; percent clamped [0,100].
  • src/downloader/{types,service}.ts: additive optional onPageProgress(totalPages) callback, fired once per completed page.
  • src/walkthrough/steps/execute.ts: threads a progress handle — updateChapter per bundle, updatePage per completed page, finish() at the end.
  • src/index.ts: --progress flag + resolveProgressEnabled = json ? false : (progress || isTTY).

Why it exists

Real session: ~50 min downloading Zom 100 with zero aggregate feedback. Users couldn't tell how many pages/chapters remained or the ETA.

How it works internally (progress is completion-counted, not index-based)

updatePage() increments an internal completion counter (reset per chapter), independent of dispatch order. This matters: the downloader fetches pages concurrently (imageConcurrency), so callbacks fire in completion order — trusting a dispatch-order index made the bar jump backward and never reach 100%. Counting completions guarantees the Nth completed page renders as page N, and the final completion of the last chapter lands on page N/N = 100% regardless of resolution order.

What did not change

  • All walkthrough/downloader changes are additive optional params — no restructuring.
  • Downloader stays UI-agnostic (callback, not a renderer dependency).

Test checklist

  • bun test — 524 pass, 0 fail
  • bun run typecheck — clean
  • bun run check — clean
  • Bar shows chapter N/total, page N/total, %, ETA on TTY
  • --json suppresses the bar (NDJSON only)
  • --progress forces the bar on non-TTY
  • Non-TTY + no flags → zero stderr writes (CI-safe); disabled handle = full no-op
  • Render throttled ~5/sec; finish() flushes the true final 100% frame
  • Monotonic under concurrency: out-of-order page completion still ends at page N/N / 100% (progress + downloader + execute tests, all asserting real content, no sorted-tautology)
  • ETA ~-- until first sample; percent clamped [0,100]; no NaN/div-by-zero

Reviewer notes

  • Went through two review cycles: QA caught a finish() no-flush bug; inspector then caught a deeper P0 — the progress counter ran backward under concurrency and the "concurrency" test was a sorted tautology. Both fixed (completion-counter model, pageIndex param dropped from the callback chain, real out-of-order tests). Final inspector: APPROVED.
  • Overlaps src/walkthrough/index.ts with feat(walkthrough): post-download loop — download more from the same manga without restarting #163 (post-download loop) — expect a rebase; changes here are additive.
  • P3 nits left as follow-up: execute.ts end-to-end test uses a single-page chapter (strong monotonic coverage lives in progress/service tests); onPageProgress passes totalPages each call though the renderer counts internally.

Closes

Closes #108

✅ Checklist

  • Tested locally
  • Self-review complete
  • No console errors
  • Code follows project conventions
  • Docs updated in docs/ (if applicable)

@malaquiasdev malaquiasdev merged commit c9c13b2 into main Jul 3, 2026
9 checks passed
@malaquiasdev malaquiasdev deleted the feat/108-progress-indicator branch July 3, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(downloader): progress indicator for long-running fetches

1 participant