Add trending movies/series, refreshed on a schedule
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

Mirrors TMDb's own /trending/{movie|tv}/{day|week} ranking rather than
Aurora computing trending itself from popularity deltas -- that would
require re-syncing the entire catalog on a schedule, infeasible at any
real scale, whereas TMDb already computes this well from signals (real
view/search velocity) Aurora has no access to.

New: TrendingEntry model + migration 0003, MetadataProvider.get_trending
(concrete/optional, like aclose(), so existing test fakes don't all need
updating), refresh_trending() which actually ingests each trending item
(only ~20 per window, cheap enough to fully ingest rather than apply the
never-fabricate-a-stub rule recommendations uses), a twice-daily arq cron
job (WorkerSettings.cron_jobs, new to this codebase), a manual admin
trigger for right after a deploy, and GET /movies/trending + GET
/series/trending.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
NixReaper
2026-09-06 05:18:02 -05:00
co-authored by Claude Sonnet 5
parent 6c95e18aad
commit 4403305da6
16 changed files with 513 additions and 2 deletions
+19
View File
@@ -52,6 +52,7 @@ GET /api/v1/search?q=the%20matrix
| GET | `/api/v1/movies/{aurora_id}/watch-providers` |
| GET | `/api/v1/movies/now-playing` |
| GET | `/api/v1/movies/upcoming` |
| GET | `/api/v1/movies/trending` |
`GET /api/v1/movies` browses/discovers without a search query — the
complement to `/search` (section 17, additive beyond the spec's minimum
@@ -159,6 +160,22 @@ sourcing watch-provider data back to JustWatch specifically if you display
it, not just crediting TMDb itself. Read TMDb's current terms for the
exact required wording/logo before showing this in the frontend.
`GET /api/v1/movies/trending` mirrors TMDb's own `/trending/movie/{window}`
ranking rather than Aurora computing trending itself from popularity deltas
-- that would require re-syncing the entire catalog on a schedule, which
isn't feasible at any real scale, whereas TMDb already computes this well
from signals (real view/search velocity) Aurora has no access to. `window`
(`day` default | `week`), `page`, `page_size` — same response shape as
`GET /movies`. Refreshed by a worker cron job twice a day (00:00 and
12:00 UTC-ish — see `WorkerSettings.cron_jobs` in `app/workers/worker.py`);
`POST /api/v1/admin/refresh-trending` (admin-protected) triggers the same
refresh on demand, useful right after a fresh deploy so this isn't empty
until the next scheduled tick. Unlike recommendations' never-fabricate-
a-stub rule, a refresh actually ingests each trending item — there are only
~20 per window (TMDb's first page), cheap enough to fully ingest rather
than drop anything Aurora hasn't seen yet. `GET /series/trending` is the
same idea for series.
`GET /api/v1/movies/now-playing` and `GET /api/v1/movies/upcoming` use the
`release_dates` data above rather than `release_date`/`year`: `region`
(ISO 3166-1 alpha-2, default `US`), `page`, `page_size` — same
@@ -181,6 +198,7 @@ now-playing, soonest for upcoming) rather than appearing twice.
| GET | `/api/v1/series/{aurora_id}/images` |
| GET | `/api/v1/series/{aurora_id}/videos` |
| GET | `/api/v1/series/{aurora_id}/watch-providers` |
| GET | `/api/v1/series/trending` |
| GET | `/api/v1/seasons/{aurora_id}` |
| GET | `/api/v1/episodes/{aurora_id}` |
@@ -290,6 +308,7 @@ want network-level isolation on top of the API key.
| POST | `/api/v1/admin/ingest/season` | `{"tmdb_id": "1396", "season_number": 1}` | Ingests one season's full episode list. Requires the series to already be ingested (`ingest/series` first) — fails with a clear error otherwise. |
| POST | `/api/v1/admin/ingest/collection` | `{"tmdb_id": "2344"}` | Ingests a franchise's own metadata (overview, poster/backdrop) and links its already-ingested member movies (see "Collections / Companies" above). Movie ingestion also lazily creates a bare name-only stub the first time it sees a `belongs_to_collection` reference — this is what actually fills it in. |
| GET | `/api/v1/admin/ingest/jobs/{job_id}` | — | Poll job status: `pending` → `running` → `succeeded`/`failed`/`retrying`. |
| POST | `/api/v1/admin/refresh-trending` | — | Manually triggers the same refresh the twice-daily cron job runs (see "Trending" above). Not tracked via `ingest/jobs` — check `GET /movies/trending`/`GET /series/trending` directly instead of polling a job id. |
## Errors