Skip to content

Commit 0236da0

Browse files
authored
Merge pull request #89 from droans/next
Add `get_podcast` and `get_podcast_episodes`
2 parents ab1a0bd + e19c795 commit 0236da0

9 files changed

Lines changed: 188 additions & 18 deletions

File tree

custom_components/mass_queue/actions.py

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
ATTR_PROVIDERS,
4141
ATTR_QUEUE_ID,
4242
ATTR_QUEUE_ITEM_ID,
43+
ATTR_RELEASE_DATE,
4344
ATTR_VOLUME_LEVEL,
4445
CONF_DOWNLOAD_LOCAL,
4546
DEFAULT_QUEUE_ITEMS_LIMIT,
@@ -365,6 +366,12 @@ async def get_playlist_details(self, playlist_uri):
365366
LOGGER.debug(f"Getting album details for provider {provider}")
366367
return await self._client.music.get_playlist(item_id, provider)
367368

369+
async def get_podcast_details(self, podcast_uri):
370+
"""Retrieves the details for a podcast."""
371+
provider, item_id = parse_uri(podcast_uri)
372+
LOGGER.debug(f"Getting podcast details for provider {provider}")
373+
return await self._client.music.get_podcast(item_id, provider)
374+
368375
async def get_artist_tracks(self, artist_uri: str, page: int | None = None):
369376
"""Retrieves a limited number of tracks from an artist."""
370377
details = await self.get_artist_details(artist_uri)
@@ -399,6 +406,17 @@ async def get_album_tracks(self, album_uri: str, page: int | None = None):
399406
)
400407
return [self.format_track_item(item.to_dict()) for item in resp]
401408

409+
async def get_podcast_episodes(self, podcast_uri):
410+
"""Retrieves all episodes for a podcast."""
411+
provider, item_id = parse_uri(podcast_uri)
412+
LOGGER.debug(
413+
f"Getting podcast episodes for provider {provider}, item_id {item_id}",
414+
)
415+
resp: list = await self._client.music.get_podcast_episodes(item_id, provider)
416+
formatted = [self.format_podcast_episode(item.to_dict()) for item in resp]
417+
formatted.sort(key=lambda x: x[ATTR_RELEASE_DATE], reverse=True)
418+
return formatted
419+
402420
async def get_playlist_tracks(self, playlist_uri: str, page: int | None = None):
403421
"""Retrieves all playlist items."""
404422
provider, item_id = parse_uri(playlist_uri)
@@ -418,25 +436,37 @@ def format_playlist_track(self, playlist_track: dict) -> TRACK_ITEM_SCHEMA:
418436
result[ATTR_POSITION] = playlist_track["position"]
419437
return result
420438

