def getMovieData(self, movieId): """From a given TVDBPy movie object, return a graph containing its information.""" m = self.tmdb.Movies(movieId) resp = m.info({'language': self.tmdb.lang}) result = MemoryObjectGraph() movie = result.Movie(title=unicode(resp['title'])) movie.original_title = resp['original_title'] if resp.get('release_date'): movie.set( 'year', datetime.datetime.strptime(resp['release_date'], '%Y-%m-%d').year) movie.set('genres', [unicode(g['name']) for g in resp['genres']]) movie.set('rating', resp['vote_average']) movie.set('plot', [unicode(resp['overview'])]) resp = m.credits() movie.set('director', [ unicode(c['name']) for c in resp['crew'] if c['job'] == 'Director' ]) movie.set( 'writer', [unicode(c['name']) for c in resp['crew'] if c['job'] == 'Author']) try: movie.cast = [ unicode(actor['name']) + ' -- ' + unicode(actor['character']) for actor in resp['cast'] ] except KeyError: movie.cast = [] return result
def foundMetadata(query, result, link = True): """Return a graph that contains: - the only Media object found in the query graph - the result object linked as metadata to the previous media object WARNING: this functions messes with the data in the query graph, do not reuse it after calling this function. """ # TODO: check that result is valid solved = MemoryObjectGraph() # remove the stale 'matches' link before adding the media to the resulting graph #query.display_graph() media = query.find_one(Media) media.matches = [] media.metadata = [] m = solved.add_object(media) if result is None: return solved if isinstance(result, list): result = [ solved.add_object(n, recurse = Equal.OnLiterals) for n in result ] else: result = solved.add_object(result, recurse = Equal.OnLiterals) #solved.display_graph() if link: m.metadata = result return solved
def foundMetadata(query, result, link=True): """Return a graph that contains: - the only Media object found in the query graph - the result object linked as metadata to the previous media object WARNING: this functions messes with the data in the query graph, do not reuse it after calling this function. """ # TODO: check that result is valid solved = MemoryObjectGraph() # remove the stale 'matches' link before adding the media to the resulting graph #query.display_graph() media = query.find_one(Media) media.matches = [] media.metadata = [] m = solved.add_object(media) if result is None: return solved if isinstance(result, list): result = [ solved.add_object(n, recurse=Equal.OnLiterals) for n in result ] else: result = solved.add_object(result, recurse=Equal.OnLiterals) #solved.display_graph() if link: m.metadata = result return solved
def perform(self): query = MemoryObjectGraph() query.Media(filename=self.filename) result = self.taggerType().perform(query) # TODO: check that we actually found something useful #result.display_graph() # import the data into our collection self.collection.add_object(result.find_one(Media), recurse=Equal.OnUnique)
def perform(self, query): self.checkValid(query) log.debug('MovieTvdb: finding more info on %s' % u(query.find_one(Movie))) movie = query.find_one(Movie) try: mdprovider = TVDBMetadataProvider() result = mdprovider.startMovie(movie.title) except SmewtException: # movie could not be found, return a dummy Unknown movie instead so we can group them somewhere log.warning('Could not find info for movie: %s' % u(query.find_one(Media).filename)) noposter = '/static/images/noposter.png' result = MemoryObjectGraph() result.Movie(title = 'Unknown', loresImage = noposter, hiresImage = noposter) result = foundMetadata(query, result.find_one(Movie)) return result
def perform(self, query): self.checkValid(query) log.debug("MovieTvdb: finding more info on %s" % u(query.find_one(Movie))) movie = query.find_one(Movie) try: mdprovider = TVDBMetadataProvider() result = mdprovider.startMovie(movie.title) except SmewtException: # movie could not be found, return a dummy Unknown movie instead so we can group them somewhere log.warning("Could not find info for movie: %s" % u(query.find_one(Media).filename)) noposter = "/static/images/noposter.png" result = MemoryObjectGraph() result.Movie(title="Unknown", loresImage=noposter, hiresImage=noposter) result = foundMetadata(query, result.find_one(Movie)) return result
def perform(self, query): self.checkValid(query) self.query = query ep = query.find_one(Episode) log.debug('EpisodeTvdb: finding more info on %s' % ep) if ep.get('series') is None: raise SmewtException( "EpisodeTVDB: Episode doesn't contain 'series' field: %s" % ep) # little hack: if we have no season number, add 1 as default season number # (helps for series which have only 1 season) if ep.get('season') is None: ep.season = 1 try: mdprovider = TVDBMetadataProvider() result = mdprovider.startEpisode(ep) except SmewtException: # series could not be found, return a dummy Unknown series instead # so we can group them somewhere log.warning('Could not find series for file: %s' % query.find_one(Media).filename) noposter = '/static/images/noposter.png' result = MemoryObjectGraph() result.Series(title='Unknown', loresImage=noposter, hiresImage=noposter) # update the series query.delete_node(ep.series.node) ep.series = query.add_object( result.find_one(Series)) # this add_object should be unnecessary series = ep.series # and add all the potential episodes for found_ep in result.find_all(Episode): data = {'series': series} data.update(found_ep.literal_items()) ep = query.Episode(**data) return query
def getEpisodes(self, series, language): """From a given TVDBPy series object, return a graph containing its information as well as its episodes nodes.""" show, episodes = self.tvdb.get_show_and_episodes(series, language=language) # TODO: debug to see if this is the correct way to access the series' title result = MemoryObjectGraph() smewtSeries = result.Series(title=show.name) for episode in episodes: ep = result.Episode(series=smewtSeries, season=episode.season_number, episodeNumber=episode.episode_number) ep.set('title', episode.name) ep.set('synopsis', episode.overview) ep.set('originalAirDate', str(episode.first_aired)) return result
def perform(self, query): self.checkValid(query) self.query = query ep = query.find_one(Episode) log.debug('EpisodeTvdb: finding more info on %s' % ep) if ep.get('series') is None: raise SmewtException("EpisodeTVDB: Episode doesn't contain 'series' field: %s" % ep) # little hack: if we have no season number, add 1 as default season number # (helps for series which have only 1 season) if ep.get('season') is None: ep.season = 1 try: mdprovider = TVDBMetadataProvider() result = mdprovider.startEpisode(ep) except SmewtException: # series could not be found, return a dummy Unknown series instead # so we can group them somewhere log.warning('Could not find series for file: %s' % query.find_one(Media).filename) noposter = '/static/images/noposter.png' result = MemoryObjectGraph() result.Series(title = 'Unknown', loresImage=noposter, hiresImage=noposter) # update the series query.delete_node(ep.series.node) ep.series = query.add_object(result.find_one(Series)) # this add_object should be unnecessary series = ep.series # and add all the potential episodes for found_ep in result.find_all(Episode): data = { 'series': series } data.update(found_ep.literal_items()) ep = query.Episode(**data) return query
def clear_keep_config(self): # we want to keep our config object untouched tmp = MemoryObjectGraph() tmp.add_object(self.config) super(VersionedMediaGraph, self).clear() self.add_object(tmp.find_one(Config))
class TVDBMetadataProvider(object): def __init__(self): super(TVDBMetadataProvider, self).__init__() self.tvdb = thetvdbapi.TheTVDB("65D91F0290476F3E") self.tmdb = tmdbsimple.TMDB('a8b9f96dde091408a03cb4c78477bd14') self.tmdb.server_config = self.tmdb.Configuration() self.tmdb.server_config.info() self.tmdb.lang = 'en' @cachedmethod def getSeries(self, name): """Get the TVDBPy series object given its name.""" results = self.tvdb.get_matching_shows(name) ''' for id, name, lang in results: # FIXME: that doesn't look correct: either yield or no for return id raise SmewtException("EpisodeTVDB: Could not find series '%s'" % name) ''' if len(results) == 0: raise SmewtException("EpisodeTVDB: Could not find series '%s'" % name) return results @cachedmethod def getEpisodes(self, series, language): """From a given TVDBPy series object, return a graph containing its information as well as its episodes nodes.""" show, episodes = self.tvdb.get_show_and_episodes(series, language=language) # TODO: debug to see if this is the correct way to access the series' title result = MemoryObjectGraph() smewtSeries = result.Series(title=show.name) for episode in episodes: ep = result.Episode(series=smewtSeries, season=episode.season_number, episodeNumber=episode.episode_number) ep.set('title', episode.name) ep.set('synopsis', episode.overview) ep.set('originalAirDate', str(episode.first_aired)) return result @cachedmethod def getMovie(self, name): """Get the IMDBPy movie object given its name.""" if not name: raise SmewtException( 'You need to specify at least a probable name for the movie...' ) log.debug('MovieTMDB: looking for movie %s', name) results = self.tmdb.Search().movie({'query': name})['results'] for r in results: return r['id'] raise SmewtException("MovieTMDB: Could not find movie '%s'" % name) @cachedmethod def getMovieData(self, movieId): """From a given TVDBPy movie object, return a graph containing its information.""" m = self.tmdb.Movies(movieId) resp = m.info({'language': self.tmdb.lang}) result = MemoryObjectGraph() movie = result.Movie(title=unicode(resp['title'])) movie.original_title = resp['original_title'] if resp.get('release_date'): movie.set( 'year', datetime.datetime.strptime(resp['release_date'], '%Y-%m-%d').year) movie.set('genres', [unicode(g['name']) for g in resp['genres']]) movie.set('rating', resp['vote_average']) movie.set('plot', [unicode(resp['overview'])]) resp = m.credits() movie.set('director', [ unicode(c['name']) for c in resp['crew'] if c['job'] == 'Director' ]) movie.set( 'writer', [unicode(c['name']) for c in resp['crew'] if c['job'] == 'Author']) try: movie.cast = [ unicode(actor['name']) + ' -- ' + unicode(actor['character']) for actor in resp['cast'] ] except KeyError: movie.cast = [] return result def savePoster(self, posterUrl, localId): hiresFilename = path(smewt.dirs.user_data_dir, 'images', '%s_hires.jpg' % localId, createdir=True) loresFilename = path(smewt.dirs.user_data_dir, 'images', '%s_lores.jpg' % localId) # TODO: use requests instead of urlopen open(hiresFilename, 'wb').write(urlopen(posterUrl).read()) # NOTE: we do the resizing here because if we leave it to the browser, # it will use a fast resampling algorithm, which will be of lower # quality than what we achieve here # lores = 80px high width, height = 60, 80 header_msg = 'Creating %dx%d screenshot' % (width, height) log.info('%s for %s...', header_msg, hiresFilename) im = Image.open(hiresFilename) log.debug('%s: resizing...', header_msg) im.thumbnail((width, height), Image.ANTIALIAS) log.debug('%s: saving to png...', header_msg) im.save(loresFilename, "PNG") log.debug('%s: done!', header_msg) return ('/user/images/%s_lores.jpg' % localId, '/user/images/%s_hires.jpg' % localId) @cachedmethod def getSeriesPoster(self, tvdbID): """Return the low- and high-resolution posters of a tvdb object.""" noposter = '/static/images/noposter.png' urls = self.tvdb.get_show_image_choices(tvdbID) posters = [url for url in urls if url[1] == 'poster'] if posters: return self.savePoster(posters[0][0], 'series_%s' % tvdbID) else: log.warning('Could not find poster for tvdb ID %s' % tvdbID) return (noposter, noposter) @cachedmethod def getMoviePoster(self, movieId): """Return the low- and high-resolution posters (if available) of an tvdb object.""" noposter = '/static/images/noposter.png' resp = self.tmdb.Movies(movieId).info({'language': self.tmdb.lang}) image_size = 'original' image_base = self.tmdb.server_config.images[ 'base_url'] + '/' + image_size + '/' if resp['poster_path']: return self.savePoster(image_base + resp['poster_path'], 'movie_%s' % movieId) else: log.warning('Could not find poster for tmdb ID %s' % movieId) return (noposter, noposter) def startEpisode(self, episode): self.tmdb.lang = guiLanguage().alpha2 if episode.get('series') is None: raise SmewtException( "TVDBMetadataProvider: Episode doesn't contain 'series' field: %s", episode) name = episode.series.title name = name.replace(',', ' ') matching_series = self.getSeries(name) # Try first with the languages from guessit, and then with english languages = tolist(episode.get('language', [])) + ['en'] # Sort the series by id (stupid heuristic about most popular series # might have been added sooner to the db and the db id # follows the insertion order) # TODO: we should do something smarter like comparing series name distance, # episodes count and/or episodes names #print '\n'.join(['%s %s --> %f [%s] %s' % (x[1], name, textutils.levenshtein(x[1], name), x[2], x[0]) for x in matching_series]) matching_series.sort( key=lambda x: (textutils.levenshtein(x[1], name), int(x[0]))) series = None language = 'en' for lang in languages: try: language = lang ind = zip(*matching_series)[2].index(lang) series = matching_series[ind][0] break except ValueError, e: language = matching_series[0][2] series = matching_series[0][0] # TODO: at the moment, overwrite the detected language with the one # from the settings. It would be better to use the detected # language if it was more reliable (see previous TODO)... language = guiLanguage().alpha2 eps = self.getEpisodes(series, language) try: lores, hires = self.getSeriesPoster(series) eps.find_one(Series).update({ 'loresImage': lores, 'hiresImage': hires }) return eps except Exception, e: log.warning(str(e) + ' -- ' + str(textutils.toUtf8(episode))) return MemoryObjectGraph()