The catalog surface now speaks milliseconds, the app's neutral time unit.
Ticks no longer reach library/home components.
Rust:
- UserData gains playback_position_ms (dual-carry), populated from ticks
at the offline mapping seam via domain::ticks_to_ms.
Frontend:
- formatDuration(duration.ts) and the two local copies now take ms, not
ticks; all callers pass item.durationMs.
- Progress bars (EpisodeRow, EpisodeFocusView, MediaCard, LibraryListView)
compute playbackPositionMs / durationMs — unit-consistent, no tick math.
- PlaylistDetailView totalDuration sums durationMs.
- duration.test.ts + TrackList.test.ts fixtures updated to ms.
Deferred: player/session/reporting tick math (Queue, SessionCard,
RemoteControls, playbackReporting, playerEvents) — those cross the
storage/Jellyfin command boundary in ticks and need command-signature
changes (phase 3b). Display {item.type} badge -> kind label (phase 4).
Rust 456, frontend 644, check + check:boundary clean.
54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
/**
|
|
* 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" = "mm:ss"): string {
|
|
if (!ms) return "";
|
|
|
|
const totalSeconds = Math.floor(ms / 1000);
|
|
|
|
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")}`;
|
|
}
|
|
|
|
/**
|
|
* Convert seconds to formatted duration string
|
|
* @param seconds Duration in seconds
|
|
* @param format Format type: "mm:ss" (default) or "hh:mm:ss"
|
|
* @returns Formatted duration string
|
|
*/
|
|
export function formatSecondsDuration(seconds: number, format: "mm:ss" | "hh:mm:ss" = "mm:ss"): string {
|
|
if (format === "hh:mm:ss") {
|
|
const hours = Math.floor(seconds / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
const secs = seconds % 60;
|
|
|
|
return `${hours}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
|
}
|
|
|
|
// Default "mm:ss" format
|
|
const minutes = Math.floor(seconds / 60);
|
|
const secs = seconds % 60;
|
|
return `${minutes}:${secs.toString().padStart(2, "0")}`;
|
|
}
|