import { useSessionStore } from '@/stores/session-store' export type User = { id: string username: string isAdmin: boolean } export type Artist = { id: string name: string albumCount: number coverArtId: string } export type Album = { id: string artistId: string artistName: string title: string year: number trackCount: number coverArtId: string } export type Track = { id: string albumId: string artistId: string title: string artistName: string albumTitle: string trackNumber: number durationSeconds: number coverArtId?: string } export type ArtistDetail = Artist & { albums: Album[] } export type AlbumDetail = Album & { tracks: Track[] } export type SearchResults = { artists: Artist[] albums: Album[] tracks: Track[] } export type ScanStatus = { scanning: boolean startedAt?: string finishedAt?: string lastError?: string artists: number albums: number tracks: number } export type HomePayload = { recentAlbums: Album[] artists: Artist[] } export type PlaylistSummary = { id: string name: string comment: string public: boolean songCount: number durationSeconds: number createdAt: string updatedAt: string } export type PlaylistDetail = PlaylistSummary & { tracks: Track[] } const API_BASE = import.meta.env.VITE_API_BASE ?? 'http://localhost:4040' async function request(path: string, init?: RequestInit): Promise { const token = useSessionStore.getState().token const response = await fetch(`${API_BASE}${path}`, { headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(init?.headers ?? {}), }, ...init, }) if (!response.ok) { throw new Error(`Request failed: ${response.status}`) } if (response.status === 204) { return undefined as T } return response.json() as Promise } export async function login(username: string, password: string) { return request<{ token: string; user: User }>('/api/auth/login', { method: 'POST', body: JSON.stringify({ username, password }), }) } export async function fetchHome() { return request('/api/home') } export async function fetchArtists() { return request<{ items: Artist[] }>('/api/artists') } export async function fetchArtist(id: string) { return request(`/api/artists/${id}`) } export async function fetchAlbums() { return request<{ items: Album[] }>('/api/albums') } export async function fetchAlbum(id: string) { return request(`/api/albums/${id}`) } export async function fetchTracks() { return request<{ items: Track[] }>('/api/tracks') } export async function fetchTrack(id: string) { return request(`/api/tracks/${id}`) } export async function searchLibrary(query: string) { return request(`/api/search?q=${encodeURIComponent(query)}`) } export async function fetchScanStatus() { return request('/api/admin/scan-status') } export async function triggerScan() { return request('/api/admin/scan', { method: 'POST' }) } export async function fetchPlaylists() { return request<{ items: PlaylistSummary[] }>('/api/playlists') } export async function fetchPlaylist(id: string) { return request(`/api/playlists/${id}`) } export async function createPlaylist(input: { name: string comment?: string public?: boolean trackIds?: string[] }) { return request('/api/playlists', { method: 'POST', body: JSON.stringify(input), }) } export async function updatePlaylist( id: string, input: { name?: string comment?: string public?: boolean addTrackIds?: string[] removeTrackIds?: string[] }, ) { return request(`/api/playlists/${id}`, { method: 'PATCH', body: JSON.stringify(input), }) } export async function deletePlaylist(id: string) { await request(`/api/playlists/${id}`, { method: 'DELETE' }) } export function coverArtUrl(id: string) { const token = useSessionStore.getState().token return `${API_BASE}/api/cover-art/${id}${token ? `?token=${encodeURIComponent(token)}` : ''}` } export function streamUrl(id: string) { const token = useSessionStore.getState().token return `${API_BASE}/api/stream/${id}${token ? `?token=${encodeURIComponent(token)}` : ''}` }