mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
refactor(format): consolidate duration formatters into format/formatDuration (Phase L, part 2) (#690)
The mm:ss track-time formatter was hand-rolled in 11 places and the h:mm:ss total-duration formatter in 4 — extract two tested functions: - formatTrackTime(seconds, fallback='0:00') — m:ss, used for track / playback times. fallback param covers the '–' placeholder rows. - formatLongDuration(seconds) — h:mm:ss when >=1h, else m:ss, used for album / queue totals. Behaviour preserved per call site: the unified guard (!seconds || !isFinite || <0 -> fallback) produces identical output to every prior variant for all real inputs; SongRow keeps its '–' via the fallback arg. Removes the formatter exports from 6 componentHelpers files (playerBarHelpers / fullscreenPlayerHelpers deleted — they only exported the formatter) and 7 inline component copies. + formatDuration.test.ts
This commit is contained in:
committed by
GitHub
parent
7a7a9f5e6b
commit
5231169a71
@@ -1,13 +1,5 @@
|
||||
import type { ColDef } from '../useTracklistColumns';
|
||||
|
||||
export function formatDuration(seconds: number): string {
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
export function codecLabel(song: { suffix?: string; bitRate?: number }, showBitrate: boolean): string {
|
||||
const parts: string[] = [];
|
||||
if (song.suffix) parts.push(song.suffix.toUpperCase());
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
export function formatDuration(seconds: number): string {
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = seconds % 60;
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/** Strip dangerous tags/attributes from server-provided HTML */
|
||||
export function sanitizeHtml(html: string): string {
|
||||
const parser = new DOMParser();
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export function formatTime(seconds: number): string {
|
||||
if (!seconds || isNaN(seconds)) return '0:00';
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
@@ -73,10 +73,3 @@ export function initialSnapshot(): MiniSyncPayload {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function fmt(seconds: number): string {
|
||||
if (!isFinite(seconds) || seconds < 0) seconds = 0;
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export function formatTime(seconds: number): string {
|
||||
if (!seconds || isNaN(seconds)) return '0:00';
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
@@ -9,12 +9,6 @@ export function sanitizeFilename(name: string): string {
|
||||
.substring(0, 200) || 'download';
|
||||
}
|
||||
|
||||
export function formatDuration(seconds: number): string {
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = seconds % 60;
|
||||
return `${m}:${String(s).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
export function formatSize(bytes?: number): string {
|
||||
if (!bytes) return '';
|
||||
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
||||
|
||||
@@ -4,13 +4,6 @@ import type { Track } from '../../store/playerStoreTypes';
|
||||
|
||||
export type DurationMode = 'total' | 'remaining' | 'eta';
|
||||
|
||||
export function formatTime(seconds: number): string {
|
||||
if (!seconds || isNaN(seconds)) return '0:00';
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
export function formatQueueReplayGainParts(track: Track, t: TFunction): string[] {
|
||||
const parts: string[] = [];
|
||||
const fmtDb = (db: number) => `${db >= 0 ? '+' : ''}${db.toFixed(1)}`;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { formatTrackTime, formatLongDuration } from './formatDuration';
|
||||
|
||||
describe('formatTrackTime', () => {
|
||||
it('formats m:ss with zero-padded seconds', () => {
|
||||
expect(formatTrackTime(5)).toBe('0:05');
|
||||
expect(formatTrackTime(65)).toBe('1:05');
|
||||
expect(formatTrackTime(599)).toBe('9:59');
|
||||
});
|
||||
|
||||
it('does not roll minutes into hours (used for short track times)', () => {
|
||||
expect(formatTrackTime(3600)).toBe('60:00');
|
||||
expect(formatTrackTime(3661)).toBe('61:01');
|
||||
});
|
||||
|
||||
it('floors fractional seconds', () => {
|
||||
expect(formatTrackTime(90.9)).toBe('1:30');
|
||||
});
|
||||
|
||||
it('returns the fallback for zero / non-finite / negative input', () => {
|
||||
expect(formatTrackTime(0)).toBe('0:00');
|
||||
expect(formatTrackTime(NaN)).toBe('0:00');
|
||||
expect(formatTrackTime(Infinity)).toBe('0:00');
|
||||
expect(formatTrackTime(-5)).toBe('0:00');
|
||||
expect(formatTrackTime(undefined as unknown as number)).toBe('0:00');
|
||||
});
|
||||
|
||||
it('honours a custom fallback', () => {
|
||||
expect(formatTrackTime(0, '–')).toBe('–');
|
||||
expect(formatTrackTime(NaN, '–')).toBe('–');
|
||||
expect(formatTrackTime(42, '–')).toBe('0:42');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatLongDuration', () => {
|
||||
it('formats m:ss below one hour', () => {
|
||||
expect(formatLongDuration(5)).toBe('0:05');
|
||||
expect(formatLongDuration(125)).toBe('2:05');
|
||||
expect(formatLongDuration(3599)).toBe('59:59');
|
||||
});
|
||||
|
||||
it('formats h:mm:ss at or above one hour', () => {
|
||||
expect(formatLongDuration(3600)).toBe('1:00:00');
|
||||
expect(formatLongDuration(3661)).toBe('1:01:01');
|
||||
expect(formatLongDuration(7325)).toBe('2:02:05');
|
||||
});
|
||||
|
||||
it('returns 0:00 for zero / non-finite / negative input', () => {
|
||||
expect(formatLongDuration(0)).toBe('0:00');
|
||||
expect(formatLongDuration(NaN)).toBe('0:00');
|
||||
expect(formatLongDuration(-1)).toBe('0:00');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* `m:ss` track time from a seconds value. Non-positive / non-finite input
|
||||
* returns `fallback` (default `'0:00'`; pass e.g. `'–'` for placeholder rows).
|
||||
*/
|
||||
export function formatTrackTime(seconds: number, fallback = '0:00'): string {
|
||||
if (!seconds || !isFinite(seconds) || seconds < 0) return fallback;
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* `h:mm:ss` when the duration reaches an hour, otherwise `m:ss`. Used for
|
||||
* album / queue totals. Non-positive / non-finite input returns `'0:00'`.
|
||||
*/
|
||||
export function formatLongDuration(seconds: number): string {
|
||||
if (!seconds || !isFinite(seconds) || seconds < 0) return '0:00';
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
Reference in New Issue
Block a user