def __init__(self, downloader: VideoDownloader, url: str, parse_json_as_youtube: bool = False) -> None: super().__init__(downloader, url, parse_json_as_youtube=parse_json_as_youtube) clz = type(self) if clz.logger is None: clz.logger = module_logger.getChild(clz.__name__) clz.country_id = Settings.get_country_iso_3166_1().lower() clz.certifications = WorldCertifications.get_certifications( clz.country_id) clz.unrated_id = clz.certifications.get_unrated_certification( ).get_preferred_id() self._trailer_info: List[MovieType] = [] self.is_finished = False
def validate_detailed_movie_properties(cls, movie: MovieType, stack_trace: bool = True, force_check: bool = False) -> bool: """ Similar to validate_basic_movie_properties. Validates additional fields :param movie: :param stack_trace: :param force_check: Check even if debug level less than DEBUG_VERBOSE :return: True if no problems found """ if not (cls._logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE) or force_check): return True details_properties = { Movie.WRITER: 'default_' + Movie.WRITER, Movie.DETAIL_DIRECTORS: 'default_' + Movie.DETAIL_DIRECTORS, Movie.DETAIL_TITLE: 'default_' + Movie.TITLE, Movie.CAST: 'default_' + Movie.CAST, Movie.PLOT: 'default_' + Movie.PLOT, Movie.GENRE: 'default_' + Movie.GENRE, Movie.STUDIO: 'default_' + Movie.STUDIO, Movie.DETAIL_ACTORS: 'default_' + Movie.ACTORS, Movie.DETAIL_GENRES: 'default_' + Movie.GENRE, Movie.DETAIL_CERTIFICATION: 'default_' + Movie.DETAIL_CERTIFICATION, Movie.DETAIL_CERTIFICATION_IMAGE: 'default_' + Movie.DETAIL_CERTIFICATION_IMAGE, Movie.DETAIL_RUNTIME: 'default_' + Movie.RUNTIME, Movie.DETAIL_WRITERS: 'default_' + Movie.WRITER, # Movie.TMDB_TAGS: 'default_' + Movie.TAG, # For TMDB Movie.DETAIL_STUDIOS: 'default_' + Movie.STUDIO, Movie.RUNTIME: 0, # Movie.ADULT, Movie.MPAA: 'default_' + Movie.MPAA } cls.validate_basic_movie_properties(movie, stack_trace=stack_trace) failing_properties = [] is_ok = True for property_name in details_properties.keys(): if movie.get(property_name) is None: failing_properties.append(property_name) movie.setdefault(property_name, details_properties[property_name]) is_ok = False if len(failing_properties) > 0: msg = ', '.join(failing_properties) if stack_trace: LazyLogger.dump_stack('Missing details property: ' + msg) else: cls._logger.debug_verbose('Missing properties:', msg) country_id = Settings.get_country_iso_3166_1().lower() certifications = WorldCertifications.get_certifications(country_id) if not certifications.is_valid(movie[Movie.MPAA]): if movie[Movie.MPAA] != '': cls._logger.debug_verbose( f'Invalid certification: {movie[Movie.MPAA]} for movie: ' '{movie[Movie.TITLE]} set to NR') movie[Movie.MPAA] = certifications.get_unrated_certification() \ .get_preferred_id() # assert is_ok, 'LEAK, Invalid property values' return is_ok
def populate_youtube_movie_info(movie_data: MovieType, url: str) -> MovieType: """ Creates a Kodi MovieType from the data returned from Youtube. Currently only used for TFH movies. Not used for iTunes movies. Rely on DiscoverItunesMovies for that. TFH trailers are titled: <reviewer> on <MOVIE_TITLE_ALL_CAPS> Here we can try to get just the movie title and then look up a likely match in TMDB (with date, and other info). TFH may not like us changing/guessing the movie title, however. """ movie: Union[MovieType, None] = None dump_json = False missing_keywords = [] try: trailer_id = movie_data.get('id') if trailer_id is None: missing_keywords.append('id') dump_json = True if movie_data.get('title') is None: missing_keywords.append('title') dump_json = True title = movie_data.get('title', 'missing title') # title_segments = title.split(' on ') # real_title_index = len(title_segments) - 1 # movie_title = title_segments[real_title_index] movie_title = title trailer_url = 'https://youtu.be/' + trailer_id if movie_data.get('upload_date') is None: missing_keywords.append('upload_date') dump_json = True movie_data['upload_date'] = datetime.datetime.now().strftime( '%Y%m%d') upload_date = movie_data.get('upload_date', '19000101') # 20120910 year = upload_date[0:4] year = int(year) if movie_data.get('thumbnail') is None: missing_keywords.append('thumbnail') dump_json = True thumbnail = movie_data.get('thumbnail', '') original_language = '' if movie_data.get('description') is None: missing_keywords.append('description') dump_json = True description = movie_data.get('description', '') country_id = Settings.get_country_iso_3166_1().lower() certifications = WorldCertifications.get_certifications(country_id) unrated_id = certifications.get_unrated_certification( ).get_preferred_id() trailers_in_playlist = movie_data.get('n_entries', 1) playlist_index = movie_data.get('playlist_index', 0) # Tags might have some good stuff, but very unorganized and full of junk # tags: Dict[str, str] = movie_data.get('tags', {}) if movie_data.get('average_rating') is None: missing_keywords.append('average_rating') dump_json = True if movie_data.get('duration') is None: missing_keywords.append('duration') dump_json = True movie = { Movie.SOURCE: 'unknown', Movie.YOUTUBE_ID: trailer_id, Movie.TITLE: movie_title, Movie.YEAR: year, Movie.ORIGINAL_LANGUAGE: original_language, Movie.TRAILER: trailer_url, Movie.PLOT: description, Movie.THUMBNAIL: thumbnail, Movie.DISCOVERY_STATE: Movie.NOT_FULLY_DISCOVERED, Movie.MPAA: unrated_id, Movie.ADULT: False, Movie.RATING: movie_data.get('average_rating', 0.0), # Kodi measures in seconds Movie.RUNTIME: movie_data.get('duration', 1.0) * 60 } if playlist_index is not None: movie[Movie.YOUTUBE_PLAYLIST_INDEX] = playlist_index movie[Movie.YOUTUBE_TRAILERS_IN_PLAYLIST] = trailers_in_playlist except Exception as e: dump_json = True module_logger.exception(e) if dump_json: if module_logger.isEnabledFor(LazyLogger.DEBUG): module_logger.debug( 'Missing json data. Missing keywords:', ', '.join(missing_keywords), 'URL:', url, '\njson:', json.dumps(movie_data, encoding='utf-8', ensure_ascii=False, indent=3, sort_keys=True)) return movie
def run_worker(self): # type: () -> None """ Initial Discovery of all movies in Kodi. :return: """ # Discovery is done in two parts: # # 1- query DB for every movie in library # 2- Get additional information # # There are three types of trailers for these movies: # # a- Movies with local trailers # b- Movies with trailer URLS (typically youtube links from tmdb) # TMdb will need to be queried for details # c. Movies with no trailer information, requiring a check with tmdb # to see if one exists # # Because of the above, this manager will query the DB for every movie # and then only process the ones with local trailers. The others will # be handed off to their own managers. This is done because of # the way that this application works: # Once enough information to identify a movie that matches # what the user wants, it is added to the pool of movies that # can be randomly selected for playing. Once a movie has been # selected, it is placed into a TrailerFetcherQueue. A # TrailerFetcher then gathers the remaining information so that # it can be played. # # If the lion's share of movies in the pool require significant # extra processing because they don't have local trailers, then # the fetcher can get overwhelmed. clz = DiscoverLibraryMovies self._selected_keywords = [] self._excluded_keywords = [] self._selected_genres = [] self._excluded_genres = [] if Settings.get_filter_genres(): self._selected_genres = GenreUtils.get_internal_kodi_genre_ids( GenreUtils.LOCAL_DATABASE, exclude=False) self._excluded_genres = GenreUtils.get_internal_kodi_genre_ids( GenreUtils.LOCAL_DATABASE, exclude=True) self._selected_keywords = GenreUtils.get_internal_kodi_keyword_ids( GenreUtils.LOCAL_DATABASE, exclude=False) self._excluded_keywords = GenreUtils.get_internal_kodi_keyword_ids( GenreUtils.LOCAL_DATABASE, exclude=True) query = self.create_query( self._selected_genres, self._excluded_genres, self._selected_keywords, self._excluded_keywords) if Monitor.is_abort_requested(): return start_time = datetime.datetime.now() Monitor.throw_exception_if_abort_requested() # Expensive operation query_result = JsonUtilsBasic.get_kodi_json(query, dump_results=False) Monitor.throw_exception_if_abort_requested() elapsed_time = datetime.datetime.now() - start_time if clz.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): clz.logger.debug_verbose('Library query seconds:', elapsed_time.total_seconds()) movies_skipped = 0 movies_found = 0 movies_with_local_trailers = 0 movies_with_trailer_urls = 0 movies_without_trailer_info = 0 self.throw_exception_on_forced_to_stop() result = query_result.get('result', {}) del query_result movies = result.get('movies', []) del result DiskUtils.RandomGenerator.shuffle(movies) if self._libraryURLManager is None: self._libraryURLManager = DiscoverLibraryURLTrailerMovies() self._libraryNoTrailerInfoManager = DiscoverLibraryNoTrailerMovies() library_movies = [] library_url_movies = [] library_no_trailer_movies = [] empty_limit = 50 movie_data = None if Settings.is_enable_movie_stats(): movie_data = LibraryMovieStats() country_id = Settings.get_country_iso_3166_1().lower() certifications = WorldCertifications.get_certifications(country_id) unrated_id = certifications.get_unrated_certification().get_preferred_id() for movie in movies: self.throw_exception_on_forced_to_stop() try: #clz.logger.debug('movie:', movie) movies_found += 1 if Settings.get_hide_watched_movies() and Movie.LAST_PLAYED in movie: if (self.get_days_since_last_played(movie[Movie.LAST_PLAYED], movie[Movie.TITLE]) > Settings.get_minimum_days_since_watched()): movies_skipped += 1 if clz.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE): clz.logger.debug_extra_verbose(movie[Movie.TITLE], 'will not be played due to ' 'Hide', 'Watched Movies') continue # Normalize certification # if clz.logger.isEnabledFor(LazyLogger.DEBUG): # clz.logger.debug('mpaa:', movie[Movie.MPAA], # 'movie:', movie[Movie.TITLE]) if certifications.is_valid(movie.get(Movie.MPAA, '')): movie[Movie.MPAA] = unrated_id certification = certifications.get_certification( movie.get(Movie.MPAA), movie.get(Movie.ADULT)) movie[Movie.ADULT] = movie.get(Movie.ADULT, False) if not isinstance(movie[Movie.ADULT], bool): if clz.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): clz.logger.debug_verbose(movie[Movie.TITLE], 'has invalid ADULT field: ', movie[Movie.ADULT]) movie[Movie.ADULT] = str(movie[Movie.ADULT]).lower == 'true' movie[Movie.SOURCE] = Movie.LIBRARY_SOURCE movie.setdefault(Movie.TRAILER, '') movie[Movie.TYPE] = '' if clz.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): Debug.validate_basic_movie_properties(movie) if Settings.is_enable_movie_stats(): movie_data.collect_data(movie) # Basic discovery is complete at this point. Now send # all of the movies without any trailer information to # DiscoverLibraryNoTrailerMovies while # those with trailer URLs to DiscoverLibraryURLTrailerMovies if certifications.filter(certification): trailer = movie[Movie.TRAILER] if trailer == '': movies_without_trailer_info += 1 library_no_trailer_movies.append(movie) elif trailer.startswith('plugin://') or trailer.startswith('http'): movies_with_trailer_urls += 1 library_url_movies.append(movie) elif Settings.get_include_library_trailers(): movies_with_local_trailers += 1 library_movies.append(movie) if len(library_movies) >= empty_limit: self.add_to_discovered_trailers(library_movies) del library_movies[:] # Unblock other discovery now that a few movies have been # found. if not self._some_movies_discovered_event.isSet(): self._some_movies_discovered_event.set() if len(library_no_trailer_movies) >= empty_limit: self._libraryNoTrailerInfoManager.add_to_discovered_trailers( library_no_trailer_movies) del library_no_trailer_movies[:] if len(library_url_movies) >= empty_limit: self._libraryURLManager.add_to_discovered_trailers( library_url_movies) del library_no_trailer_movies[:] # Unblock other discovery now that a few movies have been # found. self._some_movies_discovered_event.set() except AbortException: reraise(*sys.exc_info()) except Exception: clz.logger.exception('') try: if len(library_movies) >= 0: self.add_to_discovered_trailers(library_movies) if len(library_no_trailer_movies) >= 0: self._libraryNoTrailerInfoManager.add_to_discovered_trailers( library_no_trailer_movies) if len(library_url_movies) >= 0: self._libraryURLManager.add_to_discovered_trailers( library_url_movies) except AbortException: reraise(*sys.exc_info()) except Exception: clz.logger.exception('') if (clz.logger.isEnabledFor(LazyLogger.DEBUG) and clz.logger.is_trace_enabled(Trace.STATS)): clz.logger.debug('Local movies found in library:', movies_found, trace=Trace.STATS) clz.logger.debug('Local movies filtered out', movies_skipped, trace=Trace.STATS) clz.logger.debug('Movies with local trailers:', movies_with_local_trailers, trace=Trace.STATS) clz.logger.debug('Movies with trailer URLs:', movies_with_trailer_urls, trace=Trace.STATS) clz.logger.debug('Movies with no trailer information:', movies_without_trailer_info, trace=Trace.STATS) if Settings.is_enable_movie_stats(): movie_data.report_data() del movie_data
def populate_youtube_movie_info(cls, movie_data: MovieType, url: str) -> MovieType: """ Creates a Kodi MovieType from the data returned from Youtube. Not used for iTunes movies. Rely on DiscoverItunesMovies for that. TFH trailers are titled: Formats: Reviewer on CAPS TITLE (most common) Reviewer talks TITLE Reviewer talks about TITLE Reviewer discusses TITLE Reviewer's TITLE TITLE Reviewer In Conversation With Person Reviewer covers TITLE Reviewer introduces TITLE for the Cinenasty series Here we can try to get just the movie title and then look up a likely match in TMDB (with date, and other info). TFH may not like us changing/guessing the movie title, however. """ movie: Union[MovieType, None] = None dump_json = False missing_keywords = [] try: trailer_id = movie_data.get('id') if trailer_id is None: missing_keywords.append('id') dump_json = True url = movie_data.get('url') if movie_data.get('title') is None: missing_keywords.append('title') dump_json = True movie_title = movie_data.get('title', 'Missing Title') trailer_url = 'https://youtu.be/' + trailer_id upload_date = movie_data.get('upload_date', '19000101') year_str = upload_date[0:4] year = int(year_str) if movie_data.get('thumbnail') is not None: thumbnail = movie_data.get('thumbnail') if movie_data.get('description') is None: description = movie_data.get('description', '') country_id = Settings.get_country_iso_3166_1().lower() certifications = WorldCertifications.get_certifications(country_id) unrated_id = certifications.get_unrated_certification( ).get_preferred_id() trailers_in_playlist = movie_data.get('n_entries', 1) movie = { Movie.SOURCE: 'unknown', Movie.YOUTUBE_ID: trailer_id, Movie.TITLE: movie_title, Movie.YEAR: 0, Movie.ORIGINAL_LANGUAGE: '', Movie.TRAILER: trailer_url, Movie.PLOT: '', Movie.THUMBNAIL: '', Movie.DISCOVERY_STATE: Movie.NOT_FULLY_DISCOVERED, Movie.MPAA: unrated_id, Movie.ADULT: False, Movie.RATING: movie_data.get('average_rating', 0.0), # Kodi measures in seconds # At least for TFH, this appears to be time of trailer # (not movie), measured in 1/60 of a # second, or 60Hz frames. Weird. Movie.RUNTIME: 0 # Ignore trailer length } except Exception as e: dump_json = True cls.logger.exception(f'url: {url}') if dump_json: if cls.logger.isEnabledFor(LazyLogger.DEBUG): cls.logger.debug( 'Missing json data. Missing keywords:', ', '.join(missing_keywords), 'URL:', url, '\njson:', json.dumps(movie_data, ensure_ascii=False, indent=3, sort_keys=True)) return movie
def populate_youtube_movie_info(cls, movie_data: MovieType, url: str) -> MovieType: """ Creates a Kodi MovieType from the data returned from Youtube. """ movie: Union[MovieType, None] = None dump_json = False missing_keywords = [] try: trailer_id = movie_data.get('id') if trailer_id is None: missing_keywords.append('id') dump_json = True if movie_data.get('title') is None: missing_keywords.append('title') dump_json = True movie_title = movie_data.get('title', 'Missing Title') trailer_url = 'https://youtu.be/' + trailer_id if movie_data.get('upload_date') is None: missing_keywords.append('upload_date') dump_json = True movie_data['upload_date'] = datetime.datetime.now().strftime( '%Y%m%d') upload_date = movie_data.get('upload_date', '19000101') # 20120910 year_str = upload_date[0:4] year = int(year_str) if movie_data.get('thumbnail') is None: missing_keywords.append('thumbnail') dump_json = True thumbnail = movie_data.get('thumbnail', '') if movie_data.get('description') is None: missing_keywords.append('description') dump_json = True description = movie_data.get('description', '') country_id = Settings.get_country_iso_3166_1().lower() certifications = WorldCertifications.get_certifications(country_id) unrated_id = certifications.get_unrated_certification( ).get_preferred_id() # Tags might have some good stuff, but very unorganized and full of junk # tags: Dict[str, str] = movie_data.get('tags', {}) if movie_data.get('average_rating') is None: missing_keywords.append('average_rating') dump_json = True if movie_data.get('duration') is None: missing_keywords.append('duration') dump_json = True movie = { Movie.SOURCE: 'unknown', Movie.YOUTUBE_ID: trailer_id, Movie.TITLE: movie_title, Movie.YEAR: year, Movie.ORIGINAL_LANGUAGE: '', Movie.TRAILER: trailer_url, Movie.PLOT: description, Movie.THUMBNAIL: thumbnail, Movie.DISCOVERY_STATE: Movie.NOT_FULLY_DISCOVERED, Movie.MPAA: unrated_id, Movie.ADULT: False, Movie.RATING: movie_data.get('average_rating', 0.0), # Kodi measures in seconds # At least for TFH, this appears to be time of trailer # (not movie), measured in 1/60 of a # second, or 60Hz frames. Weird. Movie.RUNTIME: 0 # Ignore trailer length } except Exception as e: dump_json = True cls.logger.exception(f'url: {url}') if dump_json: if cls.logger.isEnabledFor(LazyLogger.DEBUG): cls.logger.debug( 'Missing json data. Missing keywords:', ', '.join(missing_keywords), 'URL:', url, '\njson:', json.dumps(movie_data, ensure_ascii=False, indent=3, sort_keys=True)) return movie
def __init__(self, title: str, year: Union[str, None], runtime_seconds: int) -> None: clz = type(self) if clz._logger is None: clz._logger = module_logger.getChild(clz.__name__) self._title_to_match: str = title self._year_to_match: Union[str, None] = year self._runtime_seconds_to_match: int = runtime_seconds self.candidate_movies: List[clz.CandidateMovie] = [] data = { 'api_key': Settings.get_tmdb_api_key(), 'page': '1', 'query': title, 'language': Settings.get_lang_iso_639_1() } if year is not None: data['primary_release_year'] = year try: include_adult = 'false' country_id = Settings.get_country_iso_3166_1().lower() certifications = WorldCertifications.get_certifications(country_id) adult_certification = certifications.get_certification( 'dummy', True) if certifications.filter(adult_certification): include_adult = 'true' data['include_adult'] = include_adult data['append_to_response'] = 'alternative_titles' url = 'https://api.themoviedb.org/3/search/movie' status_code, _info_string = \ JsonUtilsBasic.get_json(url, params=data, dump_msg='get_tmdb_id_from_title_year', dump_results=True, error_msg=title + f' ({year})') if clz._logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE): clz._logger.debug_extra_verbose( f'Getting TMDB movie for title: {title} year: {year} ' f'runtime: {runtime_seconds}') if _info_string is not None: results = _info_string.get('results', []) if len(results) > 1: if clz._logger.isEnabledFor( LazyLogger.DEBUG_EXTRA_VERBOSE): clz._logger.debug_extra_verbose( f'Got multiple matching movies: {title} ' f'year: {year} runtime: {runtime_seconds}') # TODO: Improve. Create best trailer function from get_tmdb_trailer # TODO: find best trailer_id current_language = Settings.get_lang_iso_639_1() for movie in results: release_date = movie.get('release_date', '') # 1932-04-22 tmdb_year = release_date[:-6] movie[Movie.YEAR] = tmdb_year tmdb_title = movie.get('title', '') tmdb_id = movie.get('id', None) tmdb_language = movie.get('original_language') runtime_minutes = movie.get(Movie.RUNTIME, 0) runtime_seconds = int(runtime_minutes * 60) titles = movie.get('alternative_titles', {'titles': []}) alt_titles = [] for title in titles['titles']: alt_title = (title['title'], title['iso_3166_1']) alt_titles.append(alt_title) if clz._logger.isEnabledFor( LazyLogger.DEBUG_EXTRA_VERBOSE): clz._logger.debug_extra_verbose( f'Matching Movie date: {tmdb_year}' f' tmdb_title: {tmdb_title}' f' lang: {tmdb_language}' f' current_lang: {current_language}') self._add(movie, tmdb_title, tmdb_year, tmdb_language, tmdb_id, runtime_seconds) except AbortException: reraise(*sys.exc_info()) except Exception as e: clz._logger.exception(e)
def _get_tmdb_id_from_title_year(title: str, year: int) -> Optional[int]: """ When we don't have a trailer for a movie, we can see if TMDB has one. :param title: :param year: :return: """ year_str = str(year) found_movie = None trailer_id = None data = {} data['api_key'] = Settings.get_tmdb_api_key() data['page'] = '1' data['query'] = title data['language'] = Settings.get_lang_iso_639_1() data['primary_release_year'] = year try: include_adult = 'false' country_id = Settings.get_country_iso_3166_1().lower() certifications = WorldCertifications.get_certifications(country_id) adult_certification = certifications.get_certification( 'dummy', True) if certifications.filter(adult_certification): include_adult = 'true' data['include_adult'] = include_adult url = 'https://api.themoviedb.org/3/search/movie' status_code, _info_string = \ JsonUtilsBasic.get_json(url, params=data, dump_msg='get_tmdb_id_from_title_year', dump_results=True, error_msg=title + ' (' + year_str + ')') if _info_string is not None: results = _info_string.get('results', []) if len(results) > 1: if TMDBUtils._logger.isEnabledFor( LazyLogger.DEBUG_EXTRA_VERBOSE): TMDBUtils._logger.debug_extra_verbose( 'Got multiple matching movies:', title, 'year:', year) # TODO: Improve. Create best trailer function from get_tmdb_trailer # TODO: find best trailer_id matches = [] current_language = Settings.get_lang_iso_639_1() movie = None for movie in results: release_date = movie.get('release_date', '') # 1932-04-22 found_year = release_date[:-6] found_title = movie.get('title', '') if (found_title.lower() == title.lower() and found_year == year_str and movie.get('original_language') == current_language): matches.append(movie) # TODO: Consider close match heuristics. if len(matches) == 1: found_movie = matches[0] elif len(matches) > 1: if TMDBUtils._logger.isEnabledFor( LazyLogger.DEBUG_VERBOSE): TMDBUtils._logger.debug_extra_verbose( 'More than one matching movie in same year choosing first ' 'one matching current language.', 'Num choices:', len(matches)) found_movie = matches[0] if movie is None: if TMDBUtils._logger.isEnabledFor( LazyLogger.DEBUG_EXTRA_VERBOSE): TMDBUtils._logger.debug_extra_verbose( 'Could not find movie:', title, 'year:', year, 'at TMDB. found', len(results), 'candidates') for a_movie in results: release_date = a_movie.get('release_date', '') # 1932-04-22 found_year = release_date[:-6] found_title = a_movie.get('title', '') tmdb_id = a_movie.get('id', None) if TMDBUtils._logger.isEnabledFor(LazyLogger.DISABLED): TMDBUtils._logger.debug_extra_verbose( 'found:', found_title, '(', found_year, ')', 'tmdb id:', tmdb_id) tmdb_data = MovieEntryUtils.get_alternate_titles( title, tmdb_id) for alt_title, country in tmdb_data['alt_titles']: if alt_title.lower() == title.lower(): found_movie = tmdb_data # Not actually in "movie" format break else: if TMDBUtils._logger.isEnabledFor(LazyLogger.INFO): TMDBUtils._logger.info('Could not find movie:', title, 'year:', year, 'at TMDB. found no candidates') except AbortException: reraise(*sys.exc_info()) except Exception: TMDBUtils._logger.exception('') tmdb_id = None if found_movie is not None: tmdb_id = found_movie.get('id', None) if tmdb_id is None: return None return int(tmdb_id)