MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
tmdb3.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3 # ----------------------
4 # Name: tmdb3.py
5 # Python Script
6 # Author: Raymond Wagner
7 # Purpose: This python script is intended to translate lookups between the
8 # TheMovieDB.org V3 API and MythTV's internal metadata format.
9 # http://www.mythtv.org/wiki/MythVideo_Grabber_Script_Format
10 # http://help.themoviedb.org/kb/api/about-3
11 #-----------------------
12 __title__ = "TheMovieDB.org V3"
13 __author__ = "Raymond Wagner"
14 __version__ = "0.3.7"
15 # 0.1.0 Initial version
16 # 0.2.0 Add language support, move cache to home directory
17 # 0.3.0 Enable version detection to allow use in MythTV
18 # 0.3.1 Add --test parameter for proper compatibility with mythmetadatalookup
19 # 0.3.2 Add --area parameter to allow country selection for release date and
20 # parental ratings
21 # 0.3.3 Use translated title if available
22 # 0.3.4 Add support for finding by IMDB under -D (simulate previous version)
23 # 0.3.5 Add debugging mode
24 # 0.3.6 Add handling for TMDB site and library returning null results in
25 # search. This should only need to be a temporary fix, and should be
26 # resolved upstream.
27 # 0.3.7 Add handling for TMDB site returning insufficient results from a
28 # query
29 
30 from optparse import OptionParser
31 import sys
32 
33 def buildSingle(inetref, opts):
34  from MythTV.tmdb3 import Movie
35  from MythTV import VideoMetadata
36  from lxml import etree
37 
38  import re
39  if re.match('^0[0-9]{6}$', inetref):
40  movie = Movie.fromIMDB(inetref)
41  else:
42  movie = Movie(inetref)
43 
44  tree = etree.XML(u'<metadata></metadata>')
45  mapping = [['runtime', 'runtime'], ['title', 'originaltitle'],
46  ['releasedate', 'releasedate'], ['tagline', 'tagline'],
47  ['description', 'overview'], ['homepage', 'homepage'],
48  ['userrating', 'userrating'], ['popularity', 'popularity'],
49  ['budget', 'budget'], ['revenue', 'revenue']]
50  m = VideoMetadata()
51  for i,j in mapping:
52  if getattr(movie, j):
53  setattr(m, i, getattr(movie, j))
54 
55  if movie.title:
56  m.title = movie.title
57 
58  releases = movie.releases.items()
59 
60  if opts.country:
61  try:
62  # resort releases with selected country at top to ensure it
63  # is selected by the metadata libraries
64  index = zip(*releases)[0].index(opts.country)
65  releases.insert(0, releases.pop(index))
66  except ValueError:
67  pass
68  else:
69  m.releasedate = releases[0][1].releasedate
70 
71  m.inetref = str(movie.id)
72  if movie.collection:
73  m.collectionref = str(movie.collection.id)
74  if movie.releasedate:
75  m.year = movie.releasedate.year
76  for country, release in releases:
77  if release.certification:
78  m.certifications[country] = release.certification
79  for genre in movie.genres:
80  m.categories.append(genre.name)
81  for studio in movie.studios:
82  m.studios.append(studio.name)
83  for country in movie.countries:
84  m.countries.append(country.name)
85  for cast in movie.cast:
86  d = {'name':cast.name, 'character':cast.character, 'department':'Actors',
87  'job':'Actor', 'url':'http://www.themoviedb.org/people/{0}'.format(cast.id)}
88  if cast.profile: d['thumb'] = cast.profile.geturl()
89  m.people.append(d)
90  for crew in movie.crew:
91  d = {'name':crew.name, 'job':crew.job, 'department':crew.department,
92  'url':'http://www.themoviedb.org/people/{0}'.format(crew.id)}
93  if crew.profile: d['thumb'] = crew.profile.geturl()
94  m.people.append(d)
95  for backdrop in movie.backdrops:
96  m.images.append({'type':'fanart', 'url':backdrop.geturl(),
97  'thumb':backdrop.geturl(backdrop.sizes()[0]),
98  'height':str(backdrop.height),
99  'width':str(backdrop.width)})
100  for poster in movie.posters:
101  m.images.append({'type':'coverart', 'url':poster.geturl(),
102  'thumb':poster.geturl(poster.sizes()[0]),
103  'height':str(poster.height),
104  'width':str(poster.width)})
105  tree.append(m.toXML())
106  sys.stdout.write(etree.tostring(tree, encoding='UTF-8', pretty_print=True,
107  xml_declaration=True))
108  sys.exit()
109 
110 def buildList(query, opts):
111  # TEMPORARY FIX:
112  # replace all dashes from queries to work around search behavior
113  # as negative to all text that comes afterwards
114  query = query.replace('-',' ')
115 
116  from MythTV.tmdb3 import searchMovie
117  from MythTV import VideoMetadata
118  from lxml import etree
119  results = iter(searchMovie(query))
120  tree = etree.XML(u'<metadata></metadata>')
121  mapping = [['runtime', 'runtime'], ['title', 'originaltitle'],
122  ['releasedate', 'releasedate'], ['tagline', 'tagline'],
123  ['description', 'overview'], ['homepage', 'homepage'],
124  ['userrating', 'userrating'], ['popularity', 'popularity']]
125 
126  count = 0
127  while True:
128  try:
129  res = results.next()
130  except StopIteration:
131  # end of results
132  break
133  except IndexError:
134  # unexpected end of results
135  # we still want to return whatever we have so far
136  break
137 
138  if res is None:
139  # faulty data, skip it and continue
140  continue
141 
142  m = VideoMetadata()
143  for i,j in mapping:
144  if getattr(res, j):
145  setattr(m, i, getattr(res, j))
146  m.inetref = str(res.id)
147 
148  if res.title:
149  m.title = res.title
150 
151  #TODO:
152  # should releasedate and year be pulled from the country-specific data
153  # or should it be left to the default information to cut down on
154  # traffic from searches
155  if res.releasedate:
156  m.year = res.releasedate.year
157  if res.backdrop:
158  b = res.backdrop
159  m.images.append({'type':'fanart', 'url':b.geturl(),
160  'thumb':b.geturl(b.sizes()[0])})
161  if res.poster:
162  p = res.poster
163  m.images.append({'type':'coverart', 'url':p.geturl(),
164  'thumb':p.geturl(p.sizes()[0])})
165  tree.append(m.toXML())
166  count += 1
167  if count >= 60:
168  # page limiter, dont want to overload the server
169  break
170 
171  sys.stdout.write(etree.tostring(tree, encoding='UTF-8', pretty_print=True,
172  xml_declaration=True))
173  sys.exit(0)
174 
175 def buildCollection(inetref, opts):
176  from MythTV.tmdb3 import Collection
177  from MythTV import VideoMetadata
178  from lxml import etree
179  collection = Collection(inetref)
180  tree = etree.XML(u'<metadata></metadata>')
181  m = VideoMetadata()
182  m.collectionref = str(collection.id)
183  m.title = collection.name
184  if collection.backdrop:
185  b = collection.backdrop
186  m.images.append({'type':'fanart', 'url':b.geturl(),
187  'thumb':b.geturl(b.sizes()[0])})
188  if collection.poster:
189  p = collection.poster
190  m.images.append({'type':'coverart', 'url':p.geturl(),
191  'thumb':p.geturl(p.sizes()[0])})
192  tree.append(m.toXML())
193  sys.stdout.write(etree.tostring(tree, encoding='UTF-8', pretty_print=True,
194  xml_declaration=True))
195  sys.exit()
196 
198  from lxml import etree
199  version = etree.XML(u'<grabber></grabber>')
200  etree.SubElement(version, "name").text = __title__
201  etree.SubElement(version, "author").text = __author__
202  etree.SubElement(version, "thumbnail").text = 'tmdb.png'
203  etree.SubElement(version, "command").text = 'tmdb3.py'
204  etree.SubElement(version, "type").text = 'movie'
205  etree.SubElement(version, "description").text = \
206  'Search and metadata downloads for themoviedb.org'
207  etree.SubElement(version, "version").text = __version__
208  sys.stdout.write(etree.tostring(version, encoding='UTF-8', pretty_print=True,
209  xml_declaration=True))
210  sys.exit(0)
211 
213  err = 0
214  try:
215  import MythTV
216  except:
217  err = 1
218  print ("Failed to import MythTV bindings. Check your `configure` output "
219  "to make sure installation was not disabled due to external "
220  "dependencies")
221  try:
222  import MythTV.tmdb3
223  except:
224  err = 1
225  print ("Failed to import PyTMDB3 library. This should have been included "
226  "with the python MythTV bindings.")
227  try:
228  import lxml
229  except:
230  err = 1
231  print "Failed to import python lxml library."
232 
233  if not err:
234  print "Everything appears in order."
235  sys.exit(err)
236 
237 def main():
238  parser = OptionParser()
239 
240  parser.add_option('-v', "--version", action="store_true", default=False,
241  dest="version", help="Display version and author")
242  parser.add_option('-t', "--test", action="store_true", default=False,
243  dest="test", help="Perform self-test for dependencies.")
244  parser.add_option('-M', "--movielist", action="store_true", default=False,
245  dest="movielist", help="Get Movies matching search.")
246  parser.add_option('-D', "--moviedata", action="store_true", default=False,
247  dest="moviedata", help="Get Movie data.")
248  parser.add_option('-C', "--collection", action="store_true", default=False,
249  dest="collectiondata", help="Get Collection data.")
250  parser.add_option('-l', "--language", metavar="LANGUAGE", default=u'en',
251  dest="language", help="Specify language for filtering.")
252  parser.add_option('-a', "--area", metavar="COUNTRY", default=None,
253  dest="country", help="Specify country for custom data.")
254  parser.add_option('--debug', action="store_true", default=False,
255  dest="debug", help=("Disable caching and enable raw "
256  "data output."))
257 
258  opts, args = parser.parse_args()
259 
260  if opts.version:
261  buildVersion()
262 
263  if opts.test:
265 
266  from MythTV.tmdb3 import set_key, set_cache, set_locale
267  set_key('c27cb71cff5bd76e1a7a009380562c62')
268 
269  if opts.debug:
270  import MythTV.tmdb3
271  MythTV.tmdb3.request.DEBUG = True
272  set_cache(engine='null')
273  else:
274  import os
275  confdir = os.environ.get('MYTHCONFDIR', '')
276  if (not confdir) or (confdir == '/'):
277  confdir = os.environ.get('HOME', '')
278  if (not confdir) or (confdir == '/'):
279  print "Unable to find MythTV directory for metadata cache."
280  sys.exit(1)
281  confdir = os.path.join(confdir, '.mythtv')
282  confpath = os.path.join(confdir, 'cache', 'pytmdb3.cache')
283  set_cache(engine='file', filename=confpath)
284 
285  if opts.language:
286  set_locale(language=opts.language, fallthrough=True)
287  if opts.country:
288  set_locale(country=opts.country, fallthrough=True)
289 
290  if (len(args) != 1) or (args[0] == ''):
291  sys.stdout.write('ERROR: tmdb3.py requires exactly one non-empty argument')
292  sys.exit(1)
293 
294  if opts.movielist:
295  buildList(args[0], opts)
296 
297  if opts.moviedata:
298  buildSingle(args[0], opts)
299 
300  if opts.collectiondata:
301  buildCollection(args[0], opts)
302 
303 if __name__ == '__main__':
304  main()
def buildVersion
Definition: tmdb3.py:197
def buildCollection
Definition: tmdb3.py:175
def buildList
Definition: tmdb3.py:110
def main
Definition: tmdb3.py:237
def buildSingle
Definition: tmdb3.py:33
def performSelfTest
Definition: tmdb3.py:212