From 425ff82781b12341d690c150503a623d9e4117d0 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 19 Feb 2025 22:43:07 +0100 Subject: [PATCH] Fix issue with pluginsource (spotify connect) playback --- music_assistant/controllers/players.py | 13 ++++++---- music_assistant/controllers/streams.py | 21 +++++++++------- music_assistant/providers/airplay/provider.py | 24 ++++++------------- .../providers/snapcast/__init__.py | 17 ++++--------- 4 files changed, 33 insertions(+), 42 deletions(-) diff --git a/music_assistant/controllers/players.py b/music_assistant/controllers/players.py index 4ade6123..b7fc935e 100644 --- a/music_assistant/controllers/players.py +++ b/music_assistant/controllers/players.py @@ -722,10 +722,12 @@ class PlayerController(CoreController): # check if player is already playing and source is different # in that case we need to stop the player first prev_source = player.active_source - if prev_source and source != prev_source and player.state != PlayerState.IDLE: - await self.cmd_stop(player_id) - await asyncio.sleep(0.5) # small delay to allow stop to process + if prev_source and source != prev_source: + if player.state != PlayerState.IDLE: + await self.cmd_stop(player_id) + await asyncio.sleep(0.5) # small delay to allow stop to process player.active_source = None + player.current_media = None # check if source is a pluginsource # in that case the source id is the lookup_key of the plugin provider if plugin_prov := self.mass.get_provider(source): @@ -735,6 +737,7 @@ class PlayerController(CoreController): # this can be used to restore the queue after a source switch if mass_queue := self.mass.player_queues.get(source): player.active_source = mass_queue.queue_id + self.update(player_id) return # basic check if player supports source selection if PlayerFeature.SELECT_SOURCE not in player.supported_features: @@ -1402,7 +1405,9 @@ class PlayerController(CoreController): # if player has plugin source active return that for plugin_source in self._get_plugin_sources(): if player.active_source == plugin_source.id or ( - player.current_media and plugin_source.id == player.current_media.queue_id + player.current_media + and plugin_source.id == player.current_media.queue_id + and player.state in (PlayerState.PLAYING, PlayerState.PAUSED) ): # copy/set current media if available if plugin_source.metadata: diff --git a/music_assistant/controllers/streams.py b/music_assistant/controllers/streams.py index 909fa7ec..496b8b1c 100644 --- a/music_assistant/controllers/streams.py +++ b/music_assistant/controllers/streams.py @@ -878,15 +878,18 @@ class StreamsController(CoreController): ) chunk_size = int(get_chunksize(output_format, 1) / 10) player.active_source = plugin_source_id - async for chunk in get_ffmpeg_stream( - audio_input=audio_input, - input_format=plugin_source.audio_format, - output_format=output_format, - chunk_size=chunk_size, - filter_params=player_filter_params, - extra_input_args=["-re"], - ): - yield chunk + try: + async for chunk in get_ffmpeg_stream( + audio_input=audio_input, + input_format=plugin_source.audio_format, + output_format=output_format, + chunk_size=chunk_size, + filter_params=player_filter_params, + extra_input_args=["-re"], + ): + yield chunk + finally: + player.active_source = player.player_id async def get_queue_item_stream( self, diff --git a/music_assistant/providers/airplay/provider.py b/music_assistant/providers/airplay/provider.py index 2f566324..b336578d 100644 --- a/music_assistant/providers/airplay/provider.py +++ b/music_assistant/providers/airplay/provider.py @@ -17,7 +17,6 @@ from music_assistant_models.enums import ( PlayerState, PlayerType, ProviderFeature, - StreamType, ) from music_assistant_models.media_items import AudioFormat from music_assistant_models.player import DeviceInfo, Player, PlayerMedia @@ -39,7 +38,6 @@ from music_assistant.helpers.datetime import utc from music_assistant.helpers.ffmpeg import get_ffmpeg_stream from music_assistant.helpers.util import TaskManager, get_ip_pton, lock, select_free_port from music_assistant.models.player_provider import PlayerProvider -from music_assistant.models.plugin import PluginProvider from music_assistant.providers.airplay.raop import RaopStreamSession from music_assistant.providers.player_group import PlayerGroupProvider @@ -321,21 +319,13 @@ class AirplayProvider(PlayerProvider): ) elif media.media_type == MediaType.PLUGIN_SOURCE: # special case: plugin source stream - # consume the stream directly, so we can skip one step in between - assert media.custom_data is not None # for type checking - provider = cast(PluginProvider, self.mass.get_provider(media.custom_data["provider"])) - plugin_source = provider.get_source() - assert plugin_source.audio_format is not None # for type checking - if plugin_source.stream_type == StreamType.CUSTOM: - input_format = plugin_source.audio_format - audio_source = provider.get_audio_stream(player_id) - else: - input_format = AIRPLAY_PCM_FORMAT - audio_source = get_ffmpeg_stream( - audio_input=media.uri, - input_format=plugin_source.audio_format, - output_format=AIRPLAY_PCM_FORMAT, - ) + input_format = AIRPLAY_PCM_FORMAT + assert media.custom_data + audio_source = self.mass.streams.get_plugin_source_stream( + plugin_source_id=media.custom_data["provider"], + output_format=AIRPLAY_PCM_FORMAT, + player_id=player_id, + ) elif media.queue_id and media.queue_id.startswith("ugp_"): # special case: UGP stream ugp_provider = cast(PlayerGroupProvider, self.mass.get_provider("player_group")) diff --git a/music_assistant/providers/snapcast/__init__.py b/music_assistant/providers/snapcast/__init__.py index e5782ced..ae939358 100644 --- a/music_assistant/providers/snapcast/__init__.py +++ b/music_assistant/providers/snapcast/__init__.py @@ -22,7 +22,6 @@ from music_assistant_models.enums import ( PlayerState, PlayerType, ProviderFeature, - StreamType, ) from music_assistant_models.errors import SetupFailedError from music_assistant_models.media_items import AudioFormat @@ -43,7 +42,6 @@ from music_assistant.helpers.audio import FFMpeg, get_ffmpeg_stream, get_player_ from music_assistant.helpers.process import AsyncProcess, check_output from music_assistant.helpers.util import get_ip_pton from music_assistant.models.player_provider import PlayerProvider -from music_assistant.models.plugin import PluginProvider if TYPE_CHECKING: from music_assistant_models.config_entries import ProviderConfig @@ -517,16 +515,11 @@ class SnapCastProvider(PlayerProvider): ) elif media.media_type == MediaType.PLUGIN_SOURCE: # special case: plugin source stream - # consume the stream directly, so we can skip one step in between - assert media.custom_data is not None # for type checking - provider = cast(PluginProvider, self.mass.get_provider(media.custom_data["provider"])) - plugin_source = provider.get_source() - assert plugin_source.audio_format is not None # for type checking - input_format = plugin_source.audio_format - audio_source = ( - provider.get_audio_stream(player_id) - if plugin_source.stream_type == StreamType.CUSTOM - else plugin_source.path + input_format = DEFAULT_SNAPCAST_FORMAT + audio_source = self.mass.streams.get_plugin_source_stream( + plugin_source_id=media.custom_data["provider"], + output_format=DEFAULT_SNAPCAST_FORMAT, + player_id=player_id, ) elif media.queue_id.startswith("ugp_"): # special case: UGP stream -- 2.34.1