/** * Duration formatting utilities. * * Durations are milliseconds — the app's neutral time unit. The backend has * already converted any provider unit (e.g. Jellyfin ticks) before it reaches * the frontend, so no tick arithmetic lives here. */ /** * Convert a millisecond duration to a formatted string. * @param ms Duration in milliseconds * @param format Format type: "mm:ss" (default) or "hh:mm:ss" * @returns Formatted duration string or empty string if no duration */ export function formatDuration( ms?: number | null, format: "mm:ss" | "hh:mm:ss" | "h m" = "mm:ss", ): string { if (!ms) return ""; const totalSeconds = Math.floor(ms / 1000); // "1h 23m" / "45m" — the shape a runtime is read at a glance, as opposed to // the clock shape a *position* is read at. Three components had hand-rolled // this identically; it belongs here with the other two. if (format === "h m") { const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`; } if (format === "hh:mm:ss") { const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; return `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`; } // Default "mm:ss" format const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; return `${minutes}:${seconds.toString().padStart(2, "0")}`; }