feat(library): surface metadata enrichment status, errors and covers
Docker Build & Publish / build (push) Has been cancelled
Docker Build & Publish / push (push) Has been cancelled
Docker Build & Publish / Prune old image versions (push) Has been cancelled

The mapper dropped metadata_status and hardcoded availability, so enrichment
state was invisible and a just-uploaded track never appeared to change. Map
metadata_status/metadata_error/has_cover onto Track; add MetadataStatusBadge
(pending spinner / enriched / failed-with-reason / manual) shown in TrackRow,
and serve token-bearing track covers via getTrackCoverUrl.

UploadPage now polls each uploaded track (stops once enrichment settles) so the
resolved title/artist — or a failure reason — appears live. i18n in en + ru.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Senko-san
2026-06-13 13:29:22 +03:00
parent facc215450
commit a37c19fd45
8 changed files with 198 additions and 11 deletions
+15 -3
View File
@@ -1,11 +1,12 @@
import { Row } from '@olly/modern-sk';
import { TrackContextMenu } from './TrackContextMenu';
import { AvailabilityBadge } from './AvailabilityBadge';
import { MetadataStatusBadge } from './MetadataStatusBadge';
import { formatDuration } from '../../lib/format';
import { useAppDispatch, useAppSelector } from '../../hooks/useAppDispatch';
import { play } from '../../store/slices/player';
import type { Track } from '../../api/types';
import { getCoverUrl } from '../../api/endpoints/streaming';
import { getCoverUrl, getTrackCoverUrl } from '../../api/endpoints/streaming';
interface Props {
track: Track;
@@ -27,8 +28,13 @@ export function TrackRow({
const dispatch = useAppDispatch();
const currentTrackId = useAppSelector((s) => s.player.currentTrackId);
const isPlaying = useAppSelector((s) => s.player.isPlaying);
const token = useAppSelector((s) => s.auth.accessToken);
const isActive = currentTrackId === track.id;
const artUrl = getCoverUrl(track.albumArtUrl);
// Prefer an explicit album art URL; otherwise serve the track's own cover
// (needs the token in the query string — `<img>` can't send a header).
const artUrl =
getCoverUrl(track.albumArtUrl) ??
(token ? getTrackCoverUrl(track.id, token, track.hasCover) : undefined);
return (
<Row
@@ -95,7 +101,13 @@ export function TrackRow({
{showAlbum && ` · ${track.albumTitle}`}
</div>
</div>
<AvailabilityBadge availability={track.availability} />
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<MetadataStatusBadge
status={track.metadataStatus}
error={track.metadataError}
/>
<AvailabilityBadge availability={track.availability} />
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<span
style={{