diff --git a/api/api.go b/api/api.go
index 07f4aab..eece652 100644
--- a/api/api.go
+++ b/api/api.go
@@ -690,6 +690,7 @@ type TrackInfo struct {
PlayCount int `json:"play_count,omitempty"`
Tags []string `json:"tags,omitempty"`
ArtID uint32 `json:"art_id,omitempty"`
+ ArtChecked bool `json:"art_checked,omitempty"` // false with art_id 0 = never probed; the web UI requests art optimistically to trigger lazy extraction
FileMissing bool `json:"file_missing,omitempty"`
}
@@ -744,6 +745,7 @@ func (s *Server) libTrackToInfo(t *library.Track) TrackInfo {
ColorName: trackColorNames[t.ColorID],
PlayCount: t.PlayCount,
ArtID: t.ArtID,
+ ArtChecked: t.ArtChecked,
FileMissing: t.FileMissing,
}
if !t.DateAdded.IsZero() {
diff --git a/api/web/index.html b/api/web/index.html
index 0291e44..28d251e 100644
--- a/api/web/index.html
+++ b/api/web/index.html
@@ -5586,12 +5586,32 @@
}
// `sort` is the accessor used by the comparator. `sortable: false` opts a
// column out of header-click sorting (e.g. the waveform thumbnail).
+// artHTML renders a track's artwork . The request is made optimistically
+// for tracks whose file has never been probed (art_id 0 with art_checked
+// unset): GET /api/artwork/{id} is what triggers the server's lazy ffmpeg
+// extraction, so the first view of a newly added track pulls the art out of
+// the file instead of waiting for a CDJ load to analyze it. Tracks already
+// probed and found artless (art_checked true) go straight to the placeholder,
+// so there is no repeated 404 traffic; onerror degrades to the placeholder
+// for the artless-probe and concurrent-probe cases.
+function artHTML(t, imgClass, fbClass, fbText) {
+ if (t.art_id > 0 || !t.art_checked) {
+ return `
`;
+ }
+ return `