Show cover thumbnail + chapter count in match search results

Backend: After the parallel search, fetch get_release_details for the
top-3 MusicBrainz hits in parallel. MB's search response carries
neither cover_url nor tracklist, so without this nothing useful
would show for MB results. Other sources already include cover in
their search response and don't have chapter data anyway.

Adds chapterCount to every result (0 when unknown). For MB matches
that resolve to a release with a tracklist, this is the actual count
that would be created as Chapters on apply.

UI: Match results now render as a row with a 48px cover thumbnail on
the left, title + metadata in the middle, Apply button on the right.
Metadata line shows author, year, source, confidence, and chapter
count (highlighted in green when present). Broken cover URLs hide
gracefully via onError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Audiolib
2026-05-26 20:54:41 +02:00
parent edf057f36e
commit 7c8e98917d
2 changed files with 47 additions and 2 deletions

View File

@@ -283,13 +283,39 @@ export default function BookDetail() {
)}
{matchResults.map((r, i) => (
<div key={i} className="flex items-center gap-3 py-2 border-t border-divider hover:bg-card transition-colors">
<div
className="w-12 h-12 flex-shrink-0 rounded overflow-hidden flex items-center justify-center"
style={{ background: r.cover ? 'transparent' : '#222' }}
>
{r.cover ? (
<img
src={r.cover}
alt=""
className="w-full h-full object-cover"
referrerPolicy="no-referrer"
onError={(e) => { (e.currentTarget as HTMLImageElement).style.display = 'none' }}
/>
) : (
<span className="text-muted" style={{ fontSize: '9px' }}>—</span>
)}
</div>
<div className="flex-1 min-w-0">
<p className="text-ink truncate" style={{ fontSize: '13px' }}>{r.title}</p>
<p className="text-muted" style={{ fontSize: '11px' }}>{r.author} · {r.source} · {Math.round(r.confidence * 100)}%</p>
<p className="text-muted truncate" style={{ fontSize: '11px' }}>
{[
r.author,
r.publishYear,
r.source,
`${Math.round(r.confidence * 100)}%`,
].filter(Boolean).join(' · ')}
{r.chapterCount > 0 && (
<span className="text-primary"> · {r.chapterCount} Kapitel</span>
)}
</p>
</div>
<button
onClick={() => handleApplyMatch(r)}
className="text-xs bg-primary-dim text-primary px-3 py-1 rounded-lg hover:bg-primary hover:text-black transition-colors"
className="text-xs bg-primary-dim text-primary px-3 py-1 rounded-lg hover:bg-primary hover:text-black transition-colors flex-shrink-0"
>
Anwenden
</button>