Skip to content

Commit

Permalink
Some improvements to the HA services (again) (#1596)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt authored Nov 1, 2023
1 parent 534449a commit 1020b2a
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 54 deletions.
91 changes: 52 additions & 39 deletions custom_components/mass/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@
ATTR_RADIO_MODE = "radio_mode"
ATTR_MEDIA_ID = "media_id"
ATTR_MEDIA_TYPE = "media_type"
ATTR_ARTIST = "artist"
ATTR_ALBUM = "album"


async def async_setup_entry(
Expand Down Expand Up @@ -125,10 +127,12 @@ async def handle_player_added(event: MassEvent) -> None:
platform.async_register_entity_service(
SERVICE_PLAY_MEDIA_ADVANCED,
{
vol.Optional(ATTR_MEDIA_TYPE): vol.Coerce(MediaType),
vol.Required(ATTR_MEDIA_ID): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_MEDIA_TYPE): vol.Coerce(MediaType),
vol.Exclusive(ATTR_MEDIA_ENQUEUE, "enqueue_announce"): vol.Coerce(QueueOption),
vol.Exclusive(ATTR_MEDIA_ANNOUNCE, "enqueue_announce"): cv.boolean,
vol.Optional(ATTR_ARTIST): cv.string,
vol.Optional(ATTR_ALBUM): cv.string,
vol.Optional(ATTR_RADIO_MODE): vol.Coerce(bool),
},
"_async_play_media_advanced",
Expand Down Expand Up @@ -382,10 +386,12 @@ async def async_play_media(
async def _async_play_media_advanced(
self,
media_id: list[str],
artist: str | None = None,
album: str | None = None,
enqueue: MediaPlayerEnqueue | QueueOption | None = QueueOption.PLAY,
announce: bool | None = None, # noqa: ARG002
radio_mode: bool | None = None, # noqa: ARG002
media_type: str | None = None, # noqa: ARG002
radio_mode: bool | None = None,
media_type: str | None = None,
) -> None:
"""Send the play_media command to the media player."""
# pylint: disable=too-many-arguments
Expand All @@ -403,37 +409,42 @@ async def _async_play_media_advanced(
media_uris.append(item.uri)
continue
# lookup by name
if item := await self._get_item_by_name(media_id_str, media_type):
if item := await self._get_item_by_name(media_id_str, artist, album, media_type):
media_uris.append(item.uri)

if not media_uris:
return
raise MediaNotFoundError(f"Could not resolve {media_id} to playable media item")

# determine active queue to send the play request to
if queue := self.mass.players.get_player_queue(self.player.active_source):
queue_id = queue.queue_id
else:
queue_id = self.player_id

# announce/alert support (WIP)
if announce and radio_mode:
radio_mode = None
if announce is None and "/api/tts_proxy" in media_id:
announce = True
if announce:
raise NotImplementedError("Music Assistant does not yet support announcements")

await self.mass.players.play_media(
queue_id, media=media_uris, option=enqueue, radio_mode=radio_mode
)

# announce/alert support
# is_tts = "/api/tts_proxy" in media_id
# if announce or is_tts:
# self.hass.create_task(
# self.player.active_queue.play_announcement(media_id, is_tts)
# )
# else:
# await self.player.active_queue.play_media(media_id, queue_opt)

async def async_browse_media(
self, media_content_type=None, media_content_id=None
) -> BrowseMedia:
"""Implement the websocket media browsing helper."""
return await async_browse_media(self.hass, self.mass, media_content_id, media_content_type)

async def _get_item_by_name(
self, name: str, media_type: str | None = None
self,
name: str,
artist: str | None = None,
album: str | None = None,
media_type: str | None = None,
) -> MediaItemType | None:
"""Try to find a media item (such as a playlist) by name."""
# pylint: disable=too-many-nested-blocks
Expand All @@ -443,42 +454,44 @@ async def _get_item_by_name(
for x in (
self.mass.music.get_library_playlists,
self.mass.music.get_library_radios,
self.mass.music.get_library_albums,
self.mass.music.get_library_tracks,
self.mass.music.get_library_albums,
self.mass.music.get_library_artists,
)
if not media_type or media_type.lower() in x.__name__
]
if not media_type:
# address (possible) voice command with mediatype in search string
for media_type_str in ("artist", "album", "track", "playlist"):
media_type_subst_str = f"{media_type_str} "
if media_type_subst_str in searchname:
media_type = MediaType(media_type_str)
searchname = searchname.replace(media_type_subst_str, "")
break

# prefer (exact) lookup in the library by name
for func in library_functions:
result = await func(search=searchname)
for item in result.items:
# handle optional artist filter
if (
artist
and (artists := getattr(item, "artists", None))
and not any(x for x in artists if x.name.lower() == artist.lower())
):
continue
# handle optional album filter
if (
album
and (item_album := getattr(item, "album", None))
and item_album.name.lower() != album.lower()
):
continue
if searchname == item.name.lower():
return item
# repeat but account for tracks or albums where an artist name is used
if func in (self.mass.music.get_library_tracks, self.mass.music.get_library_albums):
for splitter in (" - ", " by "):
if splitter in searchname:
artistname, title = searchname.split(splitter, 1)
result = await func(search=title)
for item in result.items:
if item.name.lower() != title:
continue
for artist in item.artists:
if artist.name.lower() == artistname:
return item
# nothing found in the library, fallback to search
# nothing found in the library, fallback to global search
search_name = name
if album and artist:
search_name = f"{artist} - {album} - {name}"
elif album:
search_name = f"{album} - {name}"
elif artist:
search_name = f"{artist} - {name}"
result = await self.mass.music.search(
searchname, media_types=[media_type] if media_type else MediaType.ALL
search_query=search_name,
media_types=[media_type] if media_type else MediaType.ALL,
limit=5,
)
for results in (
result.tracks,
Expand Down
19 changes: 16 additions & 3 deletions custom_components/mass/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

SERVICE_SEARCH = "search"
ATTR_MEDIA_TYPE = "media_type"
ATTR_QUERY = "query"
ATTR_SEARCH_NAME = "name"
ATTR_SEARCH_ARTIST = "artist"
ATTR_SEARCH_ALBUM = "album"
ATTR_LIMIT = "limit"


Expand All @@ -25,8 +27,17 @@ def register_services(hass: HomeAssistant) -> None:
async def handle_search(call: ServiceCall) -> ServiceResponse:
"""Handle queue_command service."""
mass = get_mass(hass)
search_name = call.data[ATTR_SEARCH_NAME]
search_artist = call.data.get(ATTR_SEARCH_ARTIST)
search_album = call.data.get(ATTR_SEARCH_ALBUM)
if search_album and search_artist:
search_name = f"{search_artist} - {search_album} - {search_name}"
elif search_album:
search_name = f"{search_album} - {search_name}"
elif search_artist:
search_name = f"{search_artist} - {search_name}"
result = await mass.music.search(
search_query=call.data[ATTR_QUERY],
search_query=search_name,
media_types=call.data.get(ATTR_MEDIA_TYPE, MediaType.ALL),
limit=call.data[ATTR_LIMIT],
)
Expand Down Expand Up @@ -64,8 +75,10 @@ def compact_item(item: dict[str, Any]) -> dict[str, Any]:
handle_search,
schema=vol.Schema(
{
vol.Required(ATTR_QUERY): cv.string,
vol.Required(ATTR_SEARCH_NAME): cv.string,
vol.Optional(ATTR_MEDIA_TYPE): vol.All(cv.ensure_list, [vol.Coerce(MediaType)]),
vol.Optional(ATTR_SEARCH_ARTIST): cv.string,
vol.Optional(ATTR_SEARCH_ALBUM): cv.string,
vol.Optional(ATTR_LIMIT, default=5): vol.Coerce(int),
}
),
Expand Down
27 changes: 24 additions & 3 deletions custom_components/mass/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ play_media:
example: "true"
selector:
boolean:
artist:
required: false
example: "Queen"
selector:
text:
album:
required: false
example: "News of the world"
selector:
text:
radio_mode:
required: false
advanced: true
Expand All @@ -55,9 +65,9 @@ play_media:

search:
fields:
query:
name:
required: true
example: "Queen - Innuendo"
example: "We Are The Champions"
selector:
text:
media_type:
Expand All @@ -72,10 +82,21 @@ search:
- playlist
- track
- radio
artist:
required: false
example: "Queen"
selector:
text:
album:
required: false
example: "News of the world"
selector:
text:
limit:
required: false
advanced: true
example: 25
default: 25
default: 5
selector:
number:
min: 1
Expand Down
26 changes: 21 additions & 5 deletions custom_components/mass/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@
"name": "Announce",
"description": "If the media should be played as an announcement."
},
"artist": {
"name": "Artist name",
"description": "When specifying a track or album by name in the Media ID field, you can optionally restrict results by this artist name."
},
"album": {
"name": "Album name",
"description": "When specifying a track by name in the Media ID field, you can optionally restrict results by this album name."
},
"radio_mode": {
"name": "Enable Radio Mode",
"description": "Enable radio mode to auto generate a playlist based on the selection."
Expand All @@ -89,13 +97,21 @@
"name": "Search Music Assistant",
"description": "Perform a global search on the Music Assistant library and all providers.",
"fields": {
"query": {
"name": "Query",
"description": "The search query."
"name": {
"name": "Search name",
"description": "The name/title to search for."
},
"media_type": {
"name": "Content type(s)",
"description": "The type of the content to search. Such as artist, album, track or playlist. All types if omitted."
"name": "Media type(s)",
"description": "The type of the content to search. Such as artist, album, track, radio or playlist. All types if omitted."
},
"artist": {
"name": "Artist name",
"description": "When specifying a track or album name in the name field, you can optionally restrict results by this artist name."
},
"album": {
"name": "Album name",
"description": "When specifying a track name in the name field, you can optionally restrict results by this album name."
},
"limit": {
"name": "Limit",
Expand Down
24 changes: 20 additions & 4 deletions custom_components/mass/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@
"name": "Announce",
"description": "If the media should be played as an announcement."
},
"artist": {
"name": "Artist name",
"description": "When specifying a track or album by name in the Media ID field, you can optionally restrict results by this artist name."
},
"album": {
"name": "Album name",
"description": "When specifying a track by name in the Media ID field, you can optionally restrict results by this album name."
},
"radio_mode": {
"name": "Enable Radio Mode",
"description": "Enable radio mode to auto generate a playlist based on the selection."
Expand All @@ -89,13 +97,21 @@
"name": "Search Music Assistant",
"description": "Perform a global search on the Music Assistant library and all providers.",
"fields": {
"query": {
"name": "Query",
"description": "The search query."
"name": {
"name": "Search name",
"description": "The name/title to search for."
},
"media_type": {
"name": "Media type(s)",
"description": "The type of the content to search. Such as artist, album, track or playlist. All types if omitted."
"description": "The type of the content to search. Such as artist, album, track, radio or playlist. All types if omitted."
},
"artist": {
"name": "Artist name",
"description": "When specifying a track or album name in the name field, you can optionally restrict results by this artist name."
},
"album": {
"name": "Album name",
"description": "When specifying a track name in the name field, you can optionally restrict results by this album name."
},
"limit": {
"name": "Limit",
Expand Down

0 comments on commit 1020b2a

Please sign in to comment.