From be0560865b1a0482c9877a33789d486cf51167f0 Mon Sep 17 00:00:00 2001 From: Christian Sarnataro Date: Thu, 3 Sep 2026 16:09:56 +0200 Subject: [PATCH 1/2] fix: added hours to playing time in seekbar At the moment if a song lasts more than 1 hour, the format of the playing time is 'mm:ss', e.g. '160:00'. Instead it should be 'hh:mm:ss', e.g. '02:40:00' --- src/lib/utils.ts | 25 ++++++++++++++++++------- src/ui/components/player/SeekBar.tsx | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 6e76d768..e2bf261d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -5,14 +5,25 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } +function pad(num: number) { + return String(num).padStart(2, "0"); +} + /** - * `m:ss` for a whole-second duration. Callers round `seconds` themselves (floor for elapsed + * Formats a whole-second duration as `hh:mm:ss` or just `mm:ss`, + * depending on the real duration (if greater or less than 1 hour). + * Callers round `seconds` themselves (floor for elapsed * position, ceil for a countdown) — this only formats what it's given. */ -export function formatMinutesSeconds(seconds: number): string { - if (!Number.isFinite(seconds)) return "0:00"; - const whole = Math.max(0, Math.floor(seconds)); - const mins = Math.floor(whole / 60); - const secs = whole % 60; - return `${mins}:${secs.toString().padStart(2, "0")}`; +export function formatMinutesSeconds(totalSeconds: number): string { + if (!Number.isFinite(totalSeconds)) return "0:00"; + + const hours = Math.floor(totalSeconds / 3600); + const mins = Math.floor((totalSeconds % 3600) / 60); + const seconds = Math.floor(totalSeconds % 60); + + if (hours > 0) { + return `${pad(hours)}:${pad(mins)}:${pad(seconds)}`; + } + return `${pad(mins)}:${pad(seconds)}`; } diff --git a/src/ui/components/player/SeekBar.tsx b/src/ui/components/player/SeekBar.tsx index eadaf6a2..b76464da 100644 --- a/src/ui/components/player/SeekBar.tsx +++ b/src/ui/components/player/SeekBar.tsx @@ -266,7 +266,7 @@ export function SeekBar() { } as React.CSSProperties} aria-label="Seek" /> - + {formatTime(duration)} From e5a5ce287447716271e553743788a6c6b6295005 Mon Sep 17 00:00:00 2001 From: Faizan Asad Date: Wed, 7 Oct 2026 20:31:33 +0500 Subject: [PATCH 2/2] fix: keep m:ss under an hour and clamp negatives Co-Authored-By: Claude Opus 5.5 --- src/lib/utils.check.ts | 16 +++++++++++++++ src/lib/utils.ts | 30 ++++++++++------------------ src/ui/components/player/SeekBar.tsx | 4 ++-- 3 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 src/lib/utils.check.ts diff --git a/src/lib/utils.check.ts b/src/lib/utils.check.ts new file mode 100644 index 00000000..9977baf4 --- /dev/null +++ b/src/lib/utils.check.ts @@ -0,0 +1,16 @@ +/** Self-check for `formatMinutesSeconds`, which every playback time in the app goes through. */ +export {}; + +import { formatMinutesSeconds } from "./utils"; + +function check(actual: string, expected: string, message: string): void { + if (actual !== expected) throw new Error(`FAILED: ${message} (got ${actual}, want ${expected})`); +} + +check(formatMinutesSeconds(225), "3:45", "under an hour stays m:ss, no leading zero"); +check(formatMinutesSeconds(59.9), "0:59", "fractions floor"); +check(formatMinutesSeconds(3725), "1:02:05", "an hour and up adds hours, padding minutes"); +check(formatMinutesSeconds(-1), "0:00", "a countdown that overshoots clamps to zero"); +check(formatMinutesSeconds(Number.NaN), "0:00", "an unknown duration reads as zero"); + +console.log("formatMinutesSeconds: ok"); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index e2bf261d..8c7fbfe1 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -5,25 +5,17 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } -function pad(num: number) { - return String(num).padStart(2, "0"); -} - /** - * Formats a whole-second duration as `hh:mm:ss` or just `mm:ss`, - * depending on the real duration (if greater or less than 1 hour). - * Callers round `seconds` themselves (floor for elapsed - * position, ceil for a countdown) — this only formats what it's given. + * `m:ss` for a whole-second duration, `h:mm:ss` from an hour up. Callers round `seconds` + * themselves (floor for elapsed position, ceil for a countdown) — this only formats what it's given. */ -export function formatMinutesSeconds(totalSeconds: number): string { - if (!Number.isFinite(totalSeconds)) return "0:00"; - - const hours = Math.floor(totalSeconds / 3600); - const mins = Math.floor((totalSeconds % 3600) / 60); - const seconds = Math.floor(totalSeconds % 60); - - if (hours > 0) { - return `${pad(hours)}:${pad(mins)}:${pad(seconds)}`; - } - return `${pad(mins)}:${pad(seconds)}`; +export function formatMinutesSeconds(seconds: number): string { + if (!Number.isFinite(seconds)) return "0:00"; + const whole = Math.max(0, Math.floor(seconds)); + const hours = Math.floor(whole / 3600); + const mins = Math.floor((whole % 3600) / 60); + const secs = (whole % 60).toString().padStart(2, "0"); + return hours > 0 + ? `${hours}:${mins.toString().padStart(2, "0")}:${secs}` + : `${mins}:${secs}`; } diff --git a/src/ui/components/player/SeekBar.tsx b/src/ui/components/player/SeekBar.tsx index b76464da..323872af 100644 --- a/src/ui/components/player/SeekBar.tsx +++ b/src/ui/components/player/SeekBar.tsx @@ -244,7 +244,7 @@ export function SeekBar() { return (
- + {formatTime(currentTime)} - + {formatTime(duration)}