Ingest and self-host company/network logos
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

CompanyBriefOut.logo_path and CompanyDetailOut.logo_path have existed
in the API schemas since Core 0.1's first pass, but nothing ever
actually populated them -- TMDb's production_companies/networks
entries carry a logo_path field that the normalizer simply never read,
so every response has silently returned null there.

- NormalizedCompanyRef now captures logo_path from both movie
  production_companies and series networks/production_companies.
- normalizer.company_logo_artwork() builds the single-image artwork
  entry for it (a company has exactly one logo ingested, so it's
  always is_primary) -- centralized there rather than duplicated in
  both ingestion modules, matching how movie/series artwork URLs are
  already built in one place.
- movie_ingestion.py and series_ingestion.py sync it via the same
  sync_artwork() the rest of section 10's self-hosting already uses,
  so it gets the same re-ingestion-safe upsert behavior for free.
- tasks.py cascades a download_artwork_task per referenced company
  after movie/series ingestion, same fan-out pattern as
  movie/series/season/person artwork.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
NixReaper
2026-09-05 07:46:30 -05:00
co-authored by Claude Sonnet 5
parent 69ce41d54f
commit c03a75989d
8 changed files with 133 additions and 8 deletions
+28 -1
View File
@@ -54,6 +54,7 @@ class NormalizedCompanyRef:
name: str
country_code: str | None = None
role: str = "production"
logo_path: str | None = None # TMDb's raw path, e.g. "/logo.png" — not a full URL yet
@dataclass
@@ -74,6 +75,27 @@ class NormalizedArtwork:
is_primary: bool = False
def company_logo_artwork(logo_path: str | None) -> NormalizedArtwork | None:
"""A single-item artwork entry for a company/network's TMDb logo_path,
or None if it doesn't have one. Shared by movie_ingestion.py and
series_ingestion.py so the "how do we turn a relative TMDb path into a
real image URL" logic stays in one place (this module), rather than the
ingestion layer needing to know about TMDb's CDN itself."""
if not logo_path:
return None
return NormalizedArtwork(
artwork_type="logo",
url=_image_url_required(logo_path),
provider_asset_id=logo_path,
width=None,
height=None,
aspect_ratio=None,
language=None,
is_primary=True, # a company has exactly one logo ingested today
)
@dataclass
class NormalizedVideo:
video_type: str
@@ -173,6 +195,7 @@ def normalize_movie(raw: dict) -> NormalizedMovie:
external_id=str(pc["id"]),
name=pc["name"],
country_code=pc.get("origin_country") or None,
logo_path=pc.get("logo_path"),
)
for pc in raw.get("production_companies", [])
]
@@ -464,7 +487,10 @@ def normalize_series(raw: dict) -> NormalizedSeries:
networks = [
NormalizedCompanyRef(
external_id=str(n["id"]), name=n["name"], country_code=n.get("origin_country") or None
external_id=str(n["id"]),
name=n["name"],
country_code=n.get("origin_country") or None,
logo_path=n.get("logo_path"),
)
for n in raw.get("networks", [])
]
@@ -473,6 +499,7 @@ def normalize_series(raw: dict) -> NormalizedSeries:
external_id=str(pc["id"]),
name=pc["name"],
country_code=pc.get("origin_country") or None,
logo_path=pc.get("logo_path"),
)
for pc in raw.get("production_companies", [])
]