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
Extends the browse/discover pattern already built for movies/series to the fourth major entity type. No genre/year/status equivalent for people -- known_for_department is the one categorical filter that actually means something for a cast/crew list, and sort_by is just popularity (default) or name. No invalid-filter case either: unlike genre/status, known_for_department is a plain string compared directly, nothing to parse or validate. build_person_summary refactored to share row-to-summary logic with the new list path, same pattern used for movies/series/companies. Co-Authored-By: Claude Sonnet 5 <[email protected]>
219 lines
9.9 KiB
Markdown
219 lines
9.9 KiB
Markdown
# Aurora Core — API
|
|
|
|
Base path: `/api/v1`. Interactive OpenAPI docs are available at `/docs` in
|
|
any environment where `AURORA_API_DOCS_ENABLED=true` (default; set to
|
|
`false` to hide docs in production — section 32).
|
|
|
|
All responses are JSON, built from Pydantic response schemas
|
|
(`app/schemas/`), never from serialized ORM objects (section 18). Errors use
|
|
a consistent envelope:
|
|
|
|
```json
|
|
{ "error": { "code": "movie_not_found", "message": "No such movie: aur_movie_..." } }
|
|
```
|
|
|
|
## Health
|
|
|
|
| Method | Path | Notes |
|
|
|---|---|---|
|
|
| GET | `/health` and `/api/v1/health` | Liveness only. |
|
|
| GET | `/ready` and `/api/v1/ready` | Checks PostgreSQL (always) and Redis (if `REDIS_REQUIRED=true`). |
|
|
|
|
## Search
|
|
|
|
| Method | Path | Notes |
|
|
|---|---|---|
|
|
| GET | `/api/v1/search?q=...&limit=10` | Searches movies, series, people, collections, companies. `limit` (1-50) caps results **per type**. |
|
|
|
|
```json
|
|
GET /api/v1/search?q=the%20matrix
|
|
|
|
{
|
|
"query": "the matrix",
|
|
"movies": [ { "id": "aur_movie_...", "type": "movie", "title": "The Matrix", "release_date": "1999-03-30", "poster_path": "https://..." } ],
|
|
"series": [],
|
|
"people": [],
|
|
"collections": [ { "id": "aur_collection_...", "name": "The Matrix Collection" } ],
|
|
"companies": [],
|
|
"total_results": 2
|
|
}
|
|
```
|
|
|
|
## Movies
|
|
|
|
| Method | Path |
|
|
|---|---|
|
|
| GET | `/api/v1/movies` |
|
|
| GET | `/api/v1/movies/{aurora_id}` |
|
|
| GET | `/api/v1/movies/{aurora_id}/credits` |
|
|
| GET | `/api/v1/movies/{aurora_id}/images` |
|
|
| GET | `/api/v1/movies/{aurora_id}/videos` |
|
|
| GET | `/api/v1/movies/{aurora_id}/recommendations` |
|
|
|
|
`GET /api/v1/movies` browses/discovers without a search query — the
|
|
complement to `/search` (section 17, additive beyond the spec's minimum
|
|
endpoint list). Query params: `genre` (an Aurora genre id), `year`
|
|
(primary release year), `status` (e.g. `released`), `sort_by`
|
|
(`popularity` default | `release_date` | `vote_average` | `title`),
|
|
`sort_dir` (`desc` default | `asc`), `page` (default 1), `page_size`
|
|
(default 20, max 50). An invalid filter value (a malformed genre id, an
|
|
unknown status) is a 422, not a silently-empty result. Response:
|
|
|
|
```json
|
|
{
|
|
"page": 1,
|
|
"page_size": 20,
|
|
"total_results": 142,
|
|
"total_pages": 8,
|
|
"results": [ { "id": "aur_movie_...", "type": "movie", "title": "The Matrix", "release_date": "1999-03-30", "poster_path": "https://...", "popularity": 45.5, "vote_average": 8.2 } ]
|
|
}
|
|
```
|
|
|
|
`GET /api/v1/movies/{aurora_id}` (cached, see `docs/architecture.md#caching`):
|
|
|
|
```json
|
|
{
|
|
"id": "aur_movie_01m1q7p1c...",
|
|
"type": "movie",
|
|
"title": "The Matrix",
|
|
"original_title": "The Matrix",
|
|
"overview": "...",
|
|
"tagline": "Welcome to the Real World",
|
|
"status": "released",
|
|
"original_language": "en",
|
|
"release_date": "1999-03-30",
|
|
"runtime": 136,
|
|
"budget": 63000000,
|
|
"revenue": 463517383,
|
|
"popularity": 45.5,
|
|
"vote_average": 8.2,
|
|
"vote_count": 24000,
|
|
"adult": false,
|
|
"genres": [{ "id": "aur_genre_...", "name": "Action" }],
|
|
"keywords": [{ "id": "aur_keyword_...", "name": "dystopia" }],
|
|
"cast": [{ "id": "aur_credit_...", "person": { "id": "aur_person_...", "name": "Keanu Reeves", "profile_path": null }, "character": "Neo", "billing_order": 0 }],
|
|
"crew": [{ "id": "aur_credit_...", "person": { "id": "aur_person_...", "name": "Lana Wachowski" }, "department": "Directing", "job": "Director" }],
|
|
"companies": [{ "id": "aur_company_...", "name": "Village Roadshow Pictures", "company_type": "production_company" }],
|
|
"collection": { "id": "aur_collection_...", "name": "The Matrix Collection" },
|
|
"images": { "posters": [...], "backdrops": [...], "logos": [...] },
|
|
"videos": [{ "id": "aur_vid_...", "type": "trailer", "site": "YouTube", "url": "https://www.youtube.com/watch?v=...", "is_official": true }],
|
|
"external_ids": { "tmdb": "603", "imdb": "tt0133093" },
|
|
"created_at": "...",
|
|
"updated_at": "..."
|
|
}
|
|
```
|
|
|
|
`GET /api/v1/movies/{aurora_id}/recommendations` returns TMDb's ranked
|
|
recommendations, but only the ones already ingested into Aurora — a
|
|
recommendation pointing at a movie Aurora hasn't seen yet is silently
|
|
dropped rather than fabricated as a stub (section 15), so this list can be
|
|
sparse (or empty) for a young dataset and fills in as more movies get
|
|
ingested:
|
|
|
|
```json
|
|
{
|
|
"id": "aur_movie_01m1q7p1c...",
|
|
"recommendations": [
|
|
{ "id": "aur_movie_...", "type": "movie", "title": "The Matrix Reloaded", "release_date": "2003-05-15", "poster_path": "https://..." }
|
|
]
|
|
}
|
|
```
|
|
|
|
## Series / Seasons / Episodes
|
|
|
|
| Method | Path |
|
|
|---|---|
|
|
| GET | `/api/v1/series` |
|
|
| GET | `/api/v1/series/{aurora_id}` |
|
|
| GET | `/api/v1/series/{aurora_id}/seasons` |
|
|
| GET | `/api/v1/series/{aurora_id}/credits` |
|
|
| GET | `/api/v1/series/{aurora_id}/images` |
|
|
| GET | `/api/v1/series/{aurora_id}/videos` |
|
|
| GET | `/api/v1/seasons/{aurora_id}` |
|
|
| GET | `/api/v1/episodes/{aurora_id}` |
|
|
|
|
`GET /api/v1/series` is the series equivalent of `GET /api/v1/movies`
|
|
above, with `sort_by` values `popularity` (default) | `first_air_date` |
|
|
`vote_average` | `title` (`release_date`/`year` become `first_air_date`/
|
|
`year` filtered against it) — otherwise the same params, response shape,
|
|
and validation behavior.
|
|
|
|
Series detail includes `networks` and `companies` as separate lists (a TV
|
|
network and a production company are both `Company` rows distinguished by
|
|
`company_type` — see `docs/database.md`), plus `creators` (series-level
|
|
"Creator" credits) alongside `cast`/`crew`. `/credits`, `/images`, and
|
|
`/videos` are the same sub-resources broken out on their own, mirroring the
|
|
movie endpoints above (parity that was missing until now) — `/credits`
|
|
carries the same `cast`/`crew`/`creators` three-way split as series detail.
|
|
|
|
## People
|
|
|
|
| Method | Path |
|
|
|---|---|
|
|
| GET | `/api/v1/people` |
|
|
| GET | `/api/v1/people/{aurora_id}` |
|
|
| GET | `/api/v1/people/{aurora_id}/credits` (movie and series filmography; guest-star episode credits are aggregated to one row per series rather than one per episode, but carry both `episode_count` and the individual `episodes` breakdown, sorted by season/episode number) |
|
|
|
|
`GET /api/v1/people` is the browse/discover equivalent for people. No
|
|
genre/year/status here — `known_for_department` (exact match, e.g.
|
|
`Acting`, `Directing`, `Writing`) is the one categorical filter that
|
|
actually means something for a cast/crew list, and `sort_by` is just
|
|
`popularity` (default) or `name`. Otherwise the same `page`/`page_size`
|
|
pagination and response shape as `/movies` and `/series`.
|
|
|
|
## Collections / Companies
|
|
|
|
| Method | Path |
|
|
|---|---|
|
|
| GET | `/api/v1/collections/{aurora_id}` |
|
|
| GET | `/api/v1/companies/{aurora_id}` |
|
|
|
|
Collection detail's `movies` list only includes movies already ingested
|
|
into Aurora — `POST /api/v1/admin/ingest/collection` links a franchise's
|
|
member movies purely by external-id lookup, the same never-fabricate-a-stub
|
|
rule `recommendations` follows, so a franchise fills in as more of its
|
|
movies get ingested rather than all at once.
|
|
|
|
Company detail also carries `movies` and `series` lists — everything that
|
|
company is credited on (any role: production, distribution, network),
|
|
ordered by release/first-air date. No never-fabricate-a-stub caveat here:
|
|
a `MovieCompany`/`SeriesCompany` row is only ever created as a side effect
|
|
of actually ingesting that movie or series, so there's no separate
|
|
"company's known filmography" TMDb ever needs linking after the fact.
|
|
|
|
## Administrative (section 17: "separately protected")
|
|
|
|
Require header `X-API-Key: <one of AURORA_ADMIN_API_KEYS>`. Never exposed to
|
|
public clients — mount behind a separate Traefik router/ACL in production if
|
|
you want network-level isolation too.
|
|
|
|
| Method | Path | Body | Notes |
|
|
|---|---|---|---|
|
|
| POST | `/api/v1/admin/ingest/movie` | `{"tmdb_id": "603"}` | Enqueues an arq job; returns `{"job_id": "...", "status": "enqueued"}` immediately (section 15: ingestion never runs inline in a request). |
|
|
| POST | `/api/v1/admin/ingest/person` | `{"tmdb_id": "6384"}` | Same, for a person profile. |
|
|
| POST | `/api/v1/admin/ingest/series` | `{"tmdb_id": "1396", "include_seasons": false}` | Ingests the series and season *stubs* (number, title, air date, poster) — no episodes yet. With `include_seasons: true`, also enqueues an `ingest/season` job for every season once the series finishes (opt-in: some shows have dozens of seasons, each a separate TMDb call). |
|
|
| 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`. |
|
|
|
|
## Errors
|
|
|
|
| Status | When |
|
|
|---|---|
|
|
| 404 | Entity not found, or the ID is well-formed but the wrong entity type (`aur_person_...` passed to `/movies/{id}`) |
|
|
| 422 | Request validation failure (e.g. missing/invalid query params) |
|
|
| 429 | Rate limit exceeded (`Retry-After` header set) |
|
|
| 401 | Missing/invalid `X-API-Key` on an admin endpoint |
|
|
| 503 | Admin API called with `AURORA_ADMIN_API_KEYS` unset, or the job queue is unavailable |
|
|
|
|
## Known limitations
|
|
|
|
- `GET /movies` and `GET /series` (browse/discover) paginate with
|
|
`page`/`page_size`, offset-based rather than the Aurora ID cursor scheme
|
|
`docs/database.md#aurora-ids-vs-primary-keys` describes, since sorting by
|
|
popularity/rating/date doesn't line up with creation-order cursors. Other
|
|
list-shaped responses (recommendations, a collection's `movies`, a
|
|
person's `credits`) still return everything with no pagination at all —
|
|
fine at today's scale, but the natural next thing to revisit if any of
|
|
them grow large.
|