Fix sync_artwork clobbering is_primary via duplicate list entries
CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s

TMDb's images.posters/backdrops alternates commonly include the *same*
image as the top-level poster_path/backdrop_path -- so normalize_movie
legitimately produces the same provider_asset_id twice in one
artwork_list: once from the top-level field (is_primary=True), once
from the general alternates loop (is_primary=False, the default).
sync_artwork matched both occurrences to the same existing DB row by
provider_asset_id and did a plain overwrite each time, so whichever
occurrence it processed last won -- the non-primary duplicate always
came second in list order, so it silently un-set is_primary right
after the first occurrence had just set it.

Caught by re-ingesting The Matrix on a devbox with the artwork
download feature live: the movie ingested successfully, but zero
download jobs were ever enqueued because get_primary_artwork_ids
found nothing marked is_primary -- the flag had been clobbered back to
false immediately after being set.

Fixed by resetting is_primary on all existing rows up front (so a
provider dropping its "primary" pick on an image actually clears, not
just adds) and accumulating with OR within the batch instead of a
plain overwrite, so any primary occurrence in one sync sticks
regardless of processing order. Also dedupes brand-new rows within the
same batch the same way, closing the equivalent bug for images that
don't have an existing row yet.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
NixReaper
2026-09-05 07:30:40 -05:00
co-authored by Claude Sonnet 5
parent fef914db36
commit 7c4bfe6cf4
2 changed files with 148 additions and 22 deletions
+35 -22
View File
@@ -120,44 +120,57 @@ async def sync_artwork(
.scalars()
.all()
)
existing_by_asset_id = {
by_asset_id: dict[str, Artwork] = {
row.provider_asset_id: row for row in existing if row.provider_asset_id is not None
}
# The incoming batch is authoritative for which image(s) are primary
# this sync — reset up front so a provider dropping its "primary" pick
# on an existing image actually clears here, not just adds.
for row in existing:
row.is_primary = False
seen_asset_ids: set[str] = set()
for art in artwork_list:
art_type = art.artwork_type if art.artwork_type in _VALID_ARTWORK_TYPES else "poster"
matched = (
existing_by_asset_id.get(art.provider_asset_id) if art.provider_asset_id else None
)
asset_id = art.provider_asset_id
matched = by_asset_id.get(asset_id) if asset_id else None
if matched is not None:
seen_asset_ids.add(art.provider_asset_id) # type: ignore[arg-type]
matched.artwork_type = ArtworkType(art_type)
matched.width = art.width
matched.height = art.height
matched.aspect_ratio = art.aspect_ratio
matched.vote_average = art.vote_average
matched.is_primary = art.is_primary
# OR, never a plain overwrite: the *same* image commonly
# appears twice in one provider fetch (TMDb's selected
# poster_path also shows up in the general posters alternates
# list) — once any occurrence in this batch says "primary",
# a later, non-primary occurrence of that same image must not
# un-set it.
matched.is_primary = matched.is_primary or art.is_primary
matched.original_url = art.url
# local_path/checksum deliberately untouched — see docstring.
else:
session.add(
Artwork(
entity_type=entity_type,
entity_id=entity_id,
artwork_type=ArtworkType(art_type),
width=art.width,
height=art.height,
aspect_ratio=art.aspect_ratio,
provider_id=provider_id,
provider_asset_id=art.provider_asset_id,
original_url=art.url,
vote_average=art.vote_average,
is_primary=art.is_primary,
)
new_row = Artwork(
entity_type=entity_type,
entity_id=entity_id,
artwork_type=ArtworkType(art_type),
width=art.width,
height=art.height,
aspect_ratio=art.aspect_ratio,
provider_id=provider_id,
provider_asset_id=asset_id,
original_url=art.url,
vote_average=art.vote_average,
is_primary=art.is_primary,
)
if art.provider_asset_id:
seen_asset_ids.add(art.provider_asset_id)
session.add(new_row)
if asset_id:
# Register immediately so a second occurrence of this same
# new asset_id later in the batch updates this row instead
# of inserting a duplicate.
by_asset_id[asset_id] = new_row
if asset_id:
seen_asset_ids.add(asset_id)
for row in existing:
if row.provider_asset_id not in seen_asset_ids: