202 lines
4.3 KiB
TypeScript
202 lines
4.3 KiB
TypeScript
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<T>(path: string, init?: RequestInit): Promise<T> {
|
|
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<T>
|
|
}
|
|
|
|
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<HomePayload>('/api/home')
|
|
}
|
|
|
|
export async function fetchArtists() {
|
|
return request<{ items: Artist[] }>('/api/artists')
|
|
}
|
|
|
|
export async function fetchArtist(id: string) {
|
|
return request<ArtistDetail>(`/api/artists/${id}`)
|
|
}
|
|
|
|
export async function fetchAlbums() {
|
|
return request<{ items: Album[] }>('/api/albums')
|
|
}
|
|
|
|
export async function fetchAlbum(id: string) {
|
|
return request<AlbumDetail>(`/api/albums/${id}`)
|
|
}
|
|
|
|
export async function fetchTracks() {
|
|
return request<{ items: Track[] }>('/api/tracks')
|
|
}
|
|
|
|
export async function fetchTrack(id: string) {
|
|
return request<Track>(`/api/tracks/${id}`)
|
|
}
|
|
|
|
export async function searchLibrary(query: string) {
|
|
return request<SearchResults>(`/api/search?q=${encodeURIComponent(query)}`)
|
|
}
|
|
|
|
export async function fetchScanStatus() {
|
|
return request<ScanStatus>('/api/admin/scan-status')
|
|
}
|
|
|
|
export async function triggerScan() {
|
|
return request<ScanStatus>('/api/admin/scan', { method: 'POST' })
|
|
}
|
|
|
|
export async function fetchPlaylists() {
|
|
return request<{ items: PlaylistSummary[] }>('/api/playlists')
|
|
}
|
|
|
|
export async function fetchPlaylist(id: string) {
|
|
return request<PlaylistDetail>(`/api/playlists/${id}`)
|
|
}
|
|
|
|
export async function createPlaylist(input: {
|
|
name: string
|
|
comment?: string
|
|
public?: boolean
|
|
trackIds?: string[]
|
|
}) {
|
|
return request<PlaylistDetail>('/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<PlaylistDetail>(`/api/playlists/${id}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify(input),
|
|
})
|
|
}
|
|
|
|
export async function deletePlaylist(id: string) {
|
|
await request<void>(`/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)}` : ''}`
|
|
}
|