export interface NowPlayingResponse { success: boolean; playingItem: PlaylistItem; isPaused: boolean; volume: number; isIdle: boolean; currentFile: string; } export interface Metadata { title?: string; description?: string; siteName?: string; } export interface PlaylistItem { filename: string; title: string | null; id: number; playing: boolean | null; metadata?: Metadata; } export const getDisplayTitle = (item: PlaylistItem): string => { return item.title || item.metadata?.title || item.filename; } export interface MetadataUpdateEvent { event: 'metadata_update'; data: { url: string; metadata: Metadata; }; } export interface SearchResult { type: string; title: string; author: string; mediaUrl: string; thumbnailUrl: string; } export interface SearchResponse { success: boolean; results: SearchResult[]; } export enum ServerEvent { PlaylistUpdate = "playlist_update", NowPlayingUpdate = "now_playing_update", VolumeUpdate = "volume_update", FavoritesUpdate = "favorites_update", MetadataUpdate = "metadata_update", MPDUpdate = "mpd_update", } export const API = { async getPlaylist(): Promise { const response = await fetch('/api/playlist'); return response.json(); }, async addToPlaylist(url: string): Promise { await fetch('/api/playlist', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ url }), }); }, async replaceCurrentFile(url: string): Promise { await fetch('/api/playlist/replace', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ url }), }); }, async removeFromPlaylist(index: number): Promise { await fetch(`/api/playlist/${index}`, { method: 'DELETE', }); }, async play(): Promise { await fetch('/api/play', { method: 'POST' }); }, async pause(): Promise { await fetch('/api/pause', { method: 'POST' }); }, async skip(): Promise { await fetch('/api/skip', { method: 'POST' }); }, async skipTo(index: number): Promise { await fetch(`/api/skip/${index}`, { method: 'POST' }); }, async previous(): Promise { await fetch('/api/previous', { method: 'POST' }); }, async getNowPlaying(): Promise { const response = await fetch('/api/nowplaying'); return response.json(); }, async setVolume(volume: number): Promise { await fetch('/api/volume', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ volume }), }); }, async search(query: string): Promise { const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`); return response.json(); }, subscribeToEvents(onMessage: (event: any) => void): WebSocket { const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; const ws = new WebSocket(`${protocol}://${window.location.host}/api/events`); ws.onmessage = (event) => { onMessage(JSON.parse(event.data)); }; return ws; }, async getFavorites(): Promise { const response = await fetch('/api/favorites'); return response.json(); }, async addToFavorites(url: string): Promise { // Maybe a little weird to make an empty PlaylistItem here, but we do want to support adding // known PlaylistItems to favorites in the future. const playlistItem: PlaylistItem = { filename: url, title: null, id: 0, playing: null, metadata: undefined, }; await fetch('/api/favorites', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(playlistItem), }); }, async removeFromFavorites(id: number): Promise { await fetch(`/api/favorites/${id}`, { method: 'DELETE' }); }, async clearFavorites(): Promise { await fetch('/api/favorites', { method: 'DELETE' }); } };