Fix some playback issues (#1689)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 30 Sep 2024 22:40:07 +0000 (00:40 +0200)
committerGitHub <noreply@github.com>
Mon, 30 Sep 2024 22:40:07 +0000 (00:40 +0200)
music_assistant/server/controllers/player_queues.py
music_assistant/server/helpers/audio.py
music_assistant/server/helpers/ffmpeg.py
music_assistant/server/helpers/util.py
music_assistant/server/providers/spotify/__init__.py
music_assistant/server/providers/spotify/bin/librespot-linux-aarch64
music_assistant/server/providers/spotify/bin/librespot-linux-x86_64
music_assistant/server/providers/spotify/bin/librespot-macos-arm64

index a4a6f96f99f0a81fafb00b971572d88649f7a907..6267eb351c94e3c9d6b6f677acd3606f1707706f 100644 (file)
@@ -290,7 +290,7 @@ class PlayerQueuesController(CoreController):
             # we need to restart playback
             self.mass.create_task(self.resume(queue_id))
         else:
-            self.mass.create_task(self._enqueue_next(queue, queue.current_index))
+            self.mass.call_later(5, self._enqueue_next(queue, queue.current_index))
 
     @api_command("player_queues/play_media")
     async def play_media(
index 559b13fd73a55e7ad118905ed834f3c6b3d47b10..b871ae309278ed30da42ec45eb164e3a0f93df4f 100644 (file)
@@ -46,7 +46,7 @@ from .playlists import HLS_CONTENT_TYPES, IsHLSPlaylist, PlaylistItem, fetch_pla
 from .process import AsyncProcess, check_output, communicate
 from .tags import parse_tags
 from .throttle_retry import BYPASS_THROTTLER
-from .util import create_tempfile
+from .util import TimedAsyncGenerator, create_tempfile
 
 if TYPE_CHECKING:
     from music_assistant.common.models.player_queue import QueueItem
@@ -296,7 +296,9 @@ async def get_media_stream(
     )
     try:
         await ffmpeg_proc.start()
-        async for chunk in ffmpeg_proc.iter_chunked(pcm_format.pcm_sample_size):
+        async for chunk in TimedAsyncGenerator(
+            ffmpeg_proc.iter_chunked(pcm_format.pcm_sample_size), 60
+        ):
             # for radio streams we just yield all chunks directly
             if streamdetails.media_type == MediaType.RADIO:
                 yield chunk
index 0aaa9dcf18f1e6a07a2e5ae9dd2fe89579a59bde..392f2ceef4e8bf8db9106321cd0e8581f7e652db 100644 (file)
@@ -14,7 +14,7 @@ from music_assistant.common.models.media_items import AudioFormat, ContentType
 from music_assistant.constants import VERBOSE_LOG_LEVEL
 
 from .process import AsyncProcess
-from .util import close_async_generator
+from .util import TimedAsyncGenerator, close_async_generator
 
 LOGGER = logging.getLogger("ffmpeg")
 
@@ -122,7 +122,7 @@ class FFMpeg(AsyncProcess):
         generator_exhausted = False
         audio_received = False
         try:
-            async for chunk in self.audio_input:
+            async for chunk in TimedAsyncGenerator(self.audio_input, 30):
                 audio_received = True
                 await self.write(chunk)
             generator_exhausted = True
@@ -169,7 +169,7 @@ async def get_ffmpeg_stream(
     ) as ffmpeg_proc:
         # read final chunks from stdout
         iterator = ffmpeg_proc.iter_chunked(chunk_size) if chunk_size else ffmpeg_proc.iter_any()
-        async for chunk in iterator:
+        async for chunk in TimedAsyncGenerator(iterator, 60):
             yield chunk
 
 
index 55c8439e80fc8b0494572f735e8412762d927499..95896f6d94aeaef940a5db3f9a64f937e33063a5 100644 (file)
@@ -249,3 +249,36 @@ def lock(
             return await func(*args, **kwargs)
 
     return wrapper
+
+
+class TimedAsyncGenerator:
+    """
+    Async iterable that times out after a given time.
+
+    Source: https://medium.com/@dmitry8912/implementing-timeouts-in-pythons-asynchronous-generators-f7cbaa6dc1e9
+    """
+
+    def __init__(self, iterable, timeout=0):
+        """
+        Initialize the AsyncTimedIterable.
+
+        Args:
+            iterable: The async iterable to wrap.
+            timeout: The timeout in seconds for each iteration.
+        """
+
+        class AsyncTimedIterator:
+            def __init__(self):
+                self._iterator = iterable.__aiter__()
+
+            async def __anext__(self):
+                result = await asyncio.wait_for(self._iterator.__anext__(), int(timeout))
+                if not result:
+                    raise StopAsyncIteration
+                return result
+
+        self._factory = AsyncTimedIterator
+
+    def __aiter__(self):
+        """Return the async iterator."""
+        return self._factory()
index 7bd49696ea99e20c343e7af32be4f764cd5aa4b9..52b238a26593bf3efe24fb2162b3397970c25938 100644 (file)
@@ -576,7 +576,7 @@ class SpotifyProvider(MusicProvider):
                 "pipe",
                 "--single-track",
                 spotify_uri,
-                "--token",
+                "--access-token",
                 auth_info["access_token"],
             ]
             if seek_position:
index 8ce08d224a33f48903b74eb4ebaf21e7f1401bd8..24c26e2f35cf8a1d25288ae4baab528a2f548391 100755 (executable)
Binary files a/music_assistant/server/providers/spotify/bin/librespot-linux-aarch64 and b/music_assistant/server/providers/spotify/bin/librespot-linux-aarch64 differ
index b780aba4038de77091f0c002bf79f84e73f5c861..fefd8061ee4927a2b14c3e8416dea80e7cb17090 100755 (executable)
Binary files a/music_assistant/server/providers/spotify/bin/librespot-linux-x86_64 and b/music_assistant/server/providers/spotify/bin/librespot-linux-x86_64 differ
index 60722e85f47b679a38ca74a998e45bffa83f77e2..4222d547ce308d90a06cae07c32d4e1bf0447d1f 100755 (executable)
Binary files a/music_assistant/server/providers/spotify/bin/librespot-macos-arm64 and b/music_assistant/server/providers/spotify/bin/librespot-macos-arm64 differ