import { clampStoredLoudnessPreAnalysisAttenuationRefDb } from '../utils/audio/loudnessPreAnalysisSlider'; import { DEFAULT_LOUDNESS_PRE_ANALYSIS_ATTENUATION_DB, DEFAULT_LIBRARY_GRID_MAX_COLUMNS, LIBRARY_GRID_MAX_COLUMNS_MAX, LIBRARY_GRID_MAX_COLUMNS_MIN, LOUDNESS_LUFS_PRESETS, MIX_MIN_RATING_FILTER_MAX_STARS, RANDOM_MIX_SIZE_OPTIONS, } from './authStoreDefaults'; import type { LoudnessLufsPreset } from './authStoreTypes'; export function generateId(): string { return Date.now().toString(36) + Math.random().toString(36).slice(2); } export function sanitizeLoudnessLufsPreset(v: unknown, fallback: LoudnessLufsPreset): LoudnessLufsPreset { return (LOUDNESS_LUFS_PRESETS as readonly number[]).includes(v as number) ? (v as LoudnessLufsPreset) : fallback; } export function sanitizeLoudnessPreAnalysisFromStorage(v: unknown): number { const n = typeof v === 'number' ? v : Number(v); if (!Number.isFinite(n)) return DEFAULT_LOUDNESS_PRE_ANALYSIS_ATTENUATION_DB; return clampStoredLoudnessPreAnalysisAttenuationRefDb(n); } export function clampMixFilterMinStars(v: unknown): number { const n = typeof v === 'number' ? v : Number(v); if (!Number.isFinite(n)) return 0; return Math.max(0, Math.min(MIX_MIN_RATING_FILTER_MAX_STARS, Math.round(n))); } export function clampRandomMixSize(v: number): number { if (!Number.isFinite(v)) return 50; // Snap to the nearest allowed option so a tampered persisted value can't break the picker. let nearest = RANDOM_MIX_SIZE_OPTIONS[0]; let bestDelta = Math.abs(v - nearest); for (const opt of RANDOM_MIX_SIZE_OPTIONS) { const d = Math.abs(v - opt); if (d < bestDelta) { nearest = opt; bestDelta = d; } } return nearest; } /** Persisted max columns for library card grids (albums, artists, playlists, …). */ export function clampLibraryGridMaxColumns(v: unknown): number { const n = typeof v === 'number' ? v : Number(v); if (!Number.isFinite(n)) return DEFAULT_LIBRARY_GRID_MAX_COLUMNS; return Math.max(LIBRARY_GRID_MAX_COLUMNS_MIN, Math.min(LIBRARY_GRID_MAX_COLUMNS_MAX, Math.round(n))); } export function clampSkipStarThreshold(v: number): number { if (!Number.isFinite(v)) return 3; return Math.max(1, Math.min(99, Math.round(v))); } export function skipStarCountStorageKey(serverId: string | null | undefined, trackId: string): string { return `${serverId ?? ''}\u001f${trackId}`; } export function sanitizeSkipStarCounts(raw: unknown): Record { if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {}; const next: Record = {}; for (const [k, v] of Object.entries(raw as Record)) { const n = Number(v); if (Number.isFinite(n) && n > 0) next[k] = Math.min(Math.floor(n), 1_000_000); } return next; }