Claude Skill

tmdb

Query TMDb metadata for films and television, then enrich results with details, credits, providers, and external IDs. Do not use this skill for personal watch history, watchlists, or tracking; use `trakt` for user activity and watch-state workflows.

LLM Mart · 0 points · 4 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download magnus919-agent-skills-tmdb-d0edebb.zip · 15 KB
Part of magnus919/agent-skills — 145 skills

Install

skills CLI npx skills add https://github.com/magnus919/agent-skills/tree/main/tmdb
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install magnus919-agent-skills@llmmart
Git git clone https://github.com/magnus919/agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole magnus919/agent-skills collection as a plugin from our marketplace. Git is the plain clone.

README

TMDb Metadata Skill

Why Install This Skill

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.

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

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

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 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 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.

Skill manifest

TMDb metadata from the terminal

Setup

Create credentials at TMDb API settings. Prefer the API Read Access Token:

export TMDB_ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
# Or use the v3 key: export TMDB_API_KEY="YOUR_API_KEY"

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

Search and identify

tmdb movie search --term "dune" --limit 5 --json
tmdb tv search --term "severance" --limit 5
tmdb find tt0111161 --source imdb_id --json

--source accepts one of the official external-source values: imdb_id, facebook_id, instagram_id, tvdb_id, tiktok_id, twitter_id, wikidata_id, and youtube_id. Freebase lookups are not supported: the retired freebase_mid and freebase_id values are rejected. The response is split into movie_results, tv_results, person_results, tv_season_results, and tv_episode_results.

Details and enrichment

tmdb movie detail 550 --append credits,videos --json
tmdb movie detail 550 --append 'credits,watch/providers,external_ids' --json

Compound responses use the requested names as top-level keys. Encode the slash in watch/providers when constructing raw URLs.

Discover and browse

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 genre list --type tv --json
tmdb certification --json

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:
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)
  1. Fetch details and compound resources:
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}'

Search then detail

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

Filter reliable discoveries

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. Only the eight documented external_source values are valid, and the retired Freebase sources (freebase_mid, freebase_id) are rejected.
  • 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 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 Credentials, pagination, rate limits, errors, language, regions, and images
references/find-and-details.md IMDb/TVDB lookup, response mapping, detail fields, compound requests
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.
Files (agent-skills)
  • evals
    • evals.json 2 KB
      {
        "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"]
          }
        ]
      }
      
  • references
    • auth-pagination-and-errors.md 2.6 KB
      # 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
      
    • find-and-details.md 2.9 KB
      # 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 `external_source` value is chosen from exactly eight supported enum entries: `imdb_id`, `facebook_id`, `instagram_id`, `tvdb_id`, `tiktok_id`, `twitter_id`, `wikidata_id`, and `youtube_id`. Freebase lookups are not supported: the retired `freebase_mid` and `freebase_id` sources have been removed from the API and must not be used or documented as valid values. 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
      
    • search-discover-trending.md 1.9 KB
      # 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
      
  • scripts
    • test_tmdb.py 7.7 KB
      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")
      
      
      class GenreListParserTests(unittest.TestCase):
          """Regression coverage for `tmdb genre list --type movie|tv`."""
      
          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_documented_nested_form_parses_and_dispatches(self):
              result = self.run_cli("--dry-run", "--json", "genre", "list", "--type", "movie")
              self.assertEqual(result.returncode, 0)
              self.assertTrue(json.loads(result.stdout)["dry_run"])
      
          def test_flat_form_is_clean_rejection_not_crash(self):
              result = self.run_cli("--json", "genre", "--type", "tv")
              self.assertNotEqual(result.returncode, 0)
              self.assertNotIn("Traceback", result.stderr)
              self.assertIn("invalid choice", result.stderr)
      
          def test_missing_type_is_argument_error(self):
              result = self.run_cli("--json", "genre", "list")
              self.assertNotEqual(result.returncode, 0)
              self.assertIn("--type", result.stderr)
      
          def test_dispatch_passes_tail_args_without_raw_argv_token_search(self):
              cli = load_cli()
              captured = {}
              real_client_factory = cli.TMDBClient
      
              def fake_client(dry_run=False):
                  return real_client_factory(dry_run=dry_run)
      
              def fake_handler(client, args):
                  captured["args"] = args
      
              cli.cmd_genre_list = fake_handler
              with patch.object(cli.sys, "argv", ["tmdb", "--dry-run", "genre", "list", "--type", "tv"]):
                  cli.main()
              self.assertEqual(captured["args"], ["--type", "tv"])
              self.assertEqual(cli.GLOBAL_FLAGS.get("dry_run"), True)
              fake_client  # client construction stays credential-free
      
      
      class TvSearchEndpointTests(unittest.TestCase):
          """TV search must hit /search/tv via client.search_tv and format TV fields."""
      
          def load_with_flags(self):
              cli = load_cli()
              cli.GLOBAL_FLAGS = {"json": True, "dry_run": False, "quiet": False, "verbose": False}
              return cli
      
          def test_json_output_uses_search_tv_and_preserves_tv_shape(self):
              cli = self.load_with_flags()
              client = cli.TMDBClient()
              client.search_tv = Mock(return_value={
                  "page": 1,
                  "total_results": 1,
                  "results": [{"id": 9626, "name": "Poirot", "first_air_date": "1989-01-08",
                               "vote_average": 7.9}],
              })
              client.search_movie = Mock(side_effect=AssertionError("/search/movie must not be used"))
              with patch("builtins.print") as printed:
                  cli.cmd_tv_search(client, ["--term", "Poirot"])
              payload = json.loads(printed.call_args.args[0])
              self.assertEqual(payload["total"], 1)
              self.assertEqual(payload["results"][0]["name"], "Poirot")
              self.assertEqual(payload["results"][0]["first_air_date"], "1989-01-08")
              client.search_tv.assert_called_once_with("Poirot")
              client.search_movie.assert_not_called()
      
          def test_human_output_formats_name_and_first_air_date_year(self):
              cli = load_cli()
              cli.GLOBAL_FLAGS = {"json": False, "dry_run": False, "quiet": False, "verbose": False}
              client = cli.TMDBClient()
              client.search_tv = Mock(return_value={
                  "total_results": 1,
                  "results": [{"id": 9626, "name": "Poirot", "first_air_date": "1989-01-08",
                               "vote_average": 7.9}],
              })
              with patch("builtins.print") as printed:
                  cli.cmd_tv_search(client, ["--term", "Poirot"])
              line = printed.call_args.args[0]
              self.assertIn("Poirot", line)
              self.assertIn("(1989)", line)
      
      
      class FindExternalSourceTests(unittest.TestCase):
          """Exactly the official eight external_source values are accepted."""
      
          def test_all_official_sources_are_accepted_parameterized(self):
              cli = load_cli()
              cli.GLOBAL_FLAGS = {"json": True, "dry_run": False, "quiet": False, "verbose": False}
              self.assertEqual(len(cli.EXTERNAL_SOURCES), 8)
              for source in cli.EXTERNAL_SOURCES:
                  with self.subTest(source=source):
                      client = cli.TMDBClient()
                      client.find_external = Mock(return_value={})
                      with patch("builtins.print"):
                          cli.cmd_find(client, [f"ext-{source}", "--source", source])
                      client.find_external.assert_called_once_with(f"ext-{source}", source)
      
          def test_freebase_sources_are_rejected_parameterized(self):
              for retired in ("freebase_mid", "freebase_id"):
                  with self.subTest(source=retired):
                      result = self.run_cli("--json", "find", "ABC123", "--source", retired)
                      self.assertNotEqual(result.returncode, 0)
                      self.assertNotIn("Traceback", result.stderr)
                      self.assertIn(retired, result.stderr)
                      self.assertIn("invalid choice", result.stderr)
      
          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)
      
      
      if __name__ == "__main__":
          unittest.main()
      
    • tmdb 18.6 KB · in bundle
  • README.md 1.9 KB
    # TMDb Metadata Skill
    
    ## Why Install This Skill
    
    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.
    
    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
    
    | 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_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 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 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.
    
  • SKILL.md 6.4 KB
    ---
    name: tmdb
    description: >-
      Query TMDb metadata for films and television, then enrich results with details, credits,
      providers, and external IDs. Do not use this skill for personal watch history,
      watchlists, or tracking; use `trakt` for user activity and watch-state workflows.
    license: MIT
    compatibility: Requires TMDB_ACCESS_TOKEN or TMDB_API_KEY, Python 3.8+, and requests.
    metadata:
      tags: tmdb, movies, tv, film, cinema, metadata
      sources: https://developer.themoviedb.org/reference
    ---
    
    # TMDb metadata from the terminal
    
    ## Setup
    
    Create credentials at [TMDb API settings](https://www.themoviedb.org/settings/api). Prefer the API Read Access Token:
    
    ```bash
    export TMDB_ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
    # Or use the v3 key: export TMDB_API_KEY="YOUR_API_KEY"
    ```
    
    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
    
    ### Search and identify
    
    ```bash
    tmdb movie search --term "dune" --limit 5 --json
    tmdb tv search --term "severance" --limit 5
    tmdb find tt0111161 --source imdb_id --json
    ```
    
    `--source` accepts one of the official external-source values: `imdb_id`, `facebook_id`, `instagram_id`, `tvdb_id`, `tiktok_id`, `twitter_id`, `wikidata_id`, and `youtube_id`. Freebase lookups are not supported: the retired `freebase_mid` and `freebase_id` values are rejected. The response is split into `movie_results`, `tv_results`, `person_results`, `tv_season_results`, and `tv_episode_results`.
    
    ### Details and enrichment
    
    ```bash
    tmdb movie detail 550 --append credits,videos --json
    tmdb movie detail 550 --append 'credits,watch/providers,external_ids' --json
    ```
    
    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 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 genre list --type tv --json
    tmdb certification --json
    ```
    
    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
    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)
    ```
    
    2. Fetch details and compound resources:
    
    ```bash
    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}'
    ```
    
    ### Search then detail
    
    ```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
    ```
    
    ### Filter reliable discoveries
    
    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. Only the eight documented `external_source` values are valid, and the retired Freebase sources (`freebase_mid`, `freebase_id`) are rejected.
    - **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 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.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related