2
# -*- coding: utf-8 -*-
3
#-----------------------
4
# Name: tmdb_api.py Simple-to-use Python interface to TMDB's API v3
6
# Author: Raymond Wagner
7
# Purpose: This Python library is intended to provide a series of classes
8
# and methods for search and retrieval of text metadata and image
10
# Preliminary API specifications can be found at
11
# http://help.themoviedb.org/kb/api/about-3
12
# License: Creative Commons GNU GPL v2
13
# (http://creativecommons.org/licenses/GPL/2.0/)
14
#-----------------------
16
__title__ = "tmdb_api - Simple-to-use Python interface to TMDB's API v3 "+\
17
"(www.themoviedb.org)"
18
__author__ = "Raymond Wagner"
20
This Python library is intended to provide a series of classes and methods
21
for search and retrieval of text metadata and image URLs from TMDB.
22
Preliminary API specifications can be found at
23
http://help.themoviedb.org/kb/api/about-3"""
26
# 0.1.0 Initial development
27
# 0.2.0 Add caching mechanism for API queries
28
# 0.2.1 Temporary work around for broken search paging
29
# 0.3.0 Rework backend machinery for managing OO interface to results
30
# 0.3.1 Add collection support
31
# 0.3.2 Remove MythTV key from results.py
32
# 0.3.3 Add functional language support
33
# 0.3.4 Re-enable search paging
35
from request import set_key, Request
36
from util import PagedList, Datapoint, Datalist, Datadict, Element
37
from tmdb_exceptions import *
46
class Configuration( Element ):
47
images = Datapoint('images')
49
return Request('configuration')
50
Configuration = Configuration()
52
def searchMovie(query, language='en', adult=False):
53
return MovieSearchResults(
54
Request('search/movie', query=query, include_adult=adult),
57
class MovieSearchResults( PagedList ):
58
"""Stores a list of search matches."""
59
def _process(self, data):
60
for item in data['results']:
61
yield Movie(raw=item, language=self.language)
63
def _getpage(self, page):
64
self.request = self.request.new(page=page)
65
return list(self._process(self.request.readJSON()))
67
def __init__(self, request, language=None):
68
self.language=language
70
self.request = request.new(language=language)
71
res = self.request.readJSON()
72
super(MovieSearchResults, self).__init__(res, res['total_results'], 20)
75
return u"<Search Results: {0}>".format(self.request._kwargs['query'])
77
def searchPerson(query):
78
return PeopleSearchResults(Request('search/person', query=query))
80
class PeopleSearchResults( PagedList ):
83
for item in data['results']:
84
yield Person(raw=item)
86
def _getpage(self, page):
87
self.request = self.request.new(page=page)
88
return list(self._process(self.request.readJSON()))
90
def __init__(self, request):
91
self.request = request
92
res = self.request.readJSON()
93
super(PeopleSearchResults, self).__init__(res, res['total_results'], 20)
96
return u"<Search Results: {0}>".format(self.request._kwargs['query'])
98
class Image( Element ):
99
filename = Datapoint('file_path', initarg=1, handler=lambda x: x.lstrip('/'))
100
aspectratio = Datapoint('aspect_ratio')
101
height = Datapoint('height')
102
width = Datapoint('width')
103
language = Datapoint('iso_639_1')
108
def geturl(self, size='original'):
109
if size not in self.sizes():
110
raise TMDBImageSizeError
111
url = Configuration.images['base_url'].rstrip('/')
112
return url+'/{0}/{1}'.format(size, self.filename)
115
return u"<{0.__class__.__name__} '{0.filename}'>".format(self)
117
class Backdrop( Image ):
119
return Configuration.images['backdrop_sizes']
120
class Poster( Image ):
122
return Configuration.images['poster_sizes']
123
class Profile( Image ):
125
return Configuration.images['profile_sizes']
127
class AlternateTitle( Element ):
128
country = Datapoint('iso_3166_1')
129
title = Datapoint('title')
131
class Person( Element ):
132
id = Datapoint('id', initarg=1)
133
name = Datapoint('name')
134
biography = Datapoint('biography')
135
dayofbirth = Datapoint('birthday', default=None, handler=lambda x: \
136
datetime.datetime.strptime(x, '%Y-%m-%d'))
137
dayofdeath = Datapoint('deathday', default=None, handler=lambda x: \
138
datetime.datetime.strptime(x, '%Y-%m-%d'))
139
homepage = Datapoint('homepage')
140
birthplace = Datapoint('place_of_birth')
141
profile = Datapoint('profile_path', handler=Profile, raw=False)
144
return u"<{0} '{1.name}' at {2}>".\
145
format(self.__class__.__name__, self, hex(id(self)))
148
return Request('person/{0}'.format(self.id))
149
def _populate_credits(self):
150
return Request('person/{0}/credits'.format(self.id), language=self._lang)
151
def _populate_images(self):
152
return Request('person/{0}/images'.format(self.id))
154
roles = Datalist('cast', handler=lambda x: ReverseCast(raw=x), poller=_populate_credits)
155
crew = Datalist('crew', handler=lambda x: ReverseCrew(raw=x), poller=_populate_credits)
156
profiles = Datalist('profiles', handler=Profile, poller=_populate_images)
158
class Cast( Person ):
159
character = Datapoint('character')
160
order = Datapoint('order')
163
return u"<{0} '{1.name}' as '{1.character}' at {2}>".\
164
format(self.__class__.__name__, self, hex(id(self)))
166
class Crew( Person ):
167
job = Datapoint('job')
168
department = Datapoint('department')
171
return u"<{0.__class__.__name__} '{1.name}','{1.job}'>".format(self)
173
class Keyword( Element ):
175
name = Datapoint('name')
178
return u"<{0.__class__.__name__} {0.name}>".format(self)
180
class Release( Element ):
181
certification = Datapoint('certification')
182
country = Datapoint('iso_3166_1')
183
releasedate = Datapoint('release_date', handler=lambda x: \
184
datetime.datetime.strptime(x, '%Y-%m-%d'))
186
return u"<Release {0.country}, {0.releasedate}>".format(self)
188
class Trailer( Element ):
189
name = Datapoint('name')
190
size = Datapoint('size')
191
source = Datapoint('source')
193
class Translation( Element ):
194
name = Datapoint('name')
195
language = Datapoint('iso_639_1')
196
englishname = Datapoint('english_name')
198
class Genre( Element ):
200
name = Datapoint('name')
203
return u"<{0.__class__.__name__} '{0.name}'>".format(self)
205
class Studio( Element ):
207
name = Datapoint('name')
210
return u"<{0.__class__.__name__} '{0.name}'>".format(self)
212
class Country( Element ):
213
code = Datapoint('iso_3166_1')
214
name = Datapoint('name')
217
return u"<{0.__class__.__name__} '{0.name}'>".format(self)
219
class Language( Element ):
220
code = Datapoint('iso_639_1')
221
name = Datapoint('name')
224
return u"<{0.__class__.__name__} '{0.name}'>".format(self)
226
class Movie( Element ):
229
req = Request('latest/movie')
231
return cls(raw=req.readJSON())
233
id = Datapoint('id', initarg=1)
234
title = Datapoint('title')
235
originaltitle = Datapoint('original_title')
236
tagline = Datapoint('tagline')
237
overview = Datapoint('overview')
238
runtime = Datapoint('runtime')
239
budget = Datapoint('budget')
240
revenue = Datapoint('revenue')
241
releasedate = Datapoint('release_date', handler=lambda x: \
242
datetime.datetime.strptime(x, '%Y-%m-%d'))
243
homepage = Datapoint('homepage')
244
imdb = Datapoint('imdb_id')
246
backdrop = Datapoint('backdrop_path', handler=Backdrop, raw=False)
247
poster = Datapoint('poster_path', handler=Poster, raw=False)
249
popularity = Datapoint('popularity')
250
userrating = Datapoint('vote_average')
251
votes = Datapoint('vote_count')
253
adult = Datapoint('adult')
254
collection = Datapoint('belongs_to_collection', handler=lambda x: Collection(raw=x))
255
genres = Datalist('genres', handler=Genre)
256
studios = Datalist('production_companies', handler=Studio)
257
countries = Datalist('production_countries', handler=Country)
258
languages = Datalist('spoken_languages', handler=Language)
261
return Request('movie/{0}'.format(self.id), language=self._lang)
262
def _populate_titles(self):
263
return Request('movie/{0}/alternative_titles'.format(self.id))
264
def _populate_cast(self):
265
return Request('movie/{0}/casts'.format(self.id))
266
def _populate_images(self):
267
return Request('movie/{0}/images'.format(self.id), language=self._lang)
268
def _populate_keywords(self):
269
return Request('movie/{0}/keywords'.format(self.id))
270
def _populate_releases(self):
271
return Request('movie/{0}/releases'.format(self.id))
272
def _populate_trailers(self):
273
return Request('movie/{0}/trailers'.format(self.id), language=self._lang)
274
def _populate_translations(self):
275
return Request('movie/{0}/translations'.format(self.id))
277
alternate_titles = Datalist('titles', handler=AlternateTitle, poller=_populate_titles)
278
cast = Datalist('cast', handler=Cast, poller=_populate_cast, sort='order')
279
crew = Datalist('crew', handler=Crew, poller=_populate_cast)
280
backdrops = Datalist('backdrops', handler=Backdrop, poller=_populate_images)
281
posters = Datalist('posters', handler=Poster, poller=_populate_images)
282
keywords = Datalist('keywords', handler=Keyword, poller=_populate_keywords)
283
releases = Datadict('countries', handler=Release, poller=_populate_releases, attr='country')
284
youtube_trailers = Datalist('youtube', handler=Trailer, poller=_populate_trailers)
285
apple_trailers = Datalist('quicktime', handler=Trailer, poller=_populate_trailers)
286
translations = Datalist('translations', handler=Translation, poller=_populate_translations)
289
if self.title is not None:
290
s = u"'{0}'".format(self.title)
291
elif self.originaltitle is not None:
292
s = u"'{0}'".format(self.originaltitle)
296
s = u"{0} ({1})".format(s, self.releasedate.year)
297
return u"<{0} {1}>".format(self.__class__.__name__, s).encode('utf-8')
299
class ReverseCast( Movie ):
300
character = Datapoint('character')
303
if self.title is not None:
304
s = u"'{0}'".format(self.title)
305
elif self.originaltitle is not None:
306
s = u"'{0}'".format(self.originaltitle)
310
s = u"{0} ({1})".format(s, self.releasedate.year)
311
return u"<{0.__class__.__name__} '{0.character}' on {1}>".format(self, s).encode('utf-8')
313
class ReverseCrew( Movie ):
314
department = Datapoint('department')
315
job = Datapoint('job')
318
if self.title is not None:
319
s = u"'{0}'".format(self.title)
320
elif self.originaltitle is not None:
321
s = u"'{0}'".format(self.originaltitle)
325
s = u"{0} ({1})".format(s, self.releasedate.year)
326
return u"<{0.__class__.__name__} '{0.job}' for {1}>".format(self, s).encode('utf-8')
328
class Collection( Element ):
329
id = Datapoint('id', initarg=1)
330
name = Datapoint('name')
331
backdrop = Datapoint('backdrop_path', handler=Backdrop, raw=False)
332
poster = Datapoint('poster_path', handler=Poster, raw=False)
333
members = Datalist('parts', handler=Movie, sort='releasedate')
336
return Request('collection/{0}'.format(self.id), language=self._lang)
339
return u"<{0.__class__.__name__} '{0.name}'>".format(self).encode('utf-8')
340
return u"<{0} {1}>".format(self.__class__.__name__, s).encode('utf-8')
342
if __name__ == '__main__':
343
set_key('c27cb71cff5bd76e1a7a009380562c62') #MythTV API Key
346
banner = 'tmdb_api interactive shell.'
349
import readline, rlcompleter
353
readline.parse_and_bind("tab: complete")
354
banner += ' TAB completion available.'
355
namespace = globals().copy()
356
namespace.update(locals())
357
code.InteractiveConsole(namespace).interact(banner)