Skip to content

Commit

Permalink
Fix Sonos play imported playlists (#113934)
Browse files Browse the repository at this point in the history
  • Loading branch information
PeteRager authored and frenck committed Apr 2, 2024
1 parent 85fb4a2 commit 2ce7841
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 6 deletions.
14 changes: 14 additions & 0 deletions homeassistant/components/sonos/media_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,20 @@ def get_media(
"""Fetch media/album."""
search_type = MEDIA_TYPES_TO_SONOS.get(search_type, search_type)

if search_type == "playlists":
# Format is S:TITLE or S:ITEM_ID
splits = item_id.split(":")
title = splits[1] if len(splits) > 1 else None
playlist = next(
(
p
for p in media_library.get_playlists()
if (item_id == p.item_id or title == p.title)
),
None,
)
return playlist

if not item_id.startswith("A:ALBUM") and search_type == SONOS_ALBUM:
item_id = "A:ALBUMARTIST/" + "/".join(item_id.split("/")[2:])

Expand Down
12 changes: 6 additions & 6 deletions homeassistant/components/sonos/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,13 +626,13 @@ def _play_media(
soco.play_uri(media_id, force_radio=is_radio)
elif media_type == MediaType.PLAYLIST:
if media_id.startswith("S:"):
item = media_browser.get_media(self.media.library, media_id, media_type)
soco.play_uri(item.get_uri())
return
try:
playlist = media_browser.get_media(
self.media.library, media_id, media_type
)
else:
playlists = soco.get_sonos_playlists(complete_result=True)
playlist = next(p for p in playlists if p.title == media_id)
except StopIteration:
playlist = next((p for p in playlists if p.title == media_id), None)
if not playlist:
_LOGGER.error('Could not find a Sonos playlist named "%s"', media_id)
else:
soco.clear_queue()
Expand Down
117 changes: 117 additions & 0 deletions tests/components/sonos/test_media_player.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
"""Tests for the Sonos Media Player platform."""

import logging

import pytest

from homeassistant.components.media_player import (
DOMAIN as MP_DOMAIN,
SERVICE_PLAY_MEDIA,
)
from homeassistant.const import STATE_IDLE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import (
Expand All @@ -8,6 +16,8 @@
DeviceRegistry,
)

from .conftest import SoCoMockFactory


async def test_device_registry(
hass: HomeAssistant, device_registry: DeviceRegistry, async_autosetup_sonos, soco
Expand Down Expand Up @@ -53,3 +63,110 @@ async def test_entity_basic(
assert attributes["friendly_name"] == "Zone A"
assert attributes["is_volume_muted"] is False
assert attributes["volume_level"] == 0.19


class _MockMusicServiceItem:
"""Mocks a Soco MusicServiceItem."""

def __init__(
self,
title: str,
item_id: str,
parent_id: str,
item_class: str,
) -> None:
"""Initialize the mock item."""
self.title = title
self.item_id = item_id
self.item_class = item_class
self.parent_id = parent_id

def get_uri(self) -> str:
"""Return URI."""
return self.item_id.replace("S://", "x-file-cifs://")


_mock_playlists = [
_MockMusicServiceItem(
"playlist1",
"S://192.168.1.68/music/iTunes/iTunes%20Music%20Library.xml#GUID_1",
"A:PLAYLISTS",
"object.container.playlistContainer",
),
_MockMusicServiceItem(
"playlist2",
"S://192.168.1.68/music/iTunes/iTunes%20Music%20Library.xml#GUID_2",
"A:PLAYLISTS",
"object.container.playlistContainer",
),
]


@pytest.mark.parametrize(
("media_content_id", "expected_item_id"),
[
(
_mock_playlists[0].item_id,
_mock_playlists[0].item_id,
),
(
f"S:{_mock_playlists[1].title}",
_mock_playlists[1].item_id,
),
],
)
async def test_play_media_music_library_playlist(
hass: HomeAssistant,
soco_factory: SoCoMockFactory,
async_autosetup_sonos,
discover,
media_content_id,
expected_item_id,
) -> None:
"""Test that playlists can be found by id or title."""
soco_mock = soco_factory.mock_list.get("192.168.42.2")
soco_mock.music_library.get_playlists.return_value = _mock_playlists

await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
{
"entity_id": "media_player.zone_a",
"media_content_type": "playlist",
"media_content_id": media_content_id,
},
blocking=True,
)

assert soco_mock.clear_queue.call_count == 1
assert soco_mock.add_to_queue.call_count == 1
assert soco_mock.add_to_queue.call_args_list[0].args[0].item_id == expected_item_id
assert soco_mock.play_from_queue.call_count == 1


async def test_play_media_music_library_playlist_dne(
hass: HomeAssistant,
soco_factory: SoCoMockFactory,
async_autosetup_sonos,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test error handling when attempting to play a non-existent playlist ."""
media_content_id = "S:nonexistent"
soco_mock = soco_factory.mock_list.get("192.168.42.2")
soco_mock.music_library.get_playlists.return_value = _mock_playlists

with caplog.at_level(logging.ERROR):
caplog.clear()
await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
{
"entity_id": "media_player.zone_a",
"media_content_type": "playlist",
"media_content_id": media_content_id,
},
blocking=True,
)
assert soco_mock.play_uri.call_count == 0
assert media_content_id in caplog.text
assert "playlist" in caplog.text

0 comments on commit 2ce7841

Please sign in to comment.