mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
docs(tmdb): thicken API skill
Add researched TMDb references, external-ID and detail commands, offline tests, and eval coverage. Refresh the human README and generated catalogs. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent
1d95c4b020
commit
8cf85fe0a6
@@ -1272,7 +1272,7 @@
|
||||
"./tmdb"
|
||||
],
|
||||
"strict": false,
|
||||
"description": "Search and discover movies, TV shows, and trending content via The Movie Database (TMDb) API v3. Use when the user asks about movies, TV, film, cinema, genres, certifications, ratings, cast, upcoming releases, or trending media."
|
||||
"description": "Query TMDb metadata for films and television, then enrich results with details, credits, providers, and external IDs. Do not use this skill for torrent search, streaming playback, or personal watch-history tracking."
|
||||
},
|
||||
{
|
||||
"name": "traefik",
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
- [tempest](tempest/SKILL.md): Query hyper-local weather from a WeatherFlow Tempest station: current conditions, 7-day forecast, historical observations, and real-time UDP broadcasts. Use when the user asks about the weather, temperature, rain, wind, humidity, forecast, or wants conditions from their own station rather than a generic weather service.
|
||||
- [terraform](terraform/SKILL.md): Operate Terraform and OpenTofu across the whole infrastructure lifecycle: module structure, state backends and locking, plan/apply workflow, drift detection, remote state, upgrade and refactor flows, and evidence-based diagnostics. Use when running or inspecting terraform plans, applies, state files, imports, or state surgery, or when the bundled tfops script should handle the task. Do not use for IaC methodology or cloud design decisions - those route up to platform-engineering.
|
||||
- [three](three/SKILL.md): Build browser-based Three.js and WebGL scenes, animations, and interactive 3D visualizations with a small vanilla JavaScript starting point.
|
||||
- [tmdb](tmdb/SKILL.md): Search and discover movies, TV shows, and trending content via The Movie Database (TMDb) API v3. Use when the user asks about movies, TV, film, cinema, genres, certifications, ratings, cast, upcoming releases, or trending media.
|
||||
- [tmdb](tmdb/SKILL.md): Query TMDb metadata for films and television, then enrich results with details, credits, providers, and external IDs. Do not use this skill for torrent search, streaming playback, or personal watch-history tracking.
|
||||
- [traefik](traefik/SKILL.md): Deploy, configure, and troubleshoot Traefik v3 reverse proxy — covers all providers, routing, TLS/ACME, middlewares, and production patterns with YAML examples. Load when setting up or debugging a Traefik instance.
|
||||
- [trakt](trakt/SKILL.md): Discover trending, anticipated, and popular movies and TV shows via the Trakt.tv API from the terminal. No authentication required for read-only discovery. Use when the user asks about what to watch, trending movies, popular shows, or media discovery.
|
||||
- [transistor](transistor/SKILL.md): Manage Transistor.fm podcast hosting from the terminal: view shows, list episodes, check analytics, and get subscriber counts. Use when the user mentions Transistor, podcast hosting, podcast analytics, show management, or episode tracking.
|
||||
|
||||
+23
-18
@@ -1,36 +1,41 @@
|
||||
# TMDb — Movie & TV Discovery from the Terminal
|
||||
|
||||
Search movies and TV shows by keyword, discover by genre/certification/rating/date, check trending and upcoming releases, and browse genre lists.
|
||||
# TMDb Metadata Skill
|
||||
|
||||
## Why Install This Skill
|
||||
|
||||
When your agent loads this skill, it can **access the entire TMDb catalog** without a browser. That means:
|
||||
Give your agent a dependable terminal workflow for exploring movie and television metadata without hand-building every HTTP request. It can start from a title, an IMDb ID, or a discovery filter, then enrich the result with credits, recommendations, images, and provider metadata.
|
||||
|
||||
- **Search movies and TV** — by keyword with release year and ratings
|
||||
- **Discover by taste** — genre, certification, rating threshold, date range
|
||||
- **Find trending content** — what's popular right now
|
||||
- **Check upcoming releases** — what's coming to theaters
|
||||
- **Browse certifications** — US ratings (G, PG, PG-13, R, NC-17)
|
||||
The skill also makes TMDb's easy-to-miss rules visible: choose one authentication mode, respect the 500-page ceiling, use the correct nested `/find` response, and URL-encode compound provider paths.
|
||||
|
||||
## What You Get
|
||||
|
||||
| Directory | Purpose |
|
||||
|-----------|---------|
|
||||
| `SKILL.md` | Complete command reference with compound filter examples |
|
||||
| `scripts/tmdb` | CLI tool for TMDb v3 API |
|
||||
| Path | Purpose |
|
||||
| --- | --- |
|
||||
| `SKILL.md` | Setup, commands, recipes, gotchas, and routing |
|
||||
| `scripts/tmdb` | Executable JSON-capable CLI for search, detail, find, discovery, and trends |
|
||||
| `scripts/test_tmdb.py` | Offline pytest/unittest coverage with mocked HTTP behavior |
|
||||
| `references/auth-pagination-and-errors.md` | Authentication, pagination, errors, rate limits, and image construction |
|
||||
| `references/find-and-details.md` | External IDs, IMDb entry points, details, credits, and compound responses |
|
||||
| `references/search-discover-trending.md` | Search, discovery filters, trending, genres, and certifications |
|
||||
| `evals/evals.json` | Runnable examples covering normal and negative routing |
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
export TMDB_ACCESS_TOKEN="your-tmdb-access-token"
|
||||
tmdb movie search --term "dune"
|
||||
tmdb movie discover --genre horror --certification R
|
||||
export TMDB_ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
|
||||
tmdb movie search --term "Dune" --limit 5 --json
|
||||
tmdb find tt0111161 --source imdb_id --json
|
||||
tmdb movie detail 550 --append credits,videos --json
|
||||
```
|
||||
|
||||
## Triggers
|
||||
|
||||
Load this for movies, TV shows, film discovery, genre browsing, or media recommendations.
|
||||
Load this skill when the request involves movie or TV metadata, title search, IMDb/TVDB resolution, credits, release dates, certifications, recommendations, images, trending media, or provider metadata.
|
||||
|
||||
## Requirements
|
||||
|
||||
Python 3.8+ with `requests` library. Free API key from themoviedb.org.
|
||||
- Python 3.8 or newer
|
||||
- `requests` Python package
|
||||
- A TMDb API Read Access Token or v3 API key
|
||||
- `jq` for the shell pipeline examples
|
||||
|
||||
This is a read-oriented metadata workflow. It does not play media, search torrents, or maintain personal watch history.
|
||||
|
||||
+79
-90
@@ -1,133 +1,122 @@
|
||||
---
|
||||
name: tmdb
|
||||
description: Search and discover movies, TV shows, and trending content via The Movie
|
||||
Database (TMDb) API v3. Use when the user asks about movies, TV, film, cinema, genres,
|
||||
certifications, ratings, cast, upcoming releases, or trending media.
|
||||
description: Query TMDb metadata for films and television, then enrich results with details, credits, providers, and external IDs. Do not use this skill for torrent search, streaming playback, or personal watch-history tracking.
|
||||
license: MIT
|
||||
compatibility: Requires TMDB_ACCESS_TOKEN or TMDB_API_KEY env var (free at themoviedb.org/settings/api),
|
||||
Python 3.8+, and the `requests` library.
|
||||
compatibility: Requires TMDB_ACCESS_TOKEN or TMDB_API_KEY, Python 3.8+, and requests.
|
||||
metadata:
|
||||
tags: tmdb, movies, tv, film, cinema, entertainment, media-discovery, api-client
|
||||
sources: https://developer.themoviedb.org/reference, https://www.themoviedb.org/settings/api
|
||||
tags: tmdb, movies, tv, film, cinema, metadata
|
||||
sources: https://developer.themoviedb.org/reference
|
||||
---
|
||||
|
||||
# tmdb — Movie & TV Discovery from the Terminal
|
||||
|
||||
Search movies and TV shows by keyword, discover by genre/certification/rating/date, check trending and upcoming releases, browse genre lists, and view US certification ratings — all from TMDb's v3 API.
|
||||
# TMDb metadata from the terminal
|
||||
|
||||
## Setup
|
||||
|
||||
1. Get a free API key or access token at [themoviedb.org/settings/api](https://www.themoviedb.org/settings/api)
|
||||
2. Set one of these environment variables:
|
||||
Create credentials at [TMDb API settings](https://www.themoviedb.org/settings/api). Prefer the API Read Access Token:
|
||||
|
||||
```bash
|
||||
export TMDB_ACCESS_TOKEN="your-tmdb-access-token" # preferred
|
||||
# OR
|
||||
export TMDB_API_KEY="your-tmdb-api-key"
|
||||
export TMDB_ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
|
||||
# Or use the v3 key: export TMDB_API_KEY="YOUR_API_KEY"
|
||||
```
|
||||
|
||||
`--help` and `--dry-run` work without credentials (lazy auth).
|
||||
The CLI sends either `Authorization: Bearer $TMDB_ACCESS_TOKEN` or `?api_key=$TMDB_API_KEY`. Both forms have the same v3 access level; configure only one. `--help` and `--dry-run` do not need credentials.
|
||||
|
||||
## Essential Commands
|
||||
## Essential commands
|
||||
|
||||
### movie search — Search movies by keyword
|
||||
### Search and identify
|
||||
|
||||
```bash
|
||||
tmdb movie search --term "dune" # basic search
|
||||
tmdb movie search --term "inception" --limit 5 # top 5 results
|
||||
tmdb movie search --term "arrival" --json # machine-readable
|
||||
tmdb movie search --term "dune" --limit 5 --json
|
||||
tmdb tv search --term "severance" --limit 5
|
||||
tmdb find tt0111161 --source imdb_id --json
|
||||
```
|
||||
|
||||
Shows: title, release year, vote average.
|
||||
`find` accepts the current external sources `imdb_id`, `tvdb_id`, `wikidata_id`, `facebook_id`, `instagram_id`, `tiktok_id`, `twitter_id`, and `youtube_id`. Its response is split into `movie_results`, `tv_results`, `person_results`, `tv_season_results`, and `tv_episode_results`.
|
||||
|
||||
### movie discover — Discover movies by genre, certification, rating, and date
|
||||
### Details and enrichment
|
||||
|
||||
```bash
|
||||
tmdb movie discover --genre horror # horror movies
|
||||
tmdb movie discover --genre horror --certification R # horror, R-rated
|
||||
tmdb movie discover --genre comedy --rating 7 --limit 15 # highly-rated comedy
|
||||
tmdb movie discover --from 2024-01-01 --to 2024-12-31 # released in 2024
|
||||
tmdb movie discover --genre scifi --from 2026-05-01 # recent sci-fi
|
||||
tmdb movie discover --genre thriller --certification R \
|
||||
--rating 6 --from 2025-01-01 --limit 20 # compound filter
|
||||
tmdb movie detail 550 --append credits,videos --json
|
||||
tmdb movie detail 550 --append 'credits,watch/providers,external_ids' --json
|
||||
```
|
||||
|
||||
### movie upcoming — Upcoming movie releases
|
||||
Compound responses use the requested names as top-level keys. Encode the slash in `watch/providers` when constructing raw URLs.
|
||||
|
||||
### Discover and browse
|
||||
|
||||
```bash
|
||||
tmdb movie upcoming # next 10 upcoming
|
||||
tmdb movie upcoming --limit 20 # more results
|
||||
tmdb movie upcoming --json # machine-readable
|
||||
```
|
||||
|
||||
### tv search — Search TV shows by keyword
|
||||
|
||||
```bash
|
||||
tmdb tv search --term "severance" # basic TV search
|
||||
tmdb tv search --term "the expanse" --limit 5
|
||||
tmdb tv search --term "silo" --json
|
||||
```
|
||||
|
||||
Shows: name, first air year, vote average.
|
||||
|
||||
### tv discover — Discover TV shows by genre, rating, and air date
|
||||
|
||||
```bash
|
||||
tmdb tv discover --genre sci-fi # sci-fi shows
|
||||
tmdb tv discover --genre drama --rating 7 # critically-acclaimed drama
|
||||
tmdb tv discover --genre comedy --from 2025-01-01 # recent comedy
|
||||
```
|
||||
|
||||
### trending — Trending content across day or week
|
||||
|
||||
```bash
|
||||
tmdb trending # trending movies this week
|
||||
tmdb trending --type tv # trending TV this week
|
||||
tmdb trending --type all --window day # all media trending today
|
||||
tmdb trending --limit 20 --json # top 20 as JSON
|
||||
```
|
||||
|
||||
### genre list — Browse available genres
|
||||
|
||||
```bash
|
||||
tmdb genre list --type movie # all movie genres
|
||||
tmdb genre list --type tv # all TV genres
|
||||
tmdb movie discover --genre horror --rating 7 --limit 10
|
||||
tmdb movie discover --genre horror --certification R --from 2024-01-01 --to 2024-12-31
|
||||
tmdb trending --type all --window week --limit 20 --json
|
||||
tmdb genre list --type movie --json
|
||||
tmdb certification --json
|
||||
```
|
||||
|
||||
### certification — View US movie certification ratings
|
||||
Use `vote_count.gte` with `vote_average.desc` in raw discover requests so a title with very few votes does not dominate. In current TMDb docs, comma-separated genre IDs are AND and pipe-separated IDs are OR.
|
||||
|
||||
## Pipeline recipes
|
||||
|
||||
### IMDb ID to enriched movie
|
||||
|
||||
1. Resolve the IMDb identifier:
|
||||
|
||||
```bash
|
||||
tmdb certification # US certification list
|
||||
tmdb certification --json # machine-readable
|
||||
curl -s -H "Authorization: Bearer $TMDB_ACCESS_TOKEN" \
|
||||
'https://api.themoviedb.org/3/find/tt0111161?external_source=imdb_id' > /tmp/find.json
|
||||
id=$(jq -r '.movie_results[0].id' /tmp/find.json)
|
||||
```
|
||||
|
||||
## Global Flags
|
||||
|
||||
These flags work in any position before, between, or after subcommands:
|
||||
2. Fetch details and compound resources:
|
||||
|
||||
```bash
|
||||
tmdb --json movie search --term "dune" # JSON output
|
||||
tmdb movie search --term "dune" --json # json after subcommand
|
||||
tmdb --dry-run movie discover --genre horror # preview without API call
|
||||
tmdb --quiet trending # suppress diagnostic output
|
||||
tmdb --verbose movie search --term "alien" # verbose logging
|
||||
curl -s -H "Authorization: Bearer $TMDB_ACCESS_TOKEN" \
|
||||
"https://api.themoviedb.org/3/movie/$id?append_to_response=credits,videos,watch%2Fproviders" \
|
||||
| jq '{title, runtime, director: [.credits.crew[] | select(.job == "Director") | .name], cast: [.credits.cast[0:5][].name], providers: .["watch/providers"].results.US}'
|
||||
```
|
||||
|
||||
## Known Gotchas
|
||||
### Search then detail
|
||||
|
||||
- **Genre name matching is case-insensitive** — `--genre Horror`, `--genre horror`, and `--genre HORROR` all work. Names are matched via substring, so `--genre sci` matches "Sci-Fi" and "Science Fiction".
|
||||
- **Certifications are US-only** — The `--certification` flag and the `certification` subcommand only return/accept US ratings (G, PG, PG-13, R, NC-17). International certifications are not available.
|
||||
- **API version** — This CLI wraps TMDb API v3. Endpoints and response shapes follow the v3 spec.
|
||||
- **Pagination defaults** — Every command defaults to 10 results. Use `--limit` to get more. The CLI does not auto-paginate beyond the first page.
|
||||
- **Now-playing is defined** — The `movie now-playing` subcommand is registered in argparse and maps to the TMDb `/movie/now_playing` endpoint.
|
||||
```bash
|
||||
tmdb movie search --term "dune" --limit 1 --json > /tmp/search.json
|
||||
id=$(jq -r '.results[0].id' /tmp/search.json)
|
||||
tmdb movie detail "$id" --append recommendations,similar --json
|
||||
```
|
||||
|
||||
## References
|
||||
### Filter reliable discoveries
|
||||
|
||||
- [scripts/tmdb](scripts/tmdb) — The CLI binary. Built following the cli-builder patterns: non-interactive, `--json`, `--dry-run`, `--quiet`, `--verbose`, dual-output via `emit()`, lazy auth, structured logging.
|
||||
- [TMDb API v3 Reference](https://developer.themoviedb.org/reference) — Official API documentation.
|
||||
- [TMDb API Settings (get a key)](https://www.themoviedb.org/settings/api) — Free API key registration.
|
||||
For direct API use, combine a date window, pipe-OR or comma-AND genre expression, `vote_count.gte`, and `sort_by=vote_average.desc`. Then retain only the fields needed by the next workflow step with jq.
|
||||
|
||||
## JSON and jq
|
||||
|
||||
Put `--json` before or after the subcommand. JSON search output has `results` and usually pagination fields `page`, `total_pages`, and `total_results`; the service limits page numbers to 500. Use `jq -r '.results[] | [.id, (.title // .name)] | @tsv'` for stable tabular handoff.
|
||||
|
||||
## Known gotchas
|
||||
|
||||
- **Credential duality:** `api_key` and Bearer are alternatives, not values to mix. A rejected credential commonly produces HTTP 401, `status_code: 7`, and `Invalid API key: You must be granted a valid key.` Permission failures use code 3. Code 33 means an invalid request token, not this API-key message.
|
||||
- **Pagination ceiling:** pages start at 1 and max at 500; over-limit requests fail. Search/discover access is effectively capped at 10,000 results, even where totals look larger. Rate guidance is around 40 requests/second and 429 responses should honor `Retry-After`.
|
||||
- **Compound syntax:** append values are comma-separated and limited to 20 calls. `watch/providers` contains a slash, so URL-encode it in curl and use jq's `.\"watch/providers\"` notation.
|
||||
- **External-ID shape:** `/find/` does not return one generic `id`; inspect the appropriate nested array before choosing movie or TV detail.
|
||||
- **Provider filters:** `with_watch_providers` requires `watch_region`; provider data carries JustWatch attribution requirements.
|
||||
- **Localization and images:** use `language=en-US` and a market `region` when reproducibility matters. Build image URLs from `/3/configuration`'s secure base URL, a valid size, and the returned path.
|
||||
|
||||
## When to use
|
||||
|
||||
Use this skill for read-only film and TV metadata discovery, credits, release information, certifications, images, recommendations, and provider metadata.
|
||||
|
||||
## When not to use
|
||||
|
||||
Do not use this skill for streaming-availability lookups (TMDb delegates watch-provider data to JustWatch and may lag), for torrent or piracy search, or for tracking what you have already watched — TMDb is a metadata database, not a viewing source; pair it with trakt for personal watch history.
|
||||
Do not use it for torrent or piracy searches, playing or downloading a stream, or maintaining personal watched/unwatched state. Use `trakt` for watch-history workflows and a playback/catalog integration for availability actions.
|
||||
|
||||
## Reference files
|
||||
|
||||
| File | Use it for |
|
||||
| --- | --- |
|
||||
| [references/auth-pagination-and-errors.md](references/auth-pagination-and-errors.md) | Credentials, pagination, rate limits, errors, language, regions, and images |
|
||||
| [references/find-and-details.md](references/find-and-details.md) | IMDb/TVDB lookup, response mapping, detail fields, compound requests |
|
||||
| [references/search-discover-trending.md](references/search-discover-trending.md) | Search, discover filters, trending, genre, certification, and release lists |
|
||||
|
||||
## Available scripts and prerequisites
|
||||
|
||||
- `scripts/tmdb` is an executable Python CLI using only the standard library and `requests`; it preserves `--json`, `--dry-run`, `--quiet`, and `--verbose`.
|
||||
- `scripts/test_tmdb.py` is an offline unittest/pytest suite; all HTTP behavior is mocked.
|
||||
- Requires Python 3.8+ and `requests`. No service is started by this skill.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"skill_name": "tmdb",
|
||||
"evals": [
|
||||
{
|
||||
"id": "search-movie",
|
||||
"prompt": "Find the top five TMDb movie results for Dune.",
|
||||
"expected_output": "Use tmdb movie search with --term and --limit, then inspect JSON results.",
|
||||
"assertions": ["uses movie search", "limits results"]
|
||||
},
|
||||
{
|
||||
"id": "imdb-find-detail-pipeline",
|
||||
"prompt": "Starting from IMDb tt0111161, find the TMDb movie and fetch credits and providers.",
|
||||
"expected_output": "Call /find/{external_id} with external_source=imdb_id, extract movie_results[0].id, then request movie details with append_to_response.",
|
||||
"assertions": ["documents IMDb entry point", "extracts movie_results id", "uses append_to_response"]
|
||||
},
|
||||
{
|
||||
"id": "auth-mode-gotcha",
|
||||
"prompt": "Explain TMDb API key versus read access token authentication and diagnose a 401.",
|
||||
"expected_output": "Use either api_key query authentication or Authorization Bearer, not both; inspect status_code 7 and status_message.",
|
||||
"assertions": ["distinguishes v3 key and bearer", "names 401 symptom"]
|
||||
},
|
||||
{
|
||||
"id": "discover-rated-movies",
|
||||
"prompt": "Discover highly rated horror movies released in a date window.",
|
||||
"expected_output": "Use discover/movie with genre, vote_average.gte, vote_count.gte, and release date filters, then process JSON with jq.",
|
||||
"assertions": ["uses discover filters", "guards vote averages with vote count"]
|
||||
},
|
||||
{
|
||||
"id": "not-for-torrents",
|
||||
"prompt": "Search torrent sites for a movie download.",
|
||||
"expected_output": "Do not route this to TMDb; it is a piracy or torrent-search request rather than metadata discovery.",
|
||||
"assertions": ["must not trigger tmdb", "refuses torrent search"]
|
||||
},
|
||||
{
|
||||
"id": "trending-json",
|
||||
"prompt": "Show movies trending this week as machine-readable JSON.",
|
||||
"expected_output": "Run tmdb trending with --window week and --json.",
|
||||
"assertions": ["uses trending endpoint", "uses json output"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# TMDb Authentication, Pagination, and Errors
|
||||
|
||||
## Choose one application credential
|
||||
|
||||
TMDb v3 accepts either `api_key` as a query parameter or an API Read Access Token in `Authorization: Bearer <token>`. Both methods provide the same access level across v3; the read token also works across v4. Obtain both from the account API settings page. Send one method, not both, so an accidental stale query key cannot obscure a rejected bearer token.
|
||||
|
||||
```bash
|
||||
curl -H 'accept: application/json' \
|
||||
-H "Authorization: Bearer $TMDB_ACCESS_TOKEN" \
|
||||
'https://api.themoviedb.org/3/movie/550'
|
||||
# Alternative: .../movie/550?api_key=$TMDB_API_KEY
|
||||
```
|
||||
|
||||
A bad credential commonly returns HTTP 401 with `status_code: 7` and `Invalid API key: You must be granted a valid key.` Permission failures use code 3 and `Authentication failed: You do not have permissions to access the service.` Do not confuse code 33, which is an invalid request token. The CLI reports the 401 response rather than retrying with a second credential.
|
||||
|
||||
## Pages and rate limits
|
||||
|
||||
Search and discover responses contain `page`, `results`, `total_pages`, and `total_results`; pages contain up to 20 results. Page numbers start at 1 and max out at 500. Requests beyond that limit return a validation error, rather than being silently clamped. Search/discover access is effectively capped at 10,000 items even when totals advertise more. Trending has a larger documented sample ceiling.
|
||||
|
||||
TMDb's current guidance describes a soft limit around 40 requests per second, subject to change. On HTTP 429, respect `Retry-After`; the service may also expose `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`. Exponential backoff is safer than tight retry loops.
|
||||
|
||||
## Parameters and images
|
||||
|
||||
Use `language=en-US` (an ISO 639-1 language plus ISO 3166-1 region) for deterministic localized fields. `region=US` selects or filters release dates for that market. Image URLs combine the secure base URL from `/3/configuration`, a valid size, and the returned path: `https://image.tmdb.org/t/p/w500/<POSTER_PATH>`. Common poster sizes include `w92`, `w185`, `w342`, `w500`, `w780`, and `original`; backdrop sizes differ.
|
||||
|
||||
## Sources
|
||||
|
||||
- https://developer.themoviedb.org/docs/authentication-application
|
||||
- https://developer.themoviedb.org/reference/authentication
|
||||
- https://www.themoviedb.org/documentation/api/status-codes
|
||||
- https://developer.themoviedb.org/docs/rate-limiting
|
||||
- https://developer.themoviedb.org/reference/search-movie
|
||||
- https://developer.themoviedb.org/docs/languages
|
||||
- https://developer.themoviedb.org/docs/region-support
|
||||
- https://developer.themoviedb.org/docs/image-basics
|
||||
- https://developer.themoviedb.org/reference/configuration-details
|
||||
@@ -0,0 +1,34 @@
|
||||
# External IDs, Details, and Compound Responses
|
||||
|
||||
## Start with an IMDb ID
|
||||
|
||||
`GET /3/find/{external_id}?external_source=imdb_id` maps a foreign identifier to TMDb objects. The current `external_source` enum includes `imdb_id`, `facebook_id`, `instagram_id`, `tvdb_id`, `tiktok_id`, `twitter_id`, `wikidata_id`, and `youtube_id`; older Freebase values are obsolete. The response has `movie_results`, `person_results`, `tv_results`, `tv_episode_results`, and `tv_season_results` arrays. Unmatched categories are empty arrays. For an IMDb movie, extract `.movie_results[0].id` before calling the movie details endpoint.
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TMDB_ACCESS_TOKEN" \
|
||||
'https://api.themoviedb.org/3/find/tt0111161?external_source=imdb_id' \
|
||||
| jq -r '.movie_results[0].id'
|
||||
```
|
||||
|
||||
## Details and append_to_response
|
||||
|
||||
Movie details expose fields such as `title`, `overview`, `genres`, `runtime`, `release_date`, `vote_average`, `vote_count`, `budget`, `revenue`, `imdb_id`, and production companies. TV details use `name`, `first_air_date`, `number_of_seasons`, `number_of_episodes`, `created_by`, `networks`, and `genres`.
|
||||
|
||||
Details endpoints accept `append_to_response`, a comma-separated list of sub-endpoints within the same namespace, with a maximum of 20 appended calls. Common movie tokens include `credits`, `images`, `videos`, `recommendations`, `similar`, `reviews`, `release_dates`, `watch/providers`, `external_ids`, `alternative_titles`, and `translations`; TV adds `aggregate_credits` and `content_ratings`. Encode the slash when needed (`watch%2Fproviders`). Returned keys mirror the requested token, so jq accesses the provider object as `."watch/providers"`.
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TMDB_ACCESS_TOKEN" \
|
||||
'https://api.themoviedb.org/3/movie/550?append_to_response=credits,videos,watch%2Fproviders' \
|
||||
| jq '{title, runtime, director: [.credits.crew[] | select(.job == "Director") | .name], cast: [.credits.cast[0:5][].name], us: .["watch/providers"].results.US}'
|
||||
```
|
||||
|
||||
Credits contain `cast[]` (including `id`, `name`, `character`, `order`) and `crew[]` (including `department`, `job`). Watch-provider regions contain `link`, `flatrate`, `rent`, and `buy` arrays. Release dates nest under `results[].release_dates[]`; content ratings nest under `results[]`. TMDb requires attribution and a link to JustWatch when displaying provider data.
|
||||
|
||||
## Sources
|
||||
|
||||
- https://developer.themoviedb.org/reference/find-by-id
|
||||
- https://developer.themoviedb.org/reference/movie-details
|
||||
- https://developer.themoviedb.org/reference/movie-credits
|
||||
- https://developer.themoviedb.org/reference/movie-watch-providers
|
||||
- https://developer.themoviedb.org/reference/movie-release-dates
|
||||
- https://developer.themoviedb.org/reference/tv-content-ratings
|
||||
@@ -0,0 +1,27 @@
|
||||
# Search, Discover, Trending, and Lists
|
||||
|
||||
## Search
|
||||
|
||||
Use `/search/movie` with required `query`; `include_adult` defaults to false. `/search/tv` supports `first_air_date_year`, while `/search/multi` combines movie, TV, and person results. Search responses expose `page`, `results`, `total_pages`, and `total_results`. Keep `language=en-US` explicit when scripts need stable output.
|
||||
|
||||
## Discover
|
||||
|
||||
`/discover/movie` and `/discover/tv` filter catalog metadata. Useful movie filters include `with_genres`, `vote_count.gte`, `vote_average.gte`, `primary_release_date.gte/lte`, and certification fields. TV uses `first_air_date.gte/lte`; discover TV does not expose movie certification filters. The current docs state that comma-separated genre IDs are an AND query and pipe-separated IDs are an OR query. Provider filters such as `with_watch_providers` require `watch_region`.
|
||||
|
||||
Avoid sorting only by `vote_average.desc`: require a meaningful `vote_count.gte` threshold or a tiny-vote title can dominate. Upcoming and now-playing lists are specialized release-date views; `region` controls the market.
|
||||
|
||||
## Trending and lists
|
||||
|
||||
Trending uses `/trending/{all|movie|tv|person}/{day|week}`. `all` results carry `media_type`, which lets a consumer branch to movie or TV detail calls. Genre lists return `{genres: [{id, name}]}`. Certification lists group entries under `certifications.US` (with certification, meaning, and order).
|
||||
|
||||
## Sources
|
||||
|
||||
- https://developer.themoviedb.org/reference/search-movie
|
||||
- https://developer.themoviedb.org/reference/search-tv
|
||||
- https://developer.themoviedb.org/reference/search-multi
|
||||
- https://developer.themoviedb.org/reference/discover-movie
|
||||
- https://developer.themoviedb.org/reference/discover-tv
|
||||
- https://developer.themoviedb.org/reference/trending-all
|
||||
- https://developer.themoviedb.org/reference/genre-movie-list
|
||||
- https://developer.themoviedb.org/reference/certification-movie-list
|
||||
- https://developer.themoviedb.org/docs/region-support
|
||||
@@ -0,0 +1,67 @@
|
||||
import importlib.machinery
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
SCRIPT = Path(__file__).with_name("tmdb")
|
||||
|
||||
|
||||
def load_cli():
|
||||
loader = importlib.machinery.SourceFileLoader("tmdb_cli", str(SCRIPT))
|
||||
spec = importlib.util.spec_from_loader("tmdb_cli", loader)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class TmdbCliTests(unittest.TestCase):
|
||||
def run_cli(self, *args):
|
||||
env = os.environ.copy()
|
||||
env.pop("TMDB_ACCESS_TOKEN", None)
|
||||
env.pop("TMDB_API_KEY", None)
|
||||
return subprocess.run([str(SCRIPT), *args], text=True, capture_output=True, env=env)
|
||||
|
||||
def test_help_lists_entry_points(self):
|
||||
result = self.run_cli("--help")
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertIn("find", result.stdout)
|
||||
self.assertIn("movie", result.stdout)
|
||||
|
||||
def test_missing_required_search_term_is_argument_error(self):
|
||||
result = self.run_cli("movie", "search")
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("--term", result.stderr)
|
||||
|
||||
def test_dry_run_find_emits_json_without_credentials(self):
|
||||
result = self.run_cli("--dry-run", "--json", "find", "tt0111161")
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertTrue(json.loads(result.stdout)["dry_run"])
|
||||
|
||||
def test_mocked_external_lookup_uses_source_and_parses_results(self):
|
||||
cli = load_cli()
|
||||
client = cli.TMDBClient()
|
||||
client.find_external = Mock(return_value={"movie_results": [{"id": 550, "title": "Fight Club"}]})
|
||||
cli.GLOBAL_FLAGS = {"json": True, "dry_run": False, "quiet": False, "verbose": False}
|
||||
with patch("builtins.print") as printed:
|
||||
cli.cmd_find(client, ["tt0137523", "--source", "imdb_id"])
|
||||
payload = json.loads(printed.call_args.args[0])
|
||||
self.assertEqual(payload["movie_results"][0]["id"], 550)
|
||||
client.find_external.assert_called_once_with("tt0137523", "imdb_id")
|
||||
|
||||
def test_mocked_detail_passes_append_to_response(self):
|
||||
cli = load_cli()
|
||||
client = cli.TMDBClient()
|
||||
client.get_movie = Mock(return_value={"id": 550, "title": "Fight Club", "credits": {"cast": []}})
|
||||
cli.GLOBAL_FLAGS = {"json": True, "dry_run": False, "quiet": False, "verbose": False}
|
||||
with patch("builtins.print"):
|
||||
cli.cmd_movie_detail(client, ["550", "--append", "credits,videos"])
|
||||
client.get_movie.assert_called_once_with("550", "credits,videos")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+40
-5
@@ -127,11 +127,16 @@ class TMDBClient:
|
||||
def get_trending(self, media_type="movie", window="week"):
|
||||
return self._get(f"/trending/{media_type}/{window}")
|
||||
|
||||
def get_movie(self, movie_id):
|
||||
return self._get(f"/movie/{movie_id}")
|
||||
def get_movie(self, movie_id, append=None):
|
||||
params = {"append_to_response": append} if append else None
|
||||
return self._get(f"/movie/{movie_id}", params)
|
||||
|
||||
def get_tv(self, tv_id):
|
||||
return self._get(f"/tv/{tv_id}")
|
||||
def get_tv(self, tv_id, append=None):
|
||||
params = {"append_to_response": append} if append else None
|
||||
return self._get(f"/tv/{tv_id}", params)
|
||||
|
||||
def find_external(self, external_id, source, language="en-US"):
|
||||
return self._get(f"/find/{external_id}", {"external_source": source, "language": language})
|
||||
|
||||
def get_movie_genres(self, lang="en-US"):
|
||||
return self._get("/genre/movie/list", {"language": lang})
|
||||
@@ -185,6 +190,31 @@ def cmd_movie_search(client, args):
|
||||
{"total": data.get("total_results"), "results": results})
|
||||
|
||||
|
||||
def cmd_movie_detail(client, args):
|
||||
p = argparse.ArgumentParser(prog="tmdb movie detail")
|
||||
p.add_argument("movie_id")
|
||||
p.add_argument("--append", default=None, help="comma-separated subresources")
|
||||
parsed, _ = p.parse_known_args(args)
|
||||
if client.dry_run:
|
||||
return emit("[dry-run] Get movie details", {"dry_run": True})
|
||||
data = client.get_movie(parsed.movie_id, parsed.append) or {}
|
||||
emit(data.get("title", "Movie details"), data)
|
||||
|
||||
|
||||
def cmd_find(client, args):
|
||||
p = argparse.ArgumentParser(prog="tmdb find")
|
||||
p.add_argument("external_id")
|
||||
p.add_argument("--source", default="imdb_id", choices=[
|
||||
"imdb_id", "facebook_id", "instagram_id", "tvdb_id", "tiktok_id",
|
||||
"twitter_id", "wikidata_id", "youtube_id",
|
||||
])
|
||||
parsed, _ = p.parse_known_args(args)
|
||||
if client.dry_run:
|
||||
return emit("[dry-run] Find external ID", {"dry_run": True})
|
||||
data = client.find_external(parsed.external_id, parsed.source) or {}
|
||||
emit("External ID results", data)
|
||||
|
||||
|
||||
def cmd_movie_discover(client, args):
|
||||
p = argparse.ArgumentParser(prog="tmdb movie discover")
|
||||
p.add_argument("--genre", help="Genre name (e.g. horror, comedy)")
|
||||
@@ -349,6 +379,7 @@ def main():
|
||||
msub = mp.add_subparsers(dest="action")
|
||||
s1 = msub.add_parser("search", help="Search movies"); s1.add_argument("--term", "-t", required=True); s1.add_argument("--limit", type=int, default=10)
|
||||
s2 = msub.add_parser("discover", help="Discover movies"); s2.add_argument("--genre"); s2.add_argument("--certification"); s2.add_argument("--rating", type=float); s2.add_argument("--from", dest="release_date_gte"); s2.add_argument("--to", dest="release_date_lte"); s2.add_argument("--limit", type=int, default=10)
|
||||
sdetail = msub.add_parser("detail", help="Get movie details"); sdetail.add_argument("movie_id"); sdetail.add_argument("--append")
|
||||
msub.add_parser("upcoming", help="Upcoming movies").add_argument("--limit", type=int, default=10)
|
||||
msub.add_parser("now-playing", help="Now playing movies").add_argument("--limit", type=int, default=10)
|
||||
|
||||
@@ -361,6 +392,7 @@ def main():
|
||||
# flat
|
||||
sub.add_parser("genre", help="List genres").add_argument("--type", required=True, choices=["movie", "tv"])
|
||||
sub.add_parser("certification", help="List certifications")
|
||||
fp = sub.add_parser("find", help="Find by external ID"); fp.add_argument("external_id"); fp.add_argument("--source", default="imdb_id")
|
||||
|
||||
tr = sub.add_parser("trending", help="Trending content")
|
||||
tr.add_argument("--type", default="movie", choices=["movie", "tv", "all"])
|
||||
@@ -378,6 +410,7 @@ def main():
|
||||
if args.resource == "movie":
|
||||
if args.action == "search": cmd_movie_search(client, filtered_argv[filtered_argv.index("search")+1:])
|
||||
elif args.action == "discover": cmd_movie_discover(client, filtered_argv[filtered_argv.index("discover")+1:])
|
||||
elif args.action == "detail": cmd_movie_detail(client, filtered_argv[filtered_argv.index("detail")+1:])
|
||||
elif args.action == "upcoming": cmd_upcoming(client, filtered_argv[filtered_argv.index("upcoming")+1:])
|
||||
else: parser.print_help()
|
||||
elif args.resource == "tv":
|
||||
@@ -389,7 +422,9 @@ def main():
|
||||
elif args.resource == "genre":
|
||||
cmd_genre_list(client, filtered_argv[filtered_argv.index("list")+1:])
|
||||
elif args.resource == "certification":
|
||||
cmd_cert_list(client, filtered_argv[filtered_argv.index("list")+1:])
|
||||
cmd_cert_list(client, filtered_argv[filtered_argv.index("certification")+1:])
|
||||
elif args.resource == "find":
|
||||
cmd_find(client, filtered_argv[filtered_argv.index("find")+1:])
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user