Fix: Stop playback at end of queue properly (#336)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 22 May 2022 19:44:16 +0000 (21:44 +0200)
committerGitHub <noreply@github.com>
Sun, 22 May 2022 19:44:16 +0000 (21:44 +0200)
* Fix: Stop queue stream when no more tracks in queue (no repeat)

* send stop when queue is empty

* send silence to player at queue end

music_assistant/controllers/stream.py
music_assistant/models/player_queue.py

index c956d7e6b1bb797b06ddcebe2f2724e5900b9c1d..1e35909c1582262986169bf70de273138c363bb9 100644 (file)
@@ -11,6 +11,7 @@ from aiohttp import web
 
 from music_assistant.helpers.audio import (
     check_audio_support,
+    create_wave_header,
     crossfade_pcm_parts,
     get_media_stream,
     get_preview_stream,
@@ -26,7 +27,11 @@ from music_assistant.models.enums import (
     MediaType,
     ProviderType,
 )
-from music_assistant.models.errors import MediaNotFoundError, MusicAssistantError
+from music_assistant.models.errors import (
+    MediaNotFoundError,
+    MusicAssistantError,
+    QueueEmpty,
+)
 from music_assistant.models.event import MassEvent
 from music_assistant.models.player_queue import PlayerQueue, QueueItem
 
@@ -137,12 +142,21 @@ class StreamController:
             return web.Response(status=404)
 
         # prepare request
+        try:
+            start_streamdetails = await queue.queue_stream_prepare()
+        except QueueEmpty:
+            # send stop here to prevent the player from retrying over and over
+            await queue.stop()
+            # send some silence to allow the player to process the stop request
+            result = create_wave_header(duration=10)
+            result += b"\0" * 1764000
+            return web.Response(status=200, body=result, content_type="audio/wav")
+
         resp = web.StreamResponse(
             status=200, reason="OK", headers={"Content-Type": f"audio/{fmt}"}
         )
         await resp.prepare(request)
 
-        start_streamdetails = await queue.queue_stream_prepare()
         output_fmt = ContentType(fmt)
         # work out sample rate
         if queue.settings.crossfade_mode == CrossFadeMode.ALWAYS:
@@ -162,7 +176,7 @@ class StreamController:
             output_format=output_fmt,
         )
         # get the raw pcm bytes from the queue stream and on the fly encode to wanted format
-        # send the compressed/endoded stream to the client.
+        # send the compressed/encoded stream to the client.
         async with AsyncProcess(sox_args, True) as sox_proc:
 
             async def writer():
index 210f21ad86ada4ff8dc77218d847bfe78e862db5..f524afc2efb234cfc912f3ed30967100aece6854 100644 (file)
@@ -723,7 +723,7 @@ class PlayerQueue:
 
     async def queue_stream_prepare(self) -> StreamDetails:
         """Call when queue_streamer is about to start playing."""
-        start_from_index = self._next_start_index or 0
+        start_from_index = self._next_start_index
         try:
             next_item = self._items[start_from_index]
         except (IndexError, TypeError) as err:
@@ -737,7 +737,7 @@ class PlayerQueue:
 
     async def queue_stream_start(self) -> int:
         """Call when queue_streamer starts playing the queue stream."""
-        start_from_index = self._next_start_index or 0
+        start_from_index = self._next_start_index
         self._current_item_elapsed_time = 0
         self._current_index = start_from_index
         self._start_index = start_from_index
@@ -754,12 +754,9 @@ class PlayerQueue:
 
     def get_next_index(self, index: int) -> int | None:
         """Return the next index or None if no more items."""
-        if not self._items:
+        if not self._items or index is None:
             # queue is empty
             return None
-        if index is None:
-            # guard just in case
-            return 0
         if self.settings.repeat_mode == RepeatMode.ONE:
             return index
         if len(self._items) > (index + 1):