From 442ced3dbf77a457b5a641f25b315a846a1b5343 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Mon, 8 Jun 2026 09:40:27 -0700 Subject: [PATCH] Jellyfin/Emby: populate album thumb_url during the library scan (root cause of "flags every album") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JellyfinArtist sets self.thumb (→ artists.thumb_url on scan); JellyfinAlbum never did. So for Jellyfin/Emby the library scan read getattr(album, 'thumb', None) == None and albums.thumb_url stayed empty for the WHOLE library. Two downstream effects: blank album art in the web UI, and the Cover Art Filler flagging every album as "missing cover art" (db_missing = empty thumb_url) — making the filler the only thing that ever populated the column, which is backwards. It should come from the scan, like artist thumbs do. Fix: JellyfinAlbum.thumb = /Items//Images/Primary (same shape as the artist thumb, which already normalizes + displays via fix_artist_image_url). Now the scan stores album thumbs, the UI shows art, and the filler only flags albums that are genuinely missing art. Navidrome + Plex already set album thumbs. Tests: album exposes the Primary-image thumb, None without an id, same shape as the artist thumb. --- core/jellyfin_client.py | 16 +++++++++++++-- tests/test_jellyfin_album_thumb.py | 31 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/test_jellyfin_album_thumb.py diff --git a/core/jellyfin_client.py b/core/jellyfin_client.py index 4937c2c0..8ee4eb74 100644 --- a/core/jellyfin_client.py +++ b/core/jellyfin_client.py @@ -65,7 +65,19 @@ class JellyfinAlbum: self.title = jellyfin_data.get('Name', 'Unknown Album') self.addedAt = self._parse_date(jellyfin_data.get('DateCreated')) self._artist_id = jellyfin_data.get('AlbumArtists', [{}])[0].get('Id', '') if jellyfin_data.get('AlbumArtists') else '' - + # Album cover image, mirroring JellyfinArtist.thumb — so the library + # scan stores albums.thumb_url instead of leaving it empty. Without + # this, EVERY album reads back with no thumb, the web UI shows blank + # art, and the Cover Art Filler flags the entire library as "missing + # cover art" (it became the only thing populating the column). + self.thumb = self._get_album_image_url() + + def _get_album_image_url(self) -> Optional[str]: + """Jellyfin/Emby album primary image URL (same shape as the artist one).""" + if not self.ratingKey: + return None + return f"/Items/{self.ratingKey}/Images/Primary" + def _parse_date(self, date_str: Optional[str]) -> Optional[datetime]: if not date_str: return None @@ -73,7 +85,7 @@ class JellyfinAlbum: return datetime.fromisoformat(date_str.replace('Z', '+00:00')) except: return None - + def artist(self) -> Optional[JellyfinArtist]: """Get the album artist""" if self._artist_id: diff --git a/tests/test_jellyfin_album_thumb.py b/tests/test_jellyfin_album_thumb.py new file mode 100644 index 00000000..f04c3d21 --- /dev/null +++ b/tests/test_jellyfin_album_thumb.py @@ -0,0 +1,31 @@ +"""JellyfinAlbum must expose a cover-image thumb so the library scan stores +albums.thumb_url (mirroring JellyfinArtist). Without it the whole library reads +back with empty album thumbs — blank art in the UI + the Cover Art Filler +flagging every album as "missing cover art". +""" + +from __future__ import annotations + +from core.jellyfin_client import JellyfinAlbum, JellyfinArtist + + +class _Client: + pass + + +def test_album_has_primary_image_thumb(): + alb = JellyfinAlbum({'Id': 'abc123', 'Name': 'For You'}, _Client()) + assert alb.thumb == '/Items/abc123/Images/Primary' + + +def test_album_thumb_none_without_id(): + alb = JellyfinAlbum({'Name': 'No Id Album'}, _Client()) + assert alb.thumb is None + + +def test_album_thumb_matches_artist_shape(): + # Same URL shape the artist uses (already proven to display) — just the + # album's own item id. + art = JellyfinArtist({'Id': 'xyz', 'Name': 'A'}, _Client()) + alb = JellyfinAlbum({'Id': 'xyz', 'Name': 'B'}, _Client()) + assert alb.thumb == art.thumb