3d09174c16
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>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
import type { DownloadProgress } from '@shared/ipc'
|
|
import { fmtBytes } from '@shared/format'
|
|
|
|
// yt-dlp progress-line parsing. Extracted from download.ts so it can be
|
|
// unit-tested without pulling in the electron import chain (L37). The byte/
|
|
// speed/ETA formatters now live in @shared/format (one home for both sides of
|
|
// the IPC boundary); re-exported here so existing importers keep working.
|
|
export { fmtBytes, fmtSpeed, fmtEta } from '@shared/format'
|
|
|
|
function num(s?: string): number | undefined {
|
|
if (!s || s === 'NA') return undefined
|
|
const n = Number(s)
|
|
return Number.isFinite(n) ? n : undefined
|
|
}
|
|
|
|
export function parseProgress(rest: string): DownloadProgress | null {
|
|
const parts = rest.split('|')
|
|
if (parts.length < 6) return null
|
|
const [status, dl, total, totalEst, speed, eta] = parts
|
|
const downloaded = num(dl)
|
|
const totalBytes = num(total) ?? num(totalEst)
|
|
let progress = 0
|
|
if (totalBytes && downloaded != null) progress = Math.min(1, downloaded / totalBytes)
|
|
return {
|
|
status: status || 'downloading',
|
|
progress,
|
|
// Carry raw numbers across the IPC boundary; the renderer formats them for
|
|
// display AND aggregates them (combined speed / longest ETA) without having
|
|
// to re-parse a formatted string (H4).
|
|
speedBytesPerSec: num(speed),
|
|
etaSeconds: num(eta),
|
|
sizeLabel: totalBytes ? fmtBytes(totalBytes) : undefined,
|
|
// No (estimated) total → the % can never advance; flag it so the renderer
|
|
// shows an indeterminate bar rather than a stuck 0% (L137).
|
|
sizeUnknown: !totalBytes
|
|
}
|
|
}
|