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
+4
View File
@@ -22,8 +22,10 @@ from app.core.logging import get_logger
from app.services.artwork.download import download_artwork
from app.services.artwork.storage import get_artwork_storage
from app.services.ingestion.runner import (
get_movie_company_ids,
get_or_create_ingestion_job,
get_primary_artwork_for_download,
get_series_company_ids,
get_series_season_ids,
get_series_season_numbers,
run_movie_ingestion,
@@ -52,6 +54,7 @@ async def _cascade_artwork_downloads(
async def ingest_movie_task(ctx: dict[str, Any], job_id: str, external_id: str) -> str:
movie_id = await run_movie_ingestion(uuid.UUID(job_id), external_id)
await _cascade_artwork_downloads(ctx, "movie", [movie_id])
await _cascade_artwork_downloads(ctx, "company", await get_movie_company_ids(movie_id))
return str(movie_id)
@@ -70,6 +73,7 @@ async def ingest_series_task(
# creates, independent of include_seasons (which only controls whether
# full per-season episode data gets fetched) -- so this always runs.
await _cascade_artwork_downloads(ctx, "season", await get_series_season_ids(series_id))
await _cascade_artwork_downloads(ctx, "company", await get_series_company_ids(series_id))
if include_seasons:
# Fan out one ingest_season job per season this series has, using