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
+27 -3
View File
@@ -32,7 +32,12 @@ FAKE_MOVIE = {
"imdb_id": "tt0133093",
"genres": [{"id": 28, "name": "Action"}, {"id": 878, "name": "Science Fiction"}],
"production_companies": [
{"id": 79, "name": "Village Roadshow Pictures", "origin_country": "US"}
{
"id": 79,
"name": "Village Roadshow Pictures",
"origin_country": "US",
"logo_path": "/village-roadshow-logo.png",
}
],
"poster_path": "/poster.jpg",
"backdrop_path": "/backdrop.jpg",
@@ -160,6 +165,17 @@ def test_normalize_movie_marks_exactly_one_primary_poster_and_backdrop():
assert primary_backdrops[0].provider_asset_id == "/backdrop.jpg"
def test_normalize_movie_maps_company_logo_path():
movie = normalize_movie(FAKE_MOVIE)
assert movie.companies[0].logo_path == "/village-roadshow-logo.png"
def test_normalize_movie_company_without_logo_is_none():
minimal = {**FAKE_MOVIE, "production_companies": [{"id": 1, "name": "No Logo Studio"}]}
movie = normalize_movie(minimal)
assert movie.companies[0].logo_path is None
def test_normalize_movie_external_ids_never_include_tmdb_itself():
# TMDb's own id is handled separately by the ingestion pipeline via
# `normalized.external_id`, not folded into `external_ids` (section 3:
@@ -224,9 +240,9 @@ FAKE_SERIES = {
"vote_count": 15000,
"genres": [{"id": 18, "name": "Drama"}, {"id": 80, "name": "Crime"}],
"created_by": [{"id": 66633, "name": "Vince Gilligan", "gender": 2}],
"networks": [{"id": 174, "name": "AMC", "origin_country": "US"}],
"networks": [{"id": 174, "name": "AMC", "origin_country": "US", "logo_path": "/amc-logo.png"}],
"production_companies": [
{"id": 2605, "name": "Gran Via Productions", "origin_country": "US"}
{"id": 2605, "name": "Gran Via Productions", "origin_country": "US", "logo_path": None}
],
"poster_path": "/poster.jpg",
"backdrop_path": "/backdrop.jpg",
@@ -323,6 +339,14 @@ def test_normalize_series_maps_networks_vs_production_companies():
assert [c.name for c in series.companies] == ["Gran Via Productions"]
def test_normalize_series_maps_network_logo_path():
series = normalize_series(FAKE_SERIES)
assert series.networks[0].logo_path == "/amc-logo.png"
# Gran Via's fixture entry has no logo_path at all -- must come through
# as None, not KeyError.
assert series.companies[0].logo_path is None
def test_normalize_series_maps_creators_separately_from_crew():
series = normalize_series(FAKE_SERIES)
assert len(series.creators) == 1