diff --git a/internal/api/types.go b/internal/api/types.go index 565a5257..44444203 100644 --- a/internal/api/types.go +++ b/internal/api/types.go @@ -24,3 +24,71 @@ type UserView struct { Username string `json:"username"` IsAdmin bool `json:"is_admin"` } + +// Page is the generic pagination envelope used by list and search endpoints. +// Items is always non-nil at JSON encode time — handlers must initialize +// empty slices explicitly so absent results render as [] rather than null. +type Page[T any] struct { + Items []T `json:"items"` + Total int `json:"total"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} + +// ArtistRef is the lightweight artist shape used in lists and search results. +type ArtistRef struct { + ID string `json:"id"` + Name string `json:"name"` + AlbumCount int `json:"album_count"` +} + +// AlbumRef is the lightweight album shape used in lists, artist details, +// and search results. CoverURL points to /api/albums/{id}/cover which +// Plan 3 implements. +type AlbumRef struct { + ID string `json:"id"` + Title string `json:"title"` + ArtistID string `json:"artist_id"` + ArtistName string `json:"artist_name"` + Year int `json:"year,omitempty"` + TrackCount int `json:"track_count"` + DurationSec int `json:"duration_sec"` + CoverURL string `json:"cover_url"` +} + +// TrackRef is the lightweight track shape used in album details and search. +// StreamURL points to /api/tracks/{id}/stream which Plan 3 implements. +type TrackRef struct { + ID string `json:"id"` + Title string `json:"title"` + AlbumID string `json:"album_id"` + AlbumTitle string `json:"album_title"` + ArtistID string `json:"artist_id"` + ArtistName string `json:"artist_name"` + TrackNumber int `json:"track_number,omitempty"` + DiscNumber int `json:"disc_number,omitempty"` + DurationSec int `json:"duration_sec"` + StreamURL string `json:"stream_url"` +} + +// ArtistDetail is the response body of GET /api/artists/{id}. Embeds the ref +// so callers read id/name/album_count from the same path as list entries. +type ArtistDetail struct { + ArtistRef + Albums []AlbumRef `json:"albums"` +} + +// AlbumDetail is the response body of GET /api/albums/{id}. +type AlbumDetail struct { + AlbumRef + Tracks []TrackRef `json:"tracks"` +} + +// SearchResponse is the body of GET /api/search. Each facet carries its own +// total + limit + offset; the client sends one shared limit/offset and the +// server applies it uniformly to each facet's LIMIT/OFFSET query. +type SearchResponse struct { + Artists Page[ArtistRef] `json:"artists"` + Albums Page[AlbumRef] `json:"albums"` + Tracks Page[TrackRef] `json:"tracks"` +}