/** * What the progress bar on a download row should say. * * Extracted from `DownloadItem.svelte` so the three cases can be unit-tested: * an exact total (the server sent `Content-Length`), an estimated one (a * transcode sends none, so the backend predicted it from the source), and no * total at all — which used to render as an empty bar reading "0%" for the * whole of a transcode download. Progress with no denominator is not 0%; it * is unknown, and the bar says so. * * TRACES: UR-071 | DR-290 | UT-254 */ export interface ProgressSource { status: "pending" | "downloading" | "completed" | "failed" | "paused"; /** 0..1 as the backend reports it; already capped short of 1 when estimated. */ progress: number; bytesDownloaded: number; fileSize?: number; /** `fileSize` is the backend's prediction, not the server's word. */ fileSizeEstimated?: boolean; } export type ProgressKind = "exact" | "estimated" | "indeterminate"; export interface ProgressView { kind: ProgressKind; /** Whole percent for the bar width; `null` when there is nothing to measure against. */ percent: number | null; /** "42%" / "~42%"; empty when indeterminate. */ percentLabel: string; /** "512 B / 1.0 KB", "420 B / ~1000 B", or just the bytes so far. */ label: string; } export function formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1); return `${(bytes / Math.pow(k, i)).toFixed(i === 0 ? 0 : 1)} ${sizes[i]}`; } export function describeProgress(d: ProgressSource): ProgressView { const downloaded = formatBytes(d.bytesDownloaded); if (!d.fileSize) { return { kind: "indeterminate", percent: null, percentLabel: "", label: downloaded }; } const percent = Math.round(Math.min(Math.max(d.progress, 0), 1) * 100); if (d.fileSizeEstimated) { return { kind: "estimated", percent, percentLabel: `~${percent}%`, label: `${downloaded} / ~${formatBytes(d.fileSize)}`, }; } return { kind: "exact", percent, percentLabel: `${percent}%`, label: `${downloaded} / ${formatBytes(d.fileSize)}`, }; }