diff --git a/src/renderer-archive.ts b/src/renderer-archive.ts index 181b66e..e42ca66 100644 --- a/src/renderer-archive.ts +++ b/src/renderer-archive.ts @@ -2,15 +2,6 @@ let archiveStreamerSelectPopulated = false; let archiveSearchInFlight = false; let archiveSearchDebounceTimer: number | null = null; -function formatBytesForArchive(bytes: number): string { - if (!Number.isFinite(bytes) || bytes <= 0) return '0 B'; - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; - if (bytes < 1024 * 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; - return `${(bytes / (1024 * 1024 * 1024 * 1024)).toFixed(2)} TB`; -} - function populateArchiveStreamerSelect(): void { if (archiveStreamerSelectPopulated) return; const select = document.getElementById('archiveSearchStreamer') as HTMLSelectElement | null; @@ -118,7 +109,7 @@ function renderArchiveSearchResults(result: ArchiveSearchResult): void { ${escapeHtml(date)}
${escapeHtml(hit.fileName)}
-
${escapeHtml(formatBytesForArchive(hit.size))}
+
${escapeHtml(formatBytes(hit.size))}
diff --git a/src/renderer-shared.ts b/src/renderer-shared.ts index 3d304f6..634d318 100644 --- a/src/renderer-shared.ts +++ b/src/renderer-shared.ts @@ -29,6 +29,20 @@ function applyHtml(el: HTMLElement, html: string): void { (el as unknown as Record)[key] = html; } +/* Generic file-size formatter for the renderer. Scales B -> KB -> MB + -> GB -> TB; returns '0 B' for zero / negative / non-finite input. + Used by the archive search results and the stats card. Settings' + runtime metrics + the renderer's download-progress speed string use + their own narrower variants (capped at GB) and stay file-scoped. */ +function formatBytes(bytes: number): string { + if (!Number.isFinite(bytes) || bytes <= 0) return '0 B'; + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + if (bytes < 1024 * 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; + return `${(bytes / (1024 * 1024 * 1024 * 1024)).toFixed(2)} TB`; +} + /* localStorage helpers — every renderer module that persists state was wrapping its get/set calls in the same try/catch idiom to handle environments where localStorage isn't writable (private-browsing diff --git a/src/renderer-stats.ts b/src/renderer-stats.ts index 7f0f919..fb3191d 100644 --- a/src/renderer-stats.ts +++ b/src/renderer-stats.ts @@ -38,12 +38,12 @@ function renderStatsSummary(stats: ArchiveStats): void { } const cards: Array<{ label: string; value: string; sub?: string }> = [ - { label: UI_TEXT.static.statsTotalRecordings, value: String(stats.liveCount + stats.vodCount), sub: formatBytesForStats(stats.liveBytes + stats.vodBytes) }, - { label: UI_TEXT.static.statsLiveRecordings, value: String(stats.liveCount), sub: formatBytesForStats(stats.liveBytes) }, - { label: UI_TEXT.static.statsVodRecordings, value: String(stats.vodCount), sub: formatBytesForStats(stats.vodBytes) }, + { label: UI_TEXT.static.statsTotalRecordings, value: String(stats.liveCount + stats.vodCount), sub: formatBytes(stats.liveBytes + stats.vodBytes) }, + { label: UI_TEXT.static.statsLiveRecordings, value: String(stats.liveCount), sub: formatBytes(stats.liveBytes) }, + { label: UI_TEXT.static.statsVodRecordings, value: String(stats.vodCount), sub: formatBytes(stats.vodBytes) }, { label: UI_TEXT.static.statsStreamers, value: String(stats.streamerCount) }, - { label: UI_TEXT.static.statsAvgSize, value: stats.avgRecordingSizeBytes > 0 ? formatBytesForStats(stats.avgRecordingSizeBytes) : '-' }, - { label: UI_TEXT.static.statsChatFiles, value: String(stats.chatCount), sub: formatBytesForStats(stats.chatBytes) } + { label: UI_TEXT.static.statsAvgSize, value: stats.avgRecordingSizeBytes > 0 ? formatBytes(stats.avgRecordingSizeBytes) : '-' }, + { label: UI_TEXT.static.statsChatFiles, value: String(stats.chatCount), sub: formatBytes(stats.chatBytes) } ]; applyHtml(grid, cards.map((c) => ` @@ -72,13 +72,13 @@ function renderStatsTopStreamers(top: ArchiveStatsTopStreamer[], totalBytes: num
${escapeHtml(s.streamer)} ${s.fileCount} ${escapeHtml(UI_TEXT.static.statsFiles)} - ${formatBytesForStats(s.bytes)} (${sharePct}%) + ${formatBytes(s.bytes)} (${sharePct}%)
${(s.liveBytes > 0 || s.vodBytes > 0) ? `
- ${s.liveBytes > 0 ? `LIVE ${formatBytesForStats(s.liveBytes)}` : ''} - ${s.vodBytes > 0 ? `VOD ${formatBytesForStats(s.vodBytes)}` : ''} + ${s.liveBytes > 0 ? `LIVE ${formatBytes(s.liveBytes)}` : ''} + ${s.vodBytes > 0 ? `VOD ${formatBytes(s.vodBytes)}` : ''}
` : ''}
@@ -103,7 +103,7 @@ function renderStatsActivity(days: ArchiveStatsDay[]): void { const bars = days.map((d, idx) => { const heightPct = Math.max(4, Math.round((d.count / maxCount) * 100)); - const tooltip = `${d.date}: ${d.count} ${UI_TEXT.static.statsFiles} - ${formatBytesForStats(d.bytes)}`; + const tooltip = `${d.date}: ${d.count} ${UI_TEXT.static.statsFiles} - ${formatBytes(d.bytes)}`; const showLabel = idx === 0 || idx === days.length - 1 || idx % 7 === 0; const dayLabel = showLabel ? d.date.slice(5) : ''; return ` @@ -122,7 +122,7 @@ function renderStatsActivity(days: ArchiveStatsDay[]): void {
${bars}
${escapeHtml(UI_TEXT.static.statsActivitySummary .replace('{count}', String(totalCount)) - .replace('{size}', formatBytesForStats(totalBytes)))}
+ .replace('{size}', formatBytes(totalBytes)))}
`); } @@ -142,7 +142,7 @@ function renderStatsSizeBuckets(buckets: ArchiveStatsBucket[]): void {
${escapeHtml(b.label)} - ${b.count} ${formatBytesForStats(b.bytes)} + ${b.count} ${formatBytes(b.bytes)}
@@ -152,14 +152,6 @@ function renderStatsSizeBuckets(buckets: ArchiveStatsBucket[]): void { }).join('')); } -function formatBytesForStats(bytes: number): string { - if (!Number.isFinite(bytes) || bytes <= 0) return '0 B'; - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; - if (bytes < 1024 * 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; - return `${(bytes / (1024 * 1024 * 1024 * 1024)).toFixed(2)} TB`; -} (window as unknown as { refreshArchiveStats: typeof refreshArchiveStats }).refreshArchiveStats = refreshArchiveStats;