Example #1
0
    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()
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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()
Example #6
0
    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