import { api, apiFetch } from './client'; // Loudness normalization preference (M464 #4998). One per user, stored on // the server so the web player, the Android app and casts agree. export type NormalizationMode = 'off' | 'auto' | 'track' | 'album'; export type NormalizationTarget = -18 | -16 | -14; export type NormalizationBoost = 'headroom' | 'limiter'; export type NormalizationPrefs = { mode: NormalizationMode; target_lufs: NormalizationTarget; boost: NormalizationBoost; }; // Must match library.DefaultNormalizationPrefs on the server. export const DEFAULT_NORMALIZATION: NormalizationPrefs = { mode: 'auto', target_lufs: -18, boost: 'headroom' }; export const NORMALIZATION_MODES: NormalizationMode[] = ['off', 'auto', 'track', 'album']; export const NORMALIZATION_TARGETS: NormalizationTarget[] = [-18, -16, -14]; export function isNormalizationPrefs(v: unknown): v is NormalizationPrefs { if (typeof v !== 'object' || v === null) return false; const p = v as Record; return ( NORMALIZATION_MODES.includes(p.mode as NormalizationMode) && NORMALIZATION_TARGETS.includes(p.target_lufs as NormalizationTarget) && (p.boost === 'headroom' || p.boost === 'limiter') ); } export function getNormalization(): Promise { return api.get('/api/me/normalization'); } // The whole preference is sent every time; the server keeps the last write. export function putNormalization(p: NormalizationPrefs): Promise { return api.put('/api/me/normalization', p); } // Per-track gains (#4997): dB to the -18 LUFS ReplayGain reference and linear // peaks. A field is absent until the server has measured it. export type ReplayGain = { track_gain?: number; track_peak?: number; album_gain?: number; album_peak?: number; }; /** * Gains for up to 200 tracks; an id missing from items has none yet. Given * a deadline: the player waits on it, and a request that never answers * would leave those tracks unleveled for the rest of the session. */ export function getReplayGain(ids: string[]): Promise<{ items: Record }> { return apiFetch(`/api/tracks/replay-gain?ids=${ids.map(encodeURIComponent).join(',')}`, { signal: AbortSignal.timeout(10_000) }) as Promise<{ items: Record }>; }