124 lines
2.8 KiB
TypeScript
124 lines
2.8 KiB
TypeScript
// 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<T> {
|
|
document: T;
|
|
}
|
|
|
|
export interface SearchResponse {
|
|
found?: number;
|
|
hits?: SearchHit<Track>[];
|
|
[key: string]: unknown;
|
|
}
|