Fix playlist playback: use MA enqueue and search resolution

The play_playlist_on_speaker function was sending text search queries
to raw Cast entities which can't resolve them. Now uses enqueue: "replace"
for the first track and "add" for subsequent tracks. Added 1s delay between
requests so MA can process each Apple Music search. Increased HTTP timeout
to 30s for search latency.

The caller must pass a Music Assistant entity (_2 suffix) for text-based
search to work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 11:48:33 -06:00
parent 2c6ba345b1
commit 448be52f4a

View File

@@ -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_id": search_query,
"media_content_type": "music",
"enqueue": "replace" if i == 0 else "add",
},
)
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",
},
)
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"])