#!/usr/bin/env python # -*- coding: UTF-8 -*- # ---------------------- # Name: tmdb3.py # Python Script # Author: Raymond Wagner # Purpose: This python script is intended to translate lookups between the # TheMovieDB.org V3 API and MythTV's internal metadata format. # http://www.mythtv.org/wiki/MythVideo_Grabber_Script_Format # http://help.themoviedb.org/kb/api/about-3 #----------------------- __title__ = "TheMovieDB.org V3" __author__ = "Raymond Wagner" __version__ = "0.3.6" # 0.1.0 Initial version # 0.2.0 Add language support, move cache to home directory # 0.3.0 Enable version detection to allow use in MythTV # 0.3.1 Add --test parameter for proper compatibility with mythmetadatalookup # 0.3.2 Add --area parameter to allow country selection for release date and # parental ratings # 0.3.3 Use translated title if available # 0.3.4 Add support for finding by IMDB under -D (simulate previous version) # 0.3.5 Add debugging mode # 0.3.6 Add handling for TMDB site and library returning null results in # search. This should only need to be a temporary fix, and should be # resolved upstream. from optparse import OptionParser import sys def buildSingle(inetref, opts): from MythTV.tmdb3 import Movie from MythTV import VideoMetadata from lxml import etree import re if re.match('^0[0-9]{6}$', inetref): movie = Movie.fromIMDB(inetref) else: movie = Movie(inetref) tree = etree.XML(u'') mapping = [['runtime', 'runtime'], ['title', 'originaltitle'], ['releasedate', 'releasedate'], ['tagline', 'tagline'], ['description', 'overview'], ['homepage', 'homepage'], ['userrating', 'userrating'], ['popularity', 'popularity'], ['budget', 'budget'], ['revenue', 'revenue']] m = VideoMetadata() for i,j in mapping: if getattr(movie, j): setattr(m, i, getattr(movie, j)) if movie.title: m.title = movie.title releases = movie.releases.items() if opts.country: try: # resort releases with selected country at top to ensure it # is selected by the metadata libraries index = zip(*releases)[0].index(opts.country) releases.insert(0, releases.pop(index)) except ValueError: pass else: m.releasedate = releases[0][1].releasedate m.inetref = str(movie.id) if movie.collection: m.collectionref = str(movie.collection.id) if movie.releasedate: m.year = movie.releasedate.year for country, release in releases: if release.certification: m.certifications[country] = release.certification for genre in movie.genres: m.categories.append(genre.name) for studio in movie.studios: m.studios.append(studio.name) for country in movie.countries: m.countries.append(country.name) for cast in movie.cast: d = {'name':cast.name, 'character':cast.character, 'department':'Actors', 'job':'Actor', 'url':'http://www.themoviedb.org/people/{0}'.format(cast.id)} if cast.profile: d['thumb'] = cast.profile.geturl() m.people.append(d) for crew in movie.crew: d = {'name':crew.name, 'job':crew.job, 'department':crew.department, 'url':'http://www.themoviedb.org/people/{0}'.format(crew.id)} if crew.profile: d['thumb'] = crew.profile.geturl() m.people.append(d) for backdrop in movie.backdrops: m.images.append({'type':'fanart', 'url':backdrop.geturl(), 'thumb':backdrop.geturl(backdrop.sizes()[0])}) for poster in movie.posters: m.images.append({'type':'coverart', 'url':poster.geturl(), 'thumb':poster.geturl(poster.sizes()[0])}) tree.append(m.toXML()) sys.stdout.write(etree.tostring(tree, encoding='UTF-8', pretty_print=True, xml_declaration=True)) sys.exit() def buildList(query, opts): # TEMPORARY FIX: # replace all dashes from queries to work around search behavior # as negative to all text that comes afterwards query = query.replace('-',' ') from MythTV.tmdb3 import searchMovie from MythTV import VideoMetadata from lxml import etree results = searchMovie(query) tree = etree.XML(u'') mapping = [['runtime', 'runtime'], ['title', 'originaltitle'], ['releasedate', 'releasedate'], ['tagline', 'tagline'], ['description', 'overview'], ['homepage', 'homepage'], ['userrating', 'userrating'], ['popularity', 'popularity']] count = 0 for res in results: if res is None: continue m = VideoMetadata() for i,j in mapping: if getattr(res, j): setattr(m, i, getattr(res, j)) m.inetref = str(res.id) if res.title: m.title = res.title #TODO: # should releasedate and year be pulled from the country-specific data # or should it be left to the default information to cut down on # traffic from searches if res.releasedate: m.year = res.releasedate.year if res.backdrop: b = res.backdrop m.images.append({'type':'fanart', 'url':b.geturl(), 'thumb':b.geturl(b.sizes()[0])}) if res.poster: p = res.poster m.images.append({'type':'coverart', 'url':p.geturl(), 'thumb':p.geturl(p.sizes()[0])}) tree.append(m.toXML()) count += 1 if count >= 60: # page limiter, dont want to overload the server break sys.stdout.write(etree.tostring(tree, encoding='UTF-8', pretty_print=True, xml_declaration=True)) sys.exit(0) def buildCollection(inetref, opts): from MythTV.tmdb3 import Collection from MythTV import VideoMetadata from lxml import etree collection = Collection(inetref) tree = etree.XML(u'') m = VideoMetadata() m.collectionref = str(collection.id) m.title = collection.name if collection.backdrop: b = collection.backdrop m.images.append({'type':'fanart', 'url':b.geturl(), 'thumb':b.geturl(b.sizes()[0])}) if collection.poster: p = collection.poster m.images.append({'type':'coverart', 'url':p.geturl(), 'thumb':p.geturl(p.sizes()[0])}) tree.append(m.toXML()) sys.stdout.write(etree.tostring(tree, encoding='UTF-8', pretty_print=True, xml_declaration=True)) sys.exit() def buildVersion(): from lxml import etree version = etree.XML(u'') etree.SubElement(version, "name").text = __title__ etree.SubElement(version, "author").text = __author__ etree.SubElement(version, "thumbnail").text = 'tmdb.png' etree.SubElement(version, "command").text = 'tmdb3.py' etree.SubElement(version, "type").text = 'movie' etree.SubElement(version, "description").text = \ 'Search and metadata downloads for themoviedb.org' etree.SubElement(version, "version").text = __version__ sys.stdout.write(etree.tostring(version, encoding='UTF-8', pretty_print=True, xml_declaration=True)) sys.exit(0) def performSelfTest(): err = 0 try: import MythTV except: err = 1 print ("Failed to import MythTV bindings. Check your `configure` output " "to make sure installation was not disabled due to external " "dependencies") try: import MythTV.tmdb3 except: err = 1 print ("Failed to import PyTMDB3 library. This should have been included " "with the python MythTV bindings.") try: import lxml except: err = 1 print "Failed to import python lxml library." if not err: print "Everything appears in order." sys.exit(err) def main(): parser = OptionParser() parser.add_option('-v', "--version", action="store_true", default=False, dest="version", help="Display version and author") parser.add_option('-t', "--test", action="store_true", default=False, dest="test", help="Perform self-test for dependencies.") parser.add_option('-M', "--movielist", action="store_true", default=False, dest="movielist", help="Get Movies matching search.") parser.add_option('-D', "--moviedata", action="store_true", default=False, dest="moviedata", help="Get Movie data.") parser.add_option('-C', "--collection", action="store_true", default=False, dest="collectiondata", help="Get Collection data.") parser.add_option('-l', "--language", metavar="LANGUAGE", default=u'en', dest="language", help="Specify language for filtering.") parser.add_option('-a', "--area", metavar="COUNTRY", default=u'US', dest="country", help="Specify country for custom data.") parser.add_option('--debug', action="store_true", default=False, dest="debug", help=("Disable caching and enable raw " "data output.")) opts, args = parser.parse_args() if opts.version: buildVersion() if opts.test: performSelfTest() from MythTV.tmdb3 import set_key, set_cache, set_locale set_key('c27cb71cff5bd76e1a7a009380562c62') if opts.debug: import MythTV.tmdb3 MythTV.tmdb3.request.DEBUG = True set_cache(engine='null') else: set_cache(engine='file', filename='~/.mythtv/pytmdb3.cache') if opts.language: set_locale(language=opts.language, fallthrough=True) if opts.country: set_locale(country=opts.country, fallthrough=True) if (len(args) != 1) or (args[0] == ''): sys.stdout.write('ERROR: tmdb3.py requires exactly one non-empty argument') sys.exit(1) if opts.movielist: buildList(args[0], opts) if opts.moviedata: buildSingle(args[0], opts) if opts.collectiondata: buildCollection(args[0], opts) if __name__ == '__main__': main()