diff --git a/src/haunt_fm/services/music_assistant.py b/src/haunt_fm/services/music_assistant.py index e72b094..d396768 100644 --- a/src/haunt_fm/services/music_assistant.py +++ b/src/haunt_fm/services/music_assistant.py @@ -1,3 +1,4 @@ +import asyncio import logging import httpx @@ -13,7 +14,7 @@ async def _ha_request(method: str, path: str, **kwargs) -> dict: "Authorization": f"Bearer {settings.ha_token}", "Content-Type": "application/json", } - async with httpx.AsyncClient(timeout=10) as client: + async with httpx.AsyncClient(timeout=30) as client: resp = await client.request( method, f"{settings.ha_url}{path}", headers=headers, **kwargs ) @@ -81,37 +82,31 @@ async def play_playlist_on_speaker( tracks: list[dict], speaker_entity: str, ) -> None: - """Play a list of tracks on a speaker. Each track dict has 'artist' and 'title'. + """Play a list of tracks on a speaker via Music Assistant. - Enqueues tracks via Music Assistant. + Each track dict has 'artist' and 'title'. The speaker_entity MUST be a + Music Assistant entity (the _2 suffix ones, e.g. media_player.living_room_speaker_2) + so that text search queries are resolved via Apple Music. """ if not tracks: return for i, track in enumerate(tracks): + search_query = f"{track['artist']} {track['title']}" try: - if i == 0: - # Play first track - await _ha_request( - "POST", - "/api/services/media_player/play_media", - json={ - "entity_id": speaker_entity, - "media_content_id": f"{track['artist']} - {track['title']}", - "media_content_type": "music", - }, - ) - else: - # Enqueue subsequent tracks - await _ha_request( - "POST", - "/api/services/media_player/play_media", - json={ - "entity_id": speaker_entity, - "media_content_id": f"{track['artist']} - {track['title']}", - "media_content_type": "music", - "enqueue": "add", - }, - ) + await _ha_request( + "POST", + "/api/services/media_player/play_media", + json={ + "entity_id": speaker_entity, + "media_content_id": search_query, + "media_content_type": "music", + "enqueue": "replace" if i == 0 else "add", + }, + ) + logger.info("Enqueued [%d/%d]: %s", i + 1, len(tracks), search_query) + # Brief pause between requests so MA can process each search + if i < len(tracks) - 1: + await asyncio.sleep(1) except Exception: logger.exception("Failed to enqueue %s - %s", track["artist"], track["title"])