Files
mcma-webui/src/api/types.ts
T
Senko-san 8a70f478c3 feat: track info drawer (Get Info-style)
Add a right-side track info drawer that sits to the right of the queue
panel when both are open. Shows a large cover, title/artist/album links,
a Play/Queue/Edit actions row, and Status/General/File/Identifiers
sections (empty rows omitted). Opens from the track context menu, the
player now-playing tile, and the queue now-playing card.

- ui slice: trackInfoId + open/closeTrackInfo
- TrackInfoDrawer rendered after QueuePanel in AppShell; overlays content
  on narrow viewports
- map source/createdAt/enrichedAt from the wire (were unmapped)
- formatDateTime helper, info icon, i18n (en/ru)
- drop orphaned toggleNowPlaying/isNowPlayingOpen from player slice

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 14:02:38 +03:00

164 lines
3.5 KiB
TypeScript

export type TrackAvailability = 'server' | 'downloading' | 'error' | 'missing';
/**
* Metadata-enrichment state, distinct from file `availability`. `pending` = the
* worker hasn't finished (or hasn't started); `enriched` = identity found;
* `failed` = no match / a worker error (see `metadataError`); `manual` = user-
* edited and never auto-overwritten.
*/
export type MetadataStatus = 'pending' | 'enriched' | 'failed' | 'manual';
export interface Track {
id: string;
title: string;
artistId: string;
artistName: string;
albumId: string;
albumTitle: string;
albumArtUrl?: string;
hasCover: boolean;
durationMs: number;
trackNumber?: number;
discNumber?: number;
year?: number;
genre?: string;
availability: TrackAvailability;
metadataStatus: MetadataStatus;
/** Human-readable reason the last enrichment run set `failed`; else undefined. */
metadataError?: string;
fileSize?: number;
format?: string;
bitrate?: number;
liked: boolean;
/** Where the track entered the library (e.g. `upload`, `local_folder`). */
source?: string;
/** ISO timestamp the track was added to the library. */
createdAt?: string;
/** ISO timestamp the last successful enrichment ran; undefined if never. */
enrichedAt?: string;
}
export interface Album {
id: string;
title: string;
artistId: string;
artistName: string;
artUrl?: string;
year?: number;
trackCount: number;
totalDurationMs: number;
genre?: string;
}
export interface Artist {
id: string;
name: string;
artUrl?: string;
albumCount: number;
trackCount: number;
}
export interface Playlist {
id: string;
name: string;
description?: string;
ownerId: string;
trackCount: number;
totalDurationMs: number;
artUrl?: string;
isPublic: boolean;
createdAt: string;
updatedAt: string;
}
export interface PlaylistTrack extends Track {
position: number;
addedAt: string;
}
export interface DownloadJob {
id: string;
url: string;
title?: string;
artist?: string;
album?: string;
status: 'queued' | 'downloading' | 'processing' | 'done' | 'error';
progress: number;
errorMessage?: string;
trackId?: string;
createdAt: string;
updatedAt: string;
}
export interface UploadResponse {
track_id: string;
title: string;
already_exists: boolean;
}
export interface StorageStats {
totalBytes: number;
usedBytes: number;
trackCount: number;
albumCount: number;
artistCount: number;
}
export interface User {
id: string;
username: string;
email?: string;
role: 'admin' | 'user';
createdAt: string;
lastActiveAt?: string;
}
export interface AuthTokens {
accessToken: string;
refreshToken: string;
// Optional: the backend's TokenResponse carries no TTL — expiry is driven by
// 401→refresh, not a client-side clock. Present only if a backend supplies it.
expiresIn?: number;
}
export interface LoginRequest {
username: string;
password: string;
}
export interface LoginResponse {
user: User;
tokens: AuthTokens;
}
export interface RegisterRequest {
username: string;
password: string;
}
export interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
pageSize: number;
hasMore: boolean;
}
export interface LibraryFilters {
search?: string;
genre?: string;
artistId?: string;
albumId?: string;
liked?: boolean;
page?: number;
pageSize?: number;
sortBy?: 'title' | 'artist' | 'album' | 'year' | 'dateAdded';
sortOrder?: 'asc' | 'desc';
}
export interface ApiError {
status: number;
message: string;
code?: string;
}