def wait_until_restart_or_shutdown(self):
        # type: () -> None
        """

        :return:
        """
        clz = BaseDiscoverMovies
        finished = False
        while not finished:
            Monitor.wait_for_abort(timeout=1.0)
            self.throw_exception_on_forced_to_stop()
    def wait_for_is_not_playing_video(self, timeout=None, trace=None):
        # type: (float, str) -> Union[bool, None]
        '''
        This is a mess.

        The preferred way to deal with this is to monitor onPlayBackStarted/
        onPlayBackEnded events, but onPlayBackEnded is not reliably sent.
        So, poll isPlayingVideo, which is True prior to the video actually
        being played, so some method calls can throw exceptions until
        onPlayBackStarted is issued. Sigh

        Perhaps rely on onVidowWindowOpened/Closed, but that depends upon
        the actual dialog opening/closing. Not good
        '''

        local_class = AdvancedPlayer
        try:
            if timeout is None:
                timeout = 0
                if self._player_window_open:
                    timeout = self.getTime()
                    timeout = self.getTotalTime() - timeout + 2
        except Exception:
            # Player must be finished
            timeout = 0

        timeout = timeout * 1000  # Convert to ms
        while (self._player_window_open
               and timeout > 0 and not Monitor.wait_for_abort(0.250)):
            timeout -= 250

        if timeout > 0:
            return False
        return True
    def waitForIsPlayingVideo(self, timeout=None):
        # type: (Optional[float]) -> bool
        '''
        This is a mess.

        The preferred way to deal with this is to monitor onPlayBackStarted/
        onPlayBackEnded events, but onPlayBackEnded is not reliably sent.
        So, poll isPlayingVideo, which is True prior to the video actually
        being played, so some method calls can throw exceptions until
        onPlayBackStarted is issued. Sigh

        Perhaps rely on onVidowWindowOpened/Closed, but that depends upon
        the actual dialog opening/closing. Not good
        '''
        local_class = AdvancedPlayer
        if timeout is None:
            timeout = 3600  # An hour, insane

        timeout = timeout * 1000  # Convert to ms

        # TODO: Add check for failures: onPlabackFailed/Ended/Error
        while not self._player_window_open and timeout > 0 and not Monitor.wait_for_abort(0.250):
            timeout -= 250

        if timeout <= 0:
            return False

        return True
    def join_dead_threads(cls):
        finished = False
        while not finished:
            with cls._lock:
                joined_threads: List[threading.Thread] = []
                for thread in cls._threads_to_join:
                    if not thread.is_alive():
                        thread.join(timeout=0.0)
                        if not thread.is_alive():
                            joined_threads.append(thread)

                for thread in joined_threads:
                    cls._threads_to_join.remove(thread)

            if Monitor.wait_for_abort(timeout=10.0):
                finished = True
    def _monitor(self):
        local_class = AdvancedPlayer
        try:
            if type(self).DEBUG_MONITOR:
                local_class._logger.enter()

            while not Monitor.wait_for_abort(0.1) and not self._closed:
                if (type(self).DEBUG_MONITOR and
                        local_class._logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE)):
                    if not self.isPlaying():
                        local_class._logger.debug_extra_verbose('Player: Idling...')

                while not self.isPlaying() and not self._closed:
                    Monitor.throw_exception_if_abort_requested(timeout=0.1)

                if self.isPlayingVideo():
                    if (type(self).DEBUG_MONITOR and
                            local_class._logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE)):
                        local_class._logger.debug_verbose('Monitoring video...')
                    self._video_monitor()
                elif self.isPlayingAudio():
                    if (type(self).DEBUG_MONITOR and
                            local_class._logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE)):
                        local_class._logger.debug_verbose('Monitoring audio...')
                    self._audio_monitor()
                elif self.isPlaying():
                    if (type(self).DEBUG_MONITOR and
                            local_class._logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE)):
                        local_class._logger.debug_verbose('Monitoring pre-play...')
                    self._preplay_monitor()

            if (type(self).DEBUG_MONITOR and
                    local_class._logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE)):
                local_class._logger.debug_verbose('Player: Closed')
        except AbortException:
            pass # Just exit thread
        except Exception as e:
            local_class._logger.exception('')
        finally:
            pass
    def run_cmd(self) -> int:
        self.rc = 0
        self.run_thread = threading.Thread(target=self.run_worker,
                                           name='normalize audio')
        Monitor.throw_exception_if_abort_requested()
        self.run_thread.start()

        self.cmd_finished = False
        while not Monitor.wait_for_abort(timeout=0.1):
            try:
                if self.process is not None:  # Wait to start
                    rc = self.process.poll()
                    if rc is not None:
                        self.rc = rc
                        self.cmd_finished = True
                        break  # Complete
            except subprocess.TimeoutExpired:
                pass

        if not self.cmd_finished:
            # Shutdown in process
            self.process: subprocess.Popen
            self.process.kill(
            )  # SIGKILL. Should cause stderr & stdout to exit
            self.rc = 9

        if self.run_thread.is_alive():
            self.run_thread.join(timeout=1.0)
        if self.stdout_thread.is_alive():
            self.stdout_thread.join(timeout=0.2)
        if self.stderr_thread.is_alive():
            self.stderr_thread.join(timeout=0.2)
        Monitor.throw_exception_if_abort_requested(timeout=0.0)
        # If abort did not occur, then process finished

        if self.rc != 0:
            self.log_output()

        return self.rc
