function formatCompactNumber(value: number, fractionDigits: number): string { return value.toFixed(fractionDigits).replace(/\.0$/, '') } function getFractionDigits(value: number, unitIndex: number): number { if (unitIndex === 0 || value >= 100) return 0 return 1 } export function formatHumanBytes(bytes: number): string { if (!Number.isFinite(bytes)) return '--' const units = ['B', 'KB', 'MB', 'GB', 'TB'] const sign = bytes < 0 ? '-' : '' let value = Math.abs(bytes) let unitIndex = 0 while (value >= 1024 && unitIndex < units.length - 1) { value /= 1024 unitIndex += 1 } return `${sign}${formatCompactNumber(value, getFractionDigits(value, unitIndex))} ${units[unitIndex]}` } export function formatHumanSpeed(bytesPerSecond: number): string { if (!Number.isFinite(bytesPerSecond) || bytesPerSecond < 0) return '--' return `${formatHumanBytes(bytesPerSecond)}/s` } export function formatDurationSeconds(seconds: number): string { if (!Number.isFinite(seconds) || seconds < 0) return '计算中' const wholeSeconds = Math.max(1, Math.round(seconds)) if (wholeSeconds < 60) { return `${wholeSeconds} 秒` } const minutes = Math.floor(wholeSeconds / 60) const remainSeconds = wholeSeconds % 60 if (minutes < 60) { return remainSeconds > 0 ? `${minutes} 分 ${remainSeconds} 秒` : `${minutes} 分` } const hours = Math.floor(minutes / 60) const remainMinutes = minutes % 60 return remainMinutes > 0 ? `${hours} 小时 ${remainMinutes} 分` : `${hours} 小时` } export function formatRemainingTime(remainingBytes: number, bytesPerSecond: number): string { if (!Number.isFinite(remainingBytes) || remainingBytes <= 0) return '剩余 0 秒' if (!Number.isFinite(bytesPerSecond) || bytesPerSecond <= 0) return '剩余时间计算中' return `剩余 ${formatDurationSeconds(remainingBytes / bytesPerSecond)}` } export function formatTransferProgressSummary(params: { speedBytesPerSecond: number loadedBytes?: number totalBytes?: number includeRemaining?: boolean }): string { const { speedBytesPerSecond, loadedBytes, totalBytes, includeRemaining = true, } = params const speedText = formatHumanSpeed(speedBytesPerSecond) const hasTotal = Number.isFinite(totalBytes) && (totalBytes || 0) > 0 const safeLoaded = Math.max(0, loadedBytes || 0) const safeTotal = hasTotal ? Math.max(0, totalBytes || 0) : 0 if (!hasTotal) { return speedText } const progressText = `${formatHumanBytes(Math.min(safeLoaded, safeTotal))}/${formatHumanBytes(safeTotal)}` if (!includeRemaining) { return `${speedText} - ${progressText}` } const remainingText = formatRemainingTime(safeTotal - safeLoaded, speedBytesPerSecond) return `${speedText} - ${progressText},${remainingText}` }