H4 + H2 formatter half: carry raw numbers across the progress boundary

Closes H4 and the remaining formatter half of H2.

- New @shared/format.ts is the single home for fmtBytes/fmtSpeed/fmtEta, imported
  by both main and renderer. main/lib/formatters.ts re-exports them.
- DownloadProgress now carries raw speedBytesPerSec/etaSeconds instead of
  pre-formatted speed/eta strings. main's parseProgress emits the raw numbers.
- The renderer stores the raw numbers on DownloadItem; QueueItem formats them for
  per-item display, and summarizeQueue sums/maxes them directly. The lossy
  string->number->string round-trip in queueStats (parseSpeed / parseEtaSeconds /
  a local formatSpeed / formatEta) is deleted, along with a duplicate fmtEta in
  store/downloads.ts.
- Per-item and aggregate speed now use the same 1024-based scale; the aggregate
  previously used a 1000-based formatter (a latent inconsistency).

Tests updated for the raw-number contract (parseProgress, summarizeQueue).
typecheck + 248 tests + eslint + prettier green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 21:03:14 -04:00
parent d050e48af6
commit 3d09174c16
9 changed files with 122 additions and 135 deletions
+18 -12
View File
@@ -223,20 +223,26 @@ token system + shared primitives (UI/SIMP — high value, larger effort) · i18n
- [ ] **H1 — Decompose god files.** `SettingsView.tsx` (1104, ~11 cards, ~30 selector subs +
~20 useState), `DownloadBar.tsx` (858, ~17 state hooks), `store/downloads.ts` (614).
- [ ] **H2 — Consolidate scattered utilities.** `youtubeId` ×2; 4 YouTube-URL-parsing variants;
byte/speed/eta/duration formatting across 4 modules. *URL half done: one canonical `youtubeId` now
lives in [urlHelpers.ts](src/renderer/src/lib/urlHelpers.ts) (the complete watch/shorts/embed/live/
youtu.be parse); `thumb.ts`, `queueStats.ts` (`sameVideo`), and `store/downloads.ts` (`titleFromUrl`,
which also fixes an old bug where any host with a `?v=` param was mislabeled "YouTube video") all import
it — the three ad-hoc reimplementations are gone. Unit-tested in `test/clipboardLink.test.ts` (247 pass).
**Formatter half still open** and intentionally deferred to travel with **H4**: the renderer only
re-parses `speed`/`eta` strings back to numbers because raw numbers aren't carried across the IPC
boundary (that's H4), and `fmtBytes` (1024) vs `formatSpeed` (1000) differ on purpose — so a real
merge means solving H4 first, not just extracting a shared module.*
- [x] **H2 — Consolidate scattered utilities.** `youtubeId` ×2; 4 YouTube-URL-parsing variants;
byte/speed/eta/duration formatting across 4 modules. *URL half: one canonical `youtubeId` now lives in
[urlHelpers.ts](src/renderer/src/lib/urlHelpers.ts) (the complete watch/shorts/embed/live/youtu.be
parse); `thumb.ts`, `queueStats.ts` (`sameVideo`), and `store/downloads.ts` (`titleFromUrl`, which also
fixes an old bug where any host with a `?v=` param was mislabeled "YouTube video") all import it — the
three ad-hoc reimplementations are gone. Formatter half (done with H4): `fmtBytes`/`fmtSpeed`/`fmtEta`
now have a single home in [@shared/format.ts](src/shared/format.ts) imported by both sides; the main
formatters module re-exports them and the renderer's three private copies (a byte formatter, an ETA
formatter, and a speed string→number re-parser in `queueStats`) are deleted. Per-item and aggregate
speed now use the same 1024-based scale (the old aggregate used 1000, a latent inconsistency).
Unit-tested in `test/clipboardLink.test.ts` + `test/download.test.ts` + `test/queueStats.test.ts`.*
- [x] **H3 — Fix `probe.ts → download.ts` dependency direction** (`fmtBytes` import); resolves
with H2's shared formatter.
- [ ] **H4 — Carry raw numbers across the progress boundary.** `DownloadProgress` ships
formatted `speed`/`eta` strings; `queueStats.ts` re-parses them back to numbers.
- [x] **H4 — Carry raw numbers across the progress boundary.** `DownloadProgress` ships
formatted `speed`/`eta` strings; `queueStats.ts` re-parses them back to numbers. *Fixed: `DownloadProgress`
now carries `speedBytesPerSec`/`etaSeconds` as raw numbers (main's `parseProgress` no longer formats them);
the renderer stores the raw numbers on `DownloadItem`, `QueueItem` formats them for display via
[@shared/format](src/shared/format.ts), and `summarizeQueue` sums/maxes the numbers directly — the lossy
string→number→string round-trip (and `queueStats`' `parseSpeed`/`parseEtaSeconds`) is gone. Done together
with the formatter half of H2.*
- [x] **H5 — History re-download silently changes quality.** `HistoryView.redownload` passes the
stored `quality` (for format-picker downloads this is a *label* like "720p · mp4 · 184 MB")
back as `quality` with no `formatId`; `buildArgs.videoFormat()` has no matching case and falls