示例#7
0
    def load_fetch_queue(self) -> None:
        """
            Load the _trailers_to_fetch_queue from._discovered_trailers_queue.

            If _trailers_to_fetch_queue is full, then return

            If discoveryComplete and _discovered_trailers is empty,
            then return

            If discoveryComplete and._discovered_trailers_queue is empty,
            then shuffle_discovered_trailers and fill the _trailers_to_fetch_queue
            from it. If there are not enough items to fill the fetch queue,
            then get as many as are available.

            Otherwise, discoveryComplete == False:

            If._discovered_trailers_queue is empty and _trailers_to_fetch_queue
            is not empty, then return without loading any.

            If._discovered_trailers_queue is empty and _trailers_to_fetch_queue is empty
            then block until an item becomes available or discoveryComplete == True.

            Finally, _trailers_to_fetch_queue is not full, fill it from any available
            items from._discovered_trailers_queue.
        :return:
        """
        clz = AbstractMovieData
        start_time = datetime.datetime.now()
        if AbstractMovieData._first_load:
            Monitor.wait_for_abort(timeout=2.0)
            AbstractMovieData._first_load = False

        Monitor.throw_exception_if_abort_requested()
        finished = False
        attempts = 0
        discovery_complete_queue_empty = 0
        discovered_and_fetch_queues_empty = 0
        discovery_incomplete_fetch_not_empty = 0
        discovery_incomplete_fetch_queue_empty = 0
        get_attempts = 0
        put_attempts = 0
        while not finished:
            trailer = None  # type: Union[MovieType, None]
            Monitor.throw_exception_if_abort_requested()
            attempts += 1
            shuffle = False
            iteration_successful = False
            try:
                elapsed = datetime.datetime.now() - start_time
                if attempts > 0:
                    if (attempts > 1
                            and self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE)):
                        self.logger.debug_extra_verbose('Attempt:', attempts,
                                                       'elapsed:', elapsed.seconds)

                if self._trailers_to_fetch_queue.full():
                    if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                        self.logger.debug_extra_verbose('_trailers_to_fetch_queue full',
                                                       trace=Trace.TRACE)
                    finished = True
                    iteration_successful = True
                elif self._discovery_complete and len(self._discovered_trailers) == 0:
                    if (not self._discovery_complete_reported and
                            self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE)):
                        self._discovery_complete_reported = True
                        self.logger.debug_extra_verbose(
                            'Discovery Complete and nothing found.', trace=Trace.TRACE)
                    finished = True
                    iteration_successful = True
                elif self._discovery_complete and self._discovered_trailers_queue.empty():
                    self.logger.error(
                        'discoveryComplete,_discovered_trailers_queue empty')
                    if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                        self.logger.debug_extra_verbose(
                            'discoveryComplete,_discovered_trailers_queue empty',
                            trace=Trace.TRACE)
                    shuffle = True
                    discovery_complete_queue_empty += 1
                    #
                    # In the following, Discovery is INCOMPLETE
                    #
                elif (self._discovered_trailers_queue.empty()
                      and not self._trailers_to_fetch_queue.empty):
                    discovered_and_fetch_queues_empty += 1
                    # Use what we have
                    if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                        self.logger.debug_extra_verbose('Discovery incomplete._discovered_trailers_queue',
                                           'empty and _trailers_to_fetch_queue not empty',
                                           trace=Trace.TRACE)
                    finished = True
                elif not self._trailers_to_fetch_queue.empty():
                    # Fetch queue is not empty, nor full. Discovery
                    # is not complete. Get something from _discoveredTrailerQueue
                    # if available

                    try:
                        discovery_incomplete_fetch_not_empty += 1
                        with self._discovered_trailers_lock:
                            # self.logger.debug_verbose('Have discovered_trailers_lock')

                            trailer = self._discovered_trailers_queue.get(timeout=0.25)

                        # if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                        #     self.logger.debug_extra_verbose(' Got', trailer[Movie.TITLE],
                        #                        'from _discoveredTrailerQueue')
                    except KodiQueue.Empty:
                        pass

                    if trailer is not None:
                        try:
                            self.put_in_fetch_queue(
                                trailer, timeout=1)
                            # if self.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE):
                            #     self.logger.debug_verbose('Put in _trailers_to_fetch_queue qsize:',
                            #                        self._trailers_to_fetch_queue.qsize(),
                            #                        trailer.get(Movie.TITLE),
                            #                        trace=Trace.TRACE)
                            iteration_successful = True
                        except KodiQueue.Full:
                            if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                                self.logger.debug_extra_verbose(
                                    '_trailers_to_fetch_queue.put failed',
                                    trace=Trace.TRACE)
                        #
                        # It is not a crisis if the put fails. Since the
                        # fetch queue does have at least one entry, we are ok
                        # Even if the trailer is lost from the FetchQueue,
                        # it will get reloaded once the queue is exhausted.
                        #
                        # But since iteration_successful is not true, we might
                        # still fix it at the end.
                        #
                else:
                    # Discovery incomplete, fetch queue is empty
                    # wait until we get an item, or discovery complete

                    discovery_incomplete_fetch_queue_empty += 1
                    if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                        self.logger.debug_extra_verbose('Discovery incomplete,',
                                                       '_trailers_to_fetch_queue empty, '
                                                       'will wait',
                                                       trace=Trace.TRACE)

                if not iteration_successful:
                    if (self._discovered_trailers_queue.empty()
                            and self._discovered_trailers.len() > 0):
                        if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                            self.logger.debug_extra_verbose(
                                'Shuffling due to empty _discovered_trailers_queue and',
                                '_discovered_trailers not empty')
                        shuffle = True

                    if shuffle:  # Because we were empty
                        if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                            self.logger.debug_extra_verbose(
                                'Shuffling due to empty _discovered_trailers_queue')
                        Monitor.throw_exception_if_abort_requested()
                        if self.logger.isEnabledFor(LazyLogger.DEBUG_EXTRA_VERBOSE):
                            self.logger.debug_extra_verbose('load_fetch_queue Shuffling because',
                                               'discoveredTrailerQueue empty',
                                               trace=Trace.TRACE_DISCOVERY)
                        self.shuffle_discovered_trailers(mark_unplayed=True)

                    if trailer is None:
                        get_finished = False
                        while not get_finished:
                            try:
                                get_attempts += 1
                                with self._discovered_trailers_lock:
                                    # self.logger.debug_verbose('Have discovered_trailers_lock')

                                    trailer = self._discovered_trailers_queue.get(
                                        timeout=0.5)
                                get_finished = True
                            except KodiQueue.Empty:
                                Monitor.throw_exception_if_abort_requested()

                    put_finished = False
                    while not put_finished:
                        try:
                            put_attempts += 1
                            self.put_in_fetch_queue(trailer, timeout=0.25)
                            put_finished = True
                        except KodiQueue.Full:
                            Monitor.throw_exception_if_abort_requested()
                        iteration_successful = True

                if trailer is not None:
                    movie_title = trailer.get(Movie.TITLE)
                else:
                    movie_title = 'no movie'

                # if self.logger.isEnabledFor(LazyLogger.DEBUG_VERBOSE):
                #     self.logger.debug_verbose('Queue has:',
                #                        self._trailers_to_fetch_queue.qsize(),
                #                        'Put in _trailers_to_fetch_queue:', movie_title)
            except AbortException:
                reraise(*sys.exc_info())
            except Exception as e:
                self.logger.exception('')
                # TODO Continue?

            if self._trailers_to_fetch_queue.full():
                finished = True

            if not self._trailers_to_fetch_queue.empty() and not iteration_successful:
                finished = True

            if not finished:
                if attempts % 10 == 0:
                    if self.logger.isEnabledFor(LazyLogger.DEBUG):
                        self.logger.debug(
                            'hung reloading from._discovered_trailers_queue.',
                            'length of _discovered_trailers:',
                            len(self._discovered_trailers),
                            'length of._discovered_trailers_queue:',
                            self._discovered_trailers_queue.qsize(),
                            trace=Trace.TRACE)
                Monitor.throw_exception_if_abort_requested(timeout=0.5)

        stop_time = datetime.datetime.now()
        duration = stop_time - start_time
        self._load_fetch_total_duration += duration.seconds

        attempts = 0
        discovery_complete_queue_empty = 0
        discovered_and_fetch_queues_empty = 0
        discovery_incomplete_fetch_not_empty = 0
        discovery_incomplete_fetch_queue_empty = 0
        get_attempts = 0
        put_attempts = 0