diff --git a/skill/app.py b/skill/app.py index cf82988..798e896 100755 --- a/skill/app.py +++ b/skill/app.py @@ -300,7 +300,7 @@ class NaviSonicPlayMusicByArtist(AbstractRequestHandler): # Check if a background process is already running, if it is then terminate the process # in favour of the new process. - if backgroundProcess != None: + if backgroundProcess is not None: backgroundProcess.terminate() backgroundProcess.join() @@ -325,7 +325,7 @@ class NaviSonicPlayMusicByArtist(AbstractRequestHandler): play_queue.clear() controller.enqueue_songs(connection, play_queue, [song_id_list[0], song_id_list[1]]) # When generating the playlist return the first two tracks. - backgroundProcess = Process(target=queueWorkerThread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks + backgroundProcess = Process(target=queue_worker_thread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks backgroundProcess.start() # Start the additional thread speech = sanitise_speech_output(f'Playing music by: {artist.value}') @@ -355,7 +355,7 @@ class NaviSonicPlayAlbumByArtist(AbstractRequestHandler): # Check if a background process is already running, if it is then terminate the process # in favour of the new process. - if backgroundProcess != None: + if backgroundProcess is not None: backgroundProcess.terminate() backgroundProcess.join() @@ -395,7 +395,7 @@ class NaviSonicPlayAlbumByArtist(AbstractRequestHandler): # Work around the Amazon / Alexa 8 second timeout. controller.enqueue_songs(connection, play_queue, [song_id_list[0], song_id_list[1]]) # When generating the playlist return the first two tracks. - backgroundProcess = Process(target=queueWorkerThread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks + backgroundProcess = Process(target=queue_worker_thread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks backgroundProcess.start() # Start the additional thread speech = sanitise_speech_output(f'Playing {album.value} by: {artist.value}') @@ -425,10 +425,9 @@ class NaviSonicPlayAlbumByArtist(AbstractRequestHandler): # Work around the Amazon / Alexa 8 second timeout. controller.enqueue_songs(connection, play_queue, [song_id_list[0], song_id_list[1]]) # When generating the playlist return the first two tracks. - backgroundProcess = Process(target=queueWorkerThread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks + backgroundProcess = Process(target=queue_worker_thread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks backgroundProcess.start() # Start the additional thread - speech = sanitise_speech_output(f'Playing {album.value}') logger.info(speech) card = {'title': 'AskNavidrome', @@ -510,7 +509,7 @@ class NaviSonicPlayPlaylist(AbstractRequestHandler): # Check if a background process is already running, if it is then terminate the process # in favour of the new process. - if backgroundProcess != None: + if backgroundProcess is not None: backgroundProcess.terminate() backgroundProcess.join() @@ -532,7 +531,7 @@ class NaviSonicPlayPlaylist(AbstractRequestHandler): # Work around the Amazon / Alexa 8 second timeout. controller.enqueue_songs(connection, play_queue, [song_id_list[0], song_id_list[1]]) # When generating the playlist return the first two tracks. - backgroundProcess = Process(target=queueWorkerThread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks + backgroundProcess = Process(target=queue_worker_thread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks backgroundProcess.start() # Start the additional thread speech = sanitise_speech_output('Playing playlist ' + str(playlist.value)) @@ -544,11 +543,6 @@ class NaviSonicPlayPlaylist(AbstractRequestHandler): return controller.start_playback('play', speech, card, track_details, handler_input) -def queueWorkerThread(connection, play_queue, song_id_list): - logger.debug('In playlist processing thread!') - controller.enqueue_songs(connection, play_queue, song_id_list) - play_queue.sync() - logger.debug('Finished playlist processing!') class NaviSonicPlayMusicByGenre(AbstractRequestHandler): """ Play songs from the given genre @@ -565,7 +559,7 @@ class NaviSonicPlayMusicByGenre(AbstractRequestHandler): # Check if a background process is already running, if it is then terminate the process # in favour of the new process. - if backgroundProcess != None: + if backgroundProcess is not None: backgroundProcess.terminate() backgroundProcess.join() @@ -586,7 +580,7 @@ class NaviSonicPlayMusicByGenre(AbstractRequestHandler): # Work around the Amazon / Alexa 8 second timeout. controller.enqueue_songs(connection, play_queue, [song_id_list[0], song_id_list[1]]) # When generating the playlist return the first two tracks. - backgroundProcess = Process(target=queueWorkerThread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks + backgroundProcess = Process(target=queue_worker_thread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks backgroundProcess.start() # Start the additional thread speech = sanitise_speech_output(f'Playing {genre.value} music') @@ -614,7 +608,7 @@ class NaviSonicPlayMusicRandom(AbstractRequestHandler): # Check if a background process is already running, if it is then terminate the process # in favour of the new process. - if backgroundProcess != None: + if backgroundProcess is not None: backgroundProcess.terminate() backgroundProcess.join() @@ -632,7 +626,7 @@ class NaviSonicPlayMusicRandom(AbstractRequestHandler): # Work around the Amazon / Alexa 8 second timeout. controller.enqueue_songs(connection, play_queue, [song_id_list[0], song_id_list[1]]) # When generating the playlist return the first two tracks. - backgroundProcess = Process(target=queueWorkerThread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks + backgroundProcess = Process(target=queue_worker_thread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks backgroundProcess.start() # Start the additional thread speech = sanitise_speech_output('Playing random music') @@ -660,7 +654,7 @@ class NaviSonicPlayFavouriteSongs(AbstractRequestHandler): # Check if a background process is already running, if it is then terminate the process # in favour of the new process. - if backgroundProcess != None: + if backgroundProcess is not None: backgroundProcess.terminate() backgroundProcess.join() @@ -678,7 +672,7 @@ class NaviSonicPlayFavouriteSongs(AbstractRequestHandler): # Work around the Amazon / Alexa 8 second timeout. controller.enqueue_songs(connection, play_queue, [song_id_list[0], song_id_list[1]]) # When generating the playlist return the first two tracks. - backgroundProcess = Process(target=queueWorkerThread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks + backgroundProcess = Process(target=queue_worker_thread, args=(connection, play_queue, song_id_list[2:])) # Create a thread to enqueue the remaining tracks backgroundProcess.start() # Start the additional thread speech = sanitise_speech_output('Playing your favourite tracks.') @@ -1053,6 +1047,7 @@ class LoggingResponseInterceptor(AbstractResponseInterceptor): # Functions # + def sanitise_speech_output(speech_string: str) -> str: """Sanitise speech output inline with the SSML standard @@ -1084,6 +1079,27 @@ def sanitise_speech_output(speech_string: str) -> str: return speech_string + +def queue_worker_thread(connection: object, play_queue: object, song_id_list: list) -> None: + """Media queue worker + + This function allows media queues to be populated in the background enabling multithreading + and increasing skill response times. + + :param connection: A SubSonic API connection object + :type connection: object + :param play_queue: A MediaQueue object + :type play_queue: object + :param song_id_list: A list containing Navidrome song IDs + :type song_id_list: list + """ + + logger.debug('In playlist processing thread!') + controller.enqueue_songs(connection, play_queue, song_id_list) + play_queue.sync() + logger.debug('Finished playlist processing!') + + # Register Intent Handlers sb.add_request_handler(LaunchRequestHandler()) sb.add_request_handler(CheckAudioInterfaceHandler()) diff --git a/skill/asknavidrome/media_queue.py b/skill/asknavidrome/media_queue.py index d7e960e..bddc57a 100644 --- a/skill/asknavidrome/media_queue.py +++ b/skill/asknavidrome/media_queue.py @@ -44,19 +44,19 @@ class MediaQueue: """Method to return current_track attribute Added to allow access to the current_track object while using BaseManager - for multi threading, as BaseManager does not allow access to class + for multi threading, as BaseManager does not allow access to class attributes / properties :return: A Track object representing the current playing audio track :rtype: Track """ return self.current_track - + def set_current_track_offset(self, offset: int) -> None: """Method to set the offset of the current track in milliseconds Set the offset for the current track in milliseconds. This is used - when resuming a paused track to ensure the track isn't played from + when resuming a paused track to ensure the track isn't played from the beginning again. :param offset: The track offset in milliseconds @@ -75,7 +75,7 @@ class MediaQueue: """ return self.queue - + def get_buffer(self) -> deque: """Get the buffer @@ -86,7 +86,7 @@ class MediaQueue: """ return self.buffer - + def get_history(self) -> deque: """Get history @@ -97,7 +97,7 @@ class MediaQueue: """ return self.history - + def add_track(self, track: Track) -> None: """Add tracks to the queue diff --git a/skill/asknavidrome/subsonic_api.py b/skill/asknavidrome/subsonic_api.py index 40dd60c..90792ad 100644 --- a/skill/asknavidrome/subsonic_api.py +++ b/skill/asknavidrome/subsonic_api.py @@ -63,7 +63,7 @@ class SubsonicConnection: self.logger.error('Failed to connect to Navidrome') return self.conn.ping() - + def scrobble(self, track_id: str, time: int) -> None: """Scrobble the given track