export interface PlaylistSummary { id: string; title: string; role: string; persistent: boolean; cover_pk?: string | null; cover_url?: string | null; track_count: number; max_size?: number | null; default_ttl_secs?: number | null; last_change: string; } import type { AudioCacheMetadata } from "./audioCache"; export interface PlaylistTrack { cache_pk: string; added_at: string; ttl_secs?: number | null; lazy_pk?: string | null; metadata?: AudioCacheMetadata | null; cover_url?: string | null; cover_source?: string | null; } export interface PlaylistDetail { summary: PlaylistSummary; tracks: PlaylistTrack[]; } export interface ApiErrorPayload { error?: string; message?: string; } export interface CreatePlaylistPayload { id: string; title?: string; role?: string; cover_pk?: string; persistent?: boolean; max_size?: number; default_ttl_secs?: number; } export interface UpdatePlaylistPayload { title?: string; role?: string; max_size?: number | null; default_ttl_secs?: number | null; cover_pk?: string | null; } export interface AddTracksPayload { cache_pks: string[]; ttl_secs?: number; lazy?: boolean; } async function parseJsonOrThrow(response: Response): Promise { if (!response.ok) { let message = `HTTP ${response.status}`; try { const error: ApiErrorPayload = await response.json(); if (error?.message) { message = error.message; } } catch { // Ignore JSON parsing errors and keep default message } throw new Error(message); } return response.json() as Promise; } async function ensureSuccess(response: Response): Promise { if (!response.ok) { let message = `HTTP ${response.status}`; try { const error: ApiErrorPayload = await response.json(); if (error?.message) { message = error.message; } } catch { // ignore } throw new Error(message); } } export async function listPlaylists(): Promise { const response = await fetch("/api/playlists"); return parseJsonOrThrow(response); } export async function getPlaylistDetail(id: string): Promise { const response = await fetch(`/api/playlists/${encodeURIComponent(id)}`); return parseJsonOrThrow(response); } export async function createPlaylist(body: CreatePlaylistPayload): Promise { const response = await fetch("/api/playlists", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(body), }); return parseJsonOrThrow(response); } export async function updatePlaylist( id: string, body: UpdatePlaylistPayload ): Promise { const response = await fetch(`/api/playlists/${encodeURIComponent(id)}`, { method: "PATCH", headers: { "Content-Type": "application/json", }, body: JSON.stringify(body), }); return parseJsonOrThrow(response); } export async function deletePlaylist(id: string): Promise { const response = await fetch(`/api/playlists/${encodeURIComponent(id)}`, { method: "DELETE", }); await ensureSuccess(response); } export async function addTracksToPlaylist( id: string, payload: AddTracksPayload ): Promise { const response = await fetch(`/api/playlists/${encodeURIComponent(id)}/tracks`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }); return parseJsonOrThrow(response); } export async function flushPlaylist(id: string): Promise { const response = await fetch(`/api/playlists/${encodeURIComponent(id)}/tracks`, { method: "DELETE", }); return parseJsonOrThrow(response); } export async function removeTrackFromPlaylist(id: string, cachePk: string): Promise { const response = await fetch( `/api/playlists/${encodeURIComponent(id)}/tracks/${encodeURIComponent(cachePk)}`, { method: "DELETE", } ); return parseJsonOrThrow(response); }