421-
def format_track_item(self, playlist_item: dict) -> TRACK_ITEM_SCHEMA:
422-
"""Processes the individual items in a playlist."""
423-
media_title = playlist_item.get("name") or "N/A"
424-
media_album = playlist_item.get("album") or "N/A"
439+
def format_track_item(self, track_item: dict) -> TRACK_ITEM_SCHEMA:
440+
"""Process an individual track item."""
441+
result = self.format_item(track_item)
442+
media_album = track_item.get("album") or "N/A"
425443
media_album_name = "" if media_album is None else media_album.get("name", "")
426-
media_content_id = playlist_item["uri"]
427-
media_image = find_image(playlist_item) or ""
428-
local_image_encoded = playlist_item.get(ATTR_LOCAL_IMAGE_ENCODED)
429-
favorite = playlist_item["favorite"]
430-
duration = playlist_item["duration"] or 0
431-
432-
artists = playlist_item["artists"]
444+
artists = track_item["artists"]
433445
artist_names = [artist["name"] for artist in artists]
434446
media_artist = ", ".join(artist_names)
447+
result[ATTR_MEDIA_ALBUM_NAME] = media_album_name
448+
result[ATTR_MEDIA_ARTIST] = media_artist
449+
return result
450+
451+
def format_podcast_episode(self, podcast_episode: dict) -> TRACK_ITEM_SCHEMA:
452+
"""Process an individual track item."""
453+
result = self.format_item(podcast_episode)
454+
result[ATTR_RELEASE_DATE] = podcast_episode.get("metadata", {}).get(
455+
"release_date",
456+
)
457+
return result
458+
459+
def format_item(self, media_item: dict) -> TRACK_ITEM_SCHEMA:
460+
"""Processes the individual items in a playlist."""
461+
media_title = media_item.get("name") or "N/A"
462+
media_content_id = media_item["uri"]
463+
media_image = find_image(media_item) or ""
464+
local_image_encoded = media_item.get(ATTR_LOCAL_IMAGE_ENCODED)
465+
favorite = media_item["favorite"]
466+
duration = media_item["duration"] or 0
435467
response: ServiceResponse = TRACK_ITEM_SCHEMA(
436468
{
437469
ATTR_MEDIA_TITLE: media_title,
438-
ATTR_MEDIA_ALBUM_NAME: media_album_name,
439-
ATTR_MEDIA_ARTIST: media_artist,
440470
ATTR_MEDIA_CONTENT_ID: media_content_id,
441471
ATTR_DURATION: duration,
442472
ATTR_MEDIA_IMAGE: media_image,

custom_components/mass_queue/const.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
SERVICE_GET_ARTIST_TRACKS = "get_artist_tracks"
1818
SERVICE_GET_PLAYLIST = "get_playlist"
1919
SERVICE_GET_PLAYLIST_TRACKS = "get_playlist_tracks"
20+
SERVICE_GET_PODCAST = "get_podcast"
21+
SERVICE_GET_PODCAST_EPISODES = "get_podcast_episodes"
2022
SERVICE_GET_QUEUE_ITEMS = "get_queue_items"
2123
SERVICE_GET_RECOMMENDATIONS = "get_recommendations"
2224
SERVICE_PLAY_QUEUE_ITEM = "play_queue_item"
@@ -54,6 +56,7 @@
5456
ATTR_QUEUE_ID = "active_queue"
5557
ATTR_QUEUE_ITEM_ID = "queue_item_id"
5658
ATTR_QUEUE_ITEMS = "queue_items"
59+
ATTR_RELEASE_DATE = "release_date"
5760
ATTR_VOLUME_LEVEL = "volume_level"
5861

5962
CONF_DOWNLOAD_LOCAL = "download_local"

custom_components/mass_queue/icons.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"get_artist_tracks": { "service": "mdi:account-music"},
77
"get_playlist": { "service": "mdi:playlist-music"},
88
"get_playlist_tracks": { "service": "mdi:playlist-music"},
9+
"get_podcast": { "service": "mdi:podcast"},
10+
"get_podcast_episodes": { "service": "mdi:podcast"},
911
"get_queue_items": { "service": "mdi:playlist-music" },
1012
"move_queue_item_down": { "service": "mdi:arrow-down" },
1113
"move_queue_item_next": { "service": "mdi:arrow-collapse-up" },

custom_components/mass_queue/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
"issue_tracker": "https://github.com/droans/mass_queue/issues",
1212
"requirements": ["music-assistant-client"],
1313
"ssdp": [],
14-
"version": "0.9.2",
14+
"version": "0.10.0-b.2",
1515
"zeroconf": ["_mass._tcp.local."]
1616
}

custom_components/mass_queue/schemas.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
TRACK_ITEM_SCHEMA = vol.Schema(
6262
{
6363
vol.Required(ATTR_MEDIA_TITLE): str,
64-
vol.Required(ATTR_MEDIA_ALBUM_NAME): str,
65-
vol.Required(ATTR_MEDIA_ARTIST): str,
64+
vol.Optional(ATTR_MEDIA_ALBUM_NAME): str,
65+
vol.Optional(ATTR_MEDIA_ARTIST): str,
6666
vol.Required(ATTR_MEDIA_CONTENT_ID): str,
6767
vol.Required(ATTR_MEDIA_IMAGE): str,
6868
vol.Required(ATTR_FAVORITE): bool,
@@ -137,6 +137,13 @@
137137
},
138138
)
139139

140+
GET_PODCAST_EPISODES_SERVICE_SCHEMA = vol.Schema(
141+
{
142+
vol.Required(ATTR_CONFIG_ENTRY_ID): str,
143+
vol.Required(ATTR_URI): str,
144+
},
145+
)
146+
140147
GET_DATA_SERVICE_SCHEMA = vol.Schema(
141148
{
142149
vol.Required(ATTR_CONFIG_ENTRY_ID): str,

custom_components/mass_queue/services.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from .const import (
1212
ATTR_CONFIG_ENTRY_ID,
13+
ATTR_PAGE,
1314
ATTR_PLAYER_ENTITY,
1415
ATTR_PLAYLIST_ID,
1516
ATTR_POSITIONS_TO_REMOVE,
@@ -25,6 +26,8 @@
2526
SERVICE_GET_GROUP_VOLUME,
2627
SERVICE_GET_PLAYLIST,
2728
SERVICE_GET_PLAYLIST_TRACKS,
29+
SERVICE_GET_PODCAST,
30+
SERVICE_GET_PODCAST_EPISODES,
2831
SERVICE_GET_QUEUE_ITEMS,
2932
SERVICE_GET_RECOMMENDATIONS,
3033
SERVICE_MOVE_QUEUE_ITEM_DOWN,
@@ -41,6 +44,7 @@
4144
CLEAR_QUEUE_FROM_HERE_SERVICE_SCHEMA,
4245
GET_DATA_SERVICE_SCHEMA,
4346
GET_GROUP_VOLUME_SERVICE_SCHEMA,
47+
GET_PODCAST_EPISODES_SERVICE_SCHEMA,
4448
GET_RECOMMENDATIONS_SERVICE_SCHEMA,
4549
GET_TRACKS_SERVICE_SCHEMA,
4650
MOVE_QUEUE_ITEM_DOWN_SERVICE_SCHEMA,
@@ -165,6 +169,13 @@ def register_actions(hass) -> None:
165169
schema=GET_TRACKS_SERVICE_SCHEMA,
166170
supports_response=SupportsResponse.ONLY,
167171
)
172+
hass.services.async_register(
173+
DOMAIN,
174+
SERVICE_GET_PODCAST_EPISODES,
175+
get_podcast_episodes,
176+
schema=GET_PODCAST_EPISODES_SERVICE_SCHEMA,
177+
supports_response=SupportsResponse.ONLY,
178+
)
168179
hass.services.async_register(
169180
DOMAIN,
170181
SERVICE_GET_ALBUM,
@@ -186,6 +197,13 @@ def register_actions(hass) -> None:
186197
schema=GET_DATA_SERVICE_SCHEMA,
187198
supports_response=SupportsResponse.ONLY,
188199
)
200+
hass.services.async_register(
201+
DOMAIN,
202+
SERVICE_GET_PODCAST,
203+
get_podcast,
204+
schema=GET_DATA_SERVICE_SCHEMA,
205+
supports_response=SupportsResponse.ONLY,
206+
)
189207
hass.services.async_register(
190208
DOMAIN,
191209
SERVICE_REMOVE_PLAYLIST_TRACKS,
@@ -320,11 +338,12 @@ async def get_album_tracks(call: ServiceCall):
320338
"""Gets all tracks in an album."""
321339
config_entry = call.data[ATTR_CONFIG_ENTRY_ID]
322340
uri = call.data[ATTR_URI]
341+
page = call.data.get(ATTR_PAGE)
323342
hass = call.hass
324343
entry = hass.config_entries.async_get_entry(config_entry)
325344
actions = entry.runtime_data.actions
326345
return {
327-
"tracks": await actions.get_album_tracks(uri),
346+
"tracks": await actions.get_album_tracks(uri, page),
328347
}
329348

330349

@@ -344,11 +363,24 @@ async def get_playlist_tracks(call: ServiceCall):
344363
"""Gets all tracks in a playlist."""
345364
config_entry = call.data[ATTR_CONFIG_ENTRY_ID]
346365
uri = call.data[ATTR_URI]
366+
page = call.data.get(ATTR_PAGE)
347367
hass = call.hass
348368
entry = hass.config_entries.async_get_entry(config_entry)
349369
actions = entry.runtime_data.actions
350370
return {
351-
"tracks": await actions.get_playlist_tracks(uri),
371+
"tracks": await actions.get_playlist_tracks(uri, page),
372+
}
373+
374+
375+
async def get_podcast_episodes(call: ServiceCall):
376+
"""Gets all episodes for a podcast."""
377+
config_entry = call.data[ATTR_CONFIG_ENTRY_ID]
378+
uri = call.data[ATTR_URI]
379+
hass = call.hass
380+
entry = hass.config_entries.async_get_entry(config_entry)
381+
actions = entry.runtime_data.actions
382+
return {
383+
"episodes": await actions.get_podcast_episodes(uri),
352384
}
353385

354386

@@ -382,6 +414,16 @@ async def get_playlist(call: ServiceCall):
382414
return (await actions.get_playlist_details(uri)).to_dict()
383415

384416

417+
async def get_podcast(call: ServiceCall):
418+
"""Returns the details about a podcast from the server."""
419+
config_entry = call.data[ATTR_CONFIG_ENTRY_ID]
420+
uri = call.data[ATTR_URI]
421+
hass = call.hass
422+
entry = hass.config_entries.async_get_entry(config_entry)
423+
actions = entry.runtime_data.actions
424+
return (await actions.get_podcast_details(uri)).to_dict()
425+
426+
385427
async def remove_playlist_tracks(call: ServiceCall):
386428
"""Removes one or more items from a playlist."""
387429
config_entry = call.data[ATTR_CONFIG_ENTRY_ID]

custom_components/mass_queue/services.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,21 @@ get_artist_tracks:
289289
mode: box
290290
required: false
291291
example: 0
292+
get_podcast_episodes:
293+
fields:
294+
config_entry_id:
295+
name: Config Entry ID
296+
required: true
297+
selector:
298+
config_entry:
299+
integration: mass_queue
300+
uri:
301+
name: Podcast URI
302+
description: URI for the podcast
303+
selector:
304+
text:
305+
example: "library://podcast/12"
306+
required: true
292307
get_album:
293308
fields:
294309
config_entry_id:
@@ -334,6 +349,21 @@ get_playlist:
334349
text:
335350
example: "library://playlist/12"
336351
required: true
352+
get_podcast:
353+
fields:
354+
config_entry_id:
355+
name: Config Entry ID
356+
required: true
357+
selector:
358+
config_entry:
359+
integration: mass_queue
360+
uri:
361+
name: Podcast URI
362+
description: URI for the podcast
363+
selector:
364+
text:
365+
example: "library://podcast/12"
366+
required: true
337367
remove_playlist_tracks:
338368
fields:
339369
config_entry_id:

custom_components/mass_queue/strings.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,20 @@
297297
}
298298
}
299299
},
300+
"get_podcast_episodes": {
301+
"name": "Get Podcast Episodes",
302+
"description": "Returns all episodes for the podcast given.",
303+
"fields": {
304+
"config_entry_id": {
305+
"name": "Config Entry ID",
306+
"description": "Config Entry ID for the Music Assistant Queue Items instance."
307+
},
308+
"uri": {
309+
"name": "Podcast URI",
310+
"description": "URI for the podcast."
311+
}
312+
}
313+
},
300314
"get_playlist": {
301315
"name": "Get Playlist",
302316
"description": "Returns information about a playlist from the server.",
@@ -339,6 +353,20 @@
339353
}
340354
}
341355
},
356+
"get_podcast": {
357+
"name": "Get Podcast",
358+
"description": "Returns information about a podcast from the server.",
359+
"fields": {
360+
"config_entry_id": {
361+
"name": "Config Entry ID",
362+
"description": "Config Entry ID for the Music Assistant Queue Items instance."
363+
},
364+
"uri": {
365+
"name": "Podcast URI",
366+
"description": "URI for the podcast."
367+
}
368+
}
369+
},
342370
"remove_playlist_tracks": {
343371
"name": "Remove Playlist Tracks",
344372
"description": "Removes one or more tracks from a playlist based on position.",

custom_components/mass_queue/translations/en.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,20 @@
273273
}
274274
}
275275
},
276+
"get_podcast_episodes": {
277+
"name": "Get Podcast Episodes",
278+
"description": "Returns all episodes for the podcast given.",
279+
"fields": {
280+
"config_entry_id": {
281+
"name": "Config Entry ID",
282+
"description": "Config Entry ID for the Music Assistant Queue Items instance."
283+
},
284+
"uri": {
285+
"name": "Podcast URI",
286+
"description": "URI for the podcast."
287+
}
288+
}
289+
},
276290
"get_playlist": {
277291
"name": "Get Playlist",
278292
"description": "Returns information about a playlist from the server.",
@@ -315,6 +329,20 @@
315329
}
316330
}
317331
},
332+
"get_podcast": {
333+
"name": "Get Podcast",
334+
"description": "Returns information about a podcast from the server.",
335+
"fields": {
336+
"config_entry_id": {
337+
"name": "Config Entry ID",
338+
"description": "Config Entry ID for the Music Assistant Queue Items instance."
339+
},
340+
"uri": {
341+
"name": "Podcast URI",
342+
"description": "URI for the podcast."
343+
}
344+
}
345+
},
318346
"remove_playlist_tracks": {
319347
"name": "Remove Playlist Tracks",
320348
"description": "Removes one or more tracks from a playlist based on position.",

0 commit comments

Comments
 (0)