// Shared TypeScript types mirroring the backend (backend/src/services/db.service.ts). // Single-user app: the backend defaults the user when no `x-user-id` header is sent, // so the frontend currently omits the header. Add it here later for multi-user. export type TrackState = | 'LIBRARY' | 'RECOMMENDED' | 'HIDDEN' | 'MISSING' | 'DELETED'; export type SourceType = 'MANUAL' | 'RECOMMENDATION'; export interface TrackArtist { id: string; name: string; role: 'main' | 'featured'; } export interface Track { id: string; path: string; hash: string; title: string; artist: string; album_id: string; duration: number; state: TrackState | string; play_count: number; skip_count: number; dislike_count: number; last_played_at?: string | null; mtime?: number | null; source_type: SourceType | string; artists?: TrackArtist[]; artwork_id?: string | null; // album cover art } export interface Artist { id: string; name: string; mbid?: string | null; discogs_id?: string | null; image_path?: string | null; } export interface Album { id: string; artist_id: string; title: string; year?: number | null; artwork_id?: string | null; artist_name?: string | null; } export interface ArtistWithAlbums extends Artist { albums: Album[]; } export interface AlbumWithTracks extends Album { tracks: Track[]; } // Genres are listed via GET /api/genres (with track_count), fetched individually // via GET /api/genres/:id, and their tracks via GET /api/genres/:id/tracks. export interface Genre { id: string; name: string; parent_id?: string | null; track_count?: number; } export interface HealthResponse { postgres: 'ok' | 'error' | 'unknown'; redis: 'ok' | 'error' | 'unknown'; } // Active durable recommendation session. The sessionId comes from // POST /api/v2/vibe/sessions and identifies its persisted plan. export interface VibeSession { sessionId: string; seedTrackId: string | null; } // A row from GET /api/history: a Track plus playback metadata. export interface HistoryEntry extends Track { history_id: string; batch_id: string | null; played_at: string; completed: boolean; } export interface DislikeEntry { track_id: string; disliked_at: string; warned_at: string | null; deleted_at: string | null; grace_hours: number; state: 'HIDDEN' | 'WARNED' | 'DELETED' | string; track_title: string; track_artist: string; track_path: string; } export const FEEDBACK_ACTIONS = [ 'promoted', 'disliked', 'skipped', 'deleted_permanent', ] as const; export type FeedbackAction = (typeof FEEDBACK_ACTIONS)[number]; // Typesense-style search response surfaced by GET /api/search?q=. export interface SearchHit { document: T; } export interface SearchResponse { found?: number; hits?: SearchHit[]; [key: string]: unknown; }