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:
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@@ -13,7 +14,7 @@ async def _ha_request(method: str, path: str, **kwargs) -> dict:
|
|||||||
"Authorization": f"Bearer {settings.ha_token}",
|
"Authorization": f"Bearer {settings.ha_token}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
async with httpx.AsyncClient(timeout=10) as client:
|
async with httpx.AsyncClient(timeout=30) as client:
|
||||||
resp = await client.request(
|
resp = await client.request(
|
||||||
method, f"{settings.ha_url}{path}", headers=headers, **kwargs
|
method, f"{settings.ha_url}{path}", headers=headers, **kwargs
|
||||||
)
|
)
|
||||||
@@ -81,37 +82,31 @@ async def play_playlist_on_speaker(
|
|||||||
tracks: list[dict],
|
tracks: list[dict],
|
||||||
speaker_entity: str,
|
speaker_entity: str,
|
||||||
) -> None:
|
) -> 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:
|
if not tracks:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i, track in enumerate(tracks):
|
for i, track in enumerate(tracks):
|
||||||
|
search_query = f"{track['artist']} {track['title']}"
|
||||||
try:
|
try:
|
||||||
if i == 0:
|
await _ha_request(
|
||||||
# Play first track
|
"POST",
|
||||||
await _ha_request(
|
"/api/services/media_player/play_media",
|
||||||
"POST",
|
json={
|
||||||
"/api/services/media_player/play_media",
|
"entity_id": speaker_entity,
|
||||||
json={
|
"media_content_id": search_query,
|
||||||
"entity_id": speaker_entity,
|
"media_content_type": "music",
|
||||||
"media_content_id": f"{track['artist']} - {track['title']}",
|
"enqueue": "replace" if i == 0 else "add",
|
||||||
"media_content_type": "music",
|
},
|
||||||
},
|
)
|
||||||
)
|
logger.info("Enqueued [%d/%d]: %s", i + 1, len(tracks), search_query)
|
||||||
else:
|
# Brief pause between requests so MA can process each search
|
||||||
# Enqueue subsequent tracks
|
if i < len(tracks) - 1:
|
||||||
await _ha_request(
|
await asyncio.sleep(1)
|
||||||
"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",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to enqueue %s - %s", track["artist"], track["title"])
|
logger.exception("Failed to enqueue %s - %s", track["artist"], track["title"])
|
||||||
|
|||||||
Reference in New Issue
Block a user