# 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):
# 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:
# 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:
)
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,
PlayerState,
PlayerType,
ProviderFeature,
- StreamType,
)
from music_assistant_models.media_items import AudioFormat
from music_assistant_models.player import DeviceInfo, Player, PlayerMedia
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
)
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"))
PlayerState,
PlayerType,
ProviderFeature,
- StreamType,
)
from music_assistant_models.errors import SetupFailedError
from music_assistant_models.media_items import AudioFormat
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
)
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