-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtmdb_client.py
More file actions
156 lines (131 loc) · 5.63 KB
/
tmdb_client.py
File metadata and controls
156 lines (131 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""TMDB API client for Movies and TV Shows."""
import json
import os
import requests
class TMDBClient:
API_BASE = "https://api.themoviedb.org/3"
IMAGE_BASE = "https://image.tmdb.org/t/p/w300"
def __init__(self, creds_path="creds.json"):
# Prefer environment variable; fall back to creds.json
self.api_key = os.environ.get("TMDB_API_KEY")
if not self.api_key:
try:
with open(creds_path) as f:
creds = json.load(f)
self.api_key = creds.get("tmdb_api_key")
except FileNotFoundError:
pass
if not self.api_key:
raise RuntimeError(
"TMDB API key not found. Set TMDB_API_KEY environment variable, "
"or add 'tmdb_api_key' to your creds.json file. See README for details."
)
self._genre_cache_movie = {}
self._genre_cache_tv = {}
def _get(self, endpoint, params=None):
"""Make a GET request to the TMDB API."""
url = f"{self.API_BASE}/{endpoint}"
if params is None:
params = {}
params["api_key"] = self.api_key
resp = requests.get(url, params=params, timeout=10)
resp.raise_for_status()
return resp.json()
def _load_genres(self, media):
"""Load and cache genre ID → name mapping."""
cache = self._genre_cache_movie if media == "movie" else self._genre_cache_tv
if cache:
return cache
data = self._get(f"genre/{media}/list")
mapping = {g["id"]: g["name"] for g in data.get("genres", [])}
if media == "movie":
self._genre_cache_movie = mapping
else:
self._genre_cache_tv = mapping
return mapping
def _genre_names(self, genre_ids, media):
"""Convert list of genre IDs to comma-separated names."""
mapping = self._load_genres(media)
return ", ".join(mapping.get(gid, "") for gid in genre_ids if mapping.get(gid))
# ── Movies ────────────────────────────────────────────────────
def search_movies(self, query, limit=20):
"""Search TMDB for movies. Returns list of movie dicts."""
data = self._get("search/movie", {"query": query})
results = data.get("results", [])[:limit]
movies = []
for m in results:
release_year = None
rd = m.get("release_date", "")
if rd and len(rd) >= 4:
release_year = int(rd[:4])
movies.append({
"external_id": m["id"],
"name": m.get("title", "Unknown"),
"cover_url": f"{self.IMAGE_BASE}{m['poster_path']}" if m.get("poster_path") else None,
"release_year": release_year,
"meta_line": self._genre_names(m.get("genre_ids", []), "movie"),
"genres": self._genre_names(m.get("genre_ids", []), "movie"),
"summary": m.get("overview", ""),
})
return movies
def get_movie_by_id(self, movie_id):
"""Fetch a single movie by TMDB ID."""
try:
m = self._get(f"movie/{movie_id}")
except Exception:
return None
release_year = None
rd = m.get("release_date", "")
if rd and len(rd) >= 4:
release_year = int(rd[:4])
genres = ", ".join(g["name"] for g in m.get("genres", []))
return {
"external_id": m["id"],
"name": m.get("title", "Unknown"),
"cover_url": f"{self.IMAGE_BASE}{m['poster_path']}" if m.get("poster_path") else None,
"release_year": release_year,
"meta_line": genres,
"genres": genres,
"summary": m.get("overview", ""),
}
# ── TV Shows ──────────────────────────────────────────────────
def search_tv(self, query, limit=20):
"""Search TMDB for TV shows. Returns list of show dicts."""
data = self._get("search/tv", {"query": query})
results = data.get("results", [])[:limit]
shows = []
for s in results:
release_year = None
rd = s.get("first_air_date", "")
if rd and len(rd) >= 4:
release_year = int(rd[:4])
shows.append({
"external_id": s["id"],
"name": s.get("name", "Unknown"),
"cover_url": f"{self.IMAGE_BASE}{s['poster_path']}" if s.get("poster_path") else None,
"release_year": release_year,
"meta_line": self._genre_names(s.get("genre_ids", []), "tv"),
"genres": self._genre_names(s.get("genre_ids", []), "tv"),
"summary": s.get("overview", ""),
})
return shows
def get_tv_by_id(self, tv_id):
"""Fetch a single TV show by TMDB ID."""
try:
s = self._get(f"tv/{tv_id}")
except Exception:
return None
release_year = None
rd = s.get("first_air_date", "")
if rd and len(rd) >= 4:
release_year = int(rd[:4])
genres = ", ".join(g["name"] for g in s.get("genres", []))
return {
"external_id": s["id"],
"name": s.get("name", "Unknown"),
"cover_url": f"{self.IMAGE_BASE}{s['poster_path']}" if s.get("poster_path") else None,
"release_year": release_year,
"meta_line": genres,
"genres": genres,
"summary": s.get("overview", ""),
}