def add_missing_library_trailer(cls, tmdb_id=None, # type: int library_id=None, # type: Optional[int] title=None, # type: str year=None, # type: int source=None # type str ): # type (...) -> None """ :param tmdb_id: :param library_id: :param title: :param year: :param source: :return: """ values = { Movie.MOVIEID: library_id, 'timestamp': datetime.date.today() } if cls._logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): values[Movie.UNIQUE_ID_TMDB] = tmdb_id values[Movie.TITLE] = title values[Movie.YEAR] = year values[Movie.SOURCE] = source cls.abort_on_shutdown() with cls.lock: if tmdb_id not in TrailerUnavailableCache._all_missing_library_trailers: cls._all_missing_library_trailers[tmdb_id] = values cls.library_cache_changed() Statistics.add_missing_library_trailer()
def is_tmdb_id_missing_trailer(cls, tmdb_id): # type: (int) -> Union[bool, None] """ :param tmdb_id: :return: """ cls.abort_on_shutdown() with cls.lock: if tmdb_id not in cls._all_missing_tmdb_trailers: return None entry = cls._all_missing_tmdb_trailers[tmdb_id] elapsed_time = datetime.date.today() - entry['timestamp'] elapsed_days = elapsed_time.days if elapsed_days > Settings.get_expire_remote_db_trailer_check_days(): del cls._all_missing_tmdb_trailers[tmdb_id] entry = None cls.tmdb_cache_changed() if entry is None: Statistics.add_missing_tmdb_id_cache_miss() else: Statistics.add_missing_tmdb_cache_hit() return entry
def load_cache(cls): # type: () -> None """ :return: """ path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'missing_tmdb_trailers.json') path = xbmcvfs.validatePath(path) cls.abort_on_shutdown() with cls.lock: try: parent_dir, file_name = os.path.split(path) DiskUtils.create_path_if_needed(parent_dir) if os.path.exists(path): with io.open(path, mode='rt', newline=None, encoding='utf-8') as cacheFile: cls._all_missing_tmdb_trailers = json.load( cacheFile, encoding='utf-8', object_hook=TrailerUnavailableCache.datetime_parser) size = len(cls._all_missing_tmdb_trailers) Statistics.missing_tmdb_trailers_initial_size(size) except AbortException: reraise(*sys.exc_info()) except IOError as e: cls._logger.exception('') except JSONDecodeError as e: os.remove(path) except Exception as e: cls._logger.exception('') cls.abort_on_shutdown() path = os.path.join(Settings.get_remote_db_cache_path(), 'index', 'missing_library_trailers.json') path = xbmcvfs.validatePath(path) try: parent_dir, file_name = os.path.split(path) DiskUtils.create_path_if_needed(parent_dir) if os.path.exists(path): with io.open(path, mode='rt', newline=None, encoding='utf-8') as cacheFile: cls._all_missing_library_trailers = json.load( cacheFile, encoding='utf-8', object_hook=TrailerUnavailableCache.datetime_parser) size = len(cls._all_missing_library_trailers) Statistics.missing_library_trailers_initial_size(size) except AbortException: reraise(*sys.exc_info()) except JSONDecodeError as e: os.remove(path) except IOError as e: cls._logger.exception('') except Exception as e: cls._logger.exception('') pass
def add_unprocessed_movies(cls, movies: List[MovieType]) -> None: """ :param movies: :return: """ with cls.lock: for movie in movies: tmdb_id = int(MovieEntryUtils.get_tmdb_id(movie)) if tmdb_id not in cls._unprocessed_movies: cls._unprocessed_movies[tmdb_id] = movie cls._unprocessed_movie_changes += len(movies) cls.save_unprocessed_movie_cache() Statistics.add_tmdb_total_number_of_unprocessed_movies(len(movies))
def remove_unprocessed_movies(cls, tmdb_ids: Union[int, List[int]]) -> None: """ :param tmdb_ids: :return: """ if not isinstance(tmdb_ids, list): tmdb_ids = [tmdb_ids] with cls.lock: for tmdb_id in tmdb_ids: tmdb_id = int(tmdb_id) if tmdb_id in cls._unprocessed_movies: del cls._unprocessed_movies[tmdb_id] cls._unprocessed_movie_changes += 1 cls.save_unprocessed_movie_cache() Statistics.add_tmdb_total_number_of_removed_unprocessed_movies()
def get_cached_json( url, # type; str movie_id=None, # type: Union[str, int, None] error_msg=None, # type: Union[str, int, None] source=None, # type: Union[str, None] dump_results=False, # type: bool dump_msg='', # type: str headers=None, # type: Union[dict, None] params=None, # type: Union[dict, None] timeout=3.0 # type: int ): # type: (...) -> (int, str) """ Attempt to get cached JSON movie information before using the JSON calls to get it remotely. Any information not in the cache will be placed into it after successfully reading it. :param url: :param movie_id: :param error_msg: :param source: :param dump_results: :param dump_msg: :param headers: :param params: :param timeout: :return: """ if headers is None: headers = {} if params is None: params = {} trailer_data = None status = 0 if Settings.is_use_tmdb_cache(): start = datetime.datetime.now() trailer_data = Cache.read_tmdb_cache_json(movie_id, source, error_msg=error_msg) status = 0 stop = datetime.datetime.now() read_time = stop - start Statistics.add_json_read_time(int(read_time.microseconds / 10000)) # if JsonUtils._logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE): # JsonUtils._logger.debug_extra_verbose('json cache read time:', # read_time.microseconds / 10000, # 'ms') if trailer_data is not None: trailer_data[Movie.CACHED] = True if trailer_data is None: status, trailer_data = JsonUtils.get_json( url, dump_results=dump_results, dump_msg=dump_msg, headers=headers, error_msg=error_msg, params=params, timeout=timeout) if ((status == 0 or status == 200) and trailer_data is not None and Settings.is_use_tmdb_cache()): Cache.write_tmdb_cache_json(movie_id, source, trailer_data) if trailer_data is None and status == 0: status = -1 return status, trailer_data
def _do_next(self): # type: () -> Union[dict, None] """ :return: """ clz = PlayableTrailerService try: while not PlayableTrailersContainer.is_any_trailers_available_to_play( ): self.throw_exception_on_forced_to_stop(movie_data=None, delay=0.25) except Exception as e: self.logger.exception('') total_number_of_trailers = 0 start_time = datetime.datetime.now() # Considered locking all TrailerManagers here to guarantee # that lengths don't change while finding the right trailer # but that might block the readyToPlayQueue from getting # loaded. Besides, it doesn't matter too much if we play # the incorrect trailer, as long as we get one. The # major fear is if we have no trailers at all, but that # will be handled elsewhere. # Get total number of trailers from all managers. # It is possible that all discovery is complete and there is nothing # to play. nothing_to_play = True # playable_trailers_map = None # type: Dict[str, # PlayableTrailersContainer] # type: playable_trailers_map = PlayableTrailersContainer.get_instances() # Dict[str, PlayableTrailersContainer] # Need to use the same projected sizes throughout this method. projected_sizes_map = {} for source in playable_trailers_map: playable_trailers = playable_trailers_map[source] if not playable_trailers.is_playable_trailers(): continue movie_data = playable_trailers.get_movie_data() self.throw_exception_on_forced_to_stop(movie_data=movie_data) number_of_trailers = movie_data.get_number_of_movies() trailers_queue_size = movie_data.get_discovered_trailer_queue_size( ) if self.logger.isEnabledFor(LazyLogger.DISABLED): self.logger.debug_extra_verbose( source, 'size:', number_of_trailers, 'discoveredTrailersQueue size:', trailers_queue_size, 'readyToPlayQueue size:', playable_trailers.get_ready_to_play_queue().qsize(), 'trailersToFetchQueue size:', movie_data.get_trailers_to_fetch_queue_size()) projected_size = movie_data.get_projected_number_of_trailers() projected_sizes_map[source] = projected_size total_number_of_trailers += projected_size if not movie_data.is_discovery_complete( ) or number_of_trailers != 0: nothing_to_play = False # If we have played everything, then we start over. if (trailers_queue_size == 0 and playable_trailers.is_playable_trailers()): if self.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): self.logger.debug( 'Shuffling because discoveredTrailerQueue empty', trace=Trace.TRACE_DISCOVERY) movie_data.shuffle_discovered_trailers(mark_unplayed=True) if nothing_to_play: if self.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): self.logger.debug_verbose('Nothing to Play! numTrailers:', total_number_of_trailers) raise StopIteration # return None # Now, randomly pick playable_trailers to get a trailer from based upon # the number of trailers in each. # # We loop here because there may not be any trailers in the readyToPlayQueue # for a specific playable_trailers trailer = None attempts = 0 while trailer is None and attempts < 10: try: trailer_index_to_play = DiskUtils.RandomGenerator.randint( 0, total_number_of_trailers - 1) if self.logger.isEnabledFor(LazyLogger.DISABLED): self.logger.debug_extra_verbose( 'PlayableTrailerService.next trailer_index_to_play:', trailer_index_to_play) except (ValueError) as e: # Empty range Monitor.throw_exception_if_abort_requested(timeout=0.10) continue total_number_of_trailers = 0 found_playable_trailers = None for source in playable_trailers_map: playable_trailers = playable_trailers_map[source] if not playable_trailers.is_playable_trailers(): continue movie_data = playable_trailers.get_movie_data() self.throw_exception_on_forced_to_stop(movie_data=movie_data) projected_size = playable_trailers.get_projected_number_of_trailers( ) if self.logger.isEnabledFor(LazyLogger.DISABLED): self.logger.debug_extra_verbose('source:', source, 'projected size:', projected_size) total_number_of_trailers += projected_sizes_map[source] if self.logger.isEnabledFor(LazyLogger.DISABLED): self.logger.debug_extra_verbose( 'total_number_of_trailers:', total_number_of_trailers) if trailer_index_to_play < total_number_of_trailers: found_playable_trailers = playable_trailers break try: attempts += 1 if attempts > 1 and self.logger.isEnabledFor( LazyLogger.DEBUG_VERBOSE): self.logger.debug_verbose( 'PlayableTrailerService.next Attempt:', attempts, 'manager:', found_playable_trailers.__class__.__name__) trailer = found_playable_trailers.get_next_movie() TrailerCache.validate_cached_files(trailer) # If cached trailer is invalid, then skip over this trailer. if trailer[ Movie. DISCOVERY_STATE] != Movie.DISCOVERY_READY_TO_DISPLAY: trailer = None else: found_playable_trailers.set_starving(False) title = trailer[Movie.TITLE] + \ ' : ' + trailer[Movie.TRAILER] except queue.Empty: found_playable_trailers.set_starving(True) trailer = None duration_of_first_attempt = datetime.datetime.now() - start_time second_attempt_start_time = None second_method_attempts = None if trailer is None: if self.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): self.logger.debug_verbose( 'Trailer not found by preferred method', trace=Trace.TRACE) # Alternative method is to pick a random PlayableTrailersContainer to start # with and then find one that has a trailer. Otherwise, camp out. second_attempt_start_time = datetime.datetime.now() second_method_attempts = 0 iteration = 0 playable_trailers_list = [*playable_trailers_map.keys()] DiskUtils.RandomGenerator.shuffle(playable_trailers_list) for source in itertools.cycle(playable_trailers_list): try: playable_trailers = playable_trailers_map[source] movie_data = playable_trailers.get_movie_data() self.throw_exception_on_forced_to_stop( movie_data=movie_data) if (playable_trailers.get_number_of_playable_movies() == 0 and playable_trailers.get_movie_data( ).get_number_of_movies() > 0 and playable_trailers.is_playable_trailers()): if self.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): self.logger.debug_verbose( 'Shuffling because', 'discoveredTrailerQueue empty', 'source:', source, trace=Trace.TRACE_DISCOVERY) playable_trailers.get_movie_data().\ shuffle_discovered_trailers(mark_unplayed=True) trailer = playable_trailers.get_next_movie() # If cached trailer is invalid, then skip over this # trailer. if trailer[ Movie. DISCOVERY_STATE] != Movie.DISCOVERY_READY_TO_DISPLAY: trailer = None if trailer is not None: break except queue.Empty: pass # try again recently_played_trailers = \ PlayableTrailersContainer.get_recently_played_trailers() if len(recently_played_trailers) != 0: recently_played_list = list( recently_played_trailers.values()) trailer_index_to_play = DiskUtils.RandomGenerator.randint( 0, len(recently_played_trailers) - 1) trailer = recently_played_list[trailer_index_to_play] break iteration += 1 if iteration % len(playable_trailers_list) == 0: second_method_attempts += 1 Monitor.throw_exception_if_abort_requested(timeout=0.5) if trailer is None: self._next_failures += 1 else: trailer[Movie.TRAILER_PLAYED] = True title = trailer[Movie.TITLE] + ' : ' + trailer[Movie.TRAILER] duration = datetime.datetime.now() - start_time self._next_total_duration += duration.seconds self._next_calls += 1 self._next_attempts += attempts self._next_total_first_method_attempts += attempts Statistics.add_next_trailer_wait_time( duration_of_first_attempt.seconds, attempts) if second_method_attempts is not None: self._next_attempts += second_method_attempts self._next_second_attempts += second_method_attempts second_duration = datetime.datetime.now( ) - second_attempt_start_time self._next_second_total_Duration += second_duration.seconds Statistics.add_next_trailer_second_attempt_wait_time( second_duration.seconds, second_method_attempts) if trailer is None: raise StopIteration if self.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE): self.logger.debug_verbose('Playing:', trailer[Movie.DETAIL_TITLE], trace=Trace.TRACE) Debug.validate_detailed_movie_properties(trailer) # Periodically report on played movie statistics self._played_movies_count += 1 if self.logger.is_trace_enabled(Trace.TRACE_PLAY_STATS): if (self._played_movies_count % 100) == 0: PlayStatistics.report_play_count_stats() return trailer