Merge branch 'dev' into dhcp_discovery_subscribe

This commit is contained in:
J. Nick Koston 2025-04-16 14:18:57 -10:00 committed by GitHub
commit 3e487a6db7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 459 additions and 28 deletions

View File

@ -120,6 +120,7 @@ class AppleTvMediaPlayer(
"""Initialize the Apple TV media player.""" """Initialize the Apple TV media player."""
super().__init__(name, identifier, manager) super().__init__(name, identifier, manager)
self._playing: Playing | None = None self._playing: Playing | None = None
self._playing_last_updated: datetime | None = None
self._app_list: dict[str, str] = {} self._app_list: dict[str, str] = {}
@callback @callback
@ -209,6 +210,7 @@ class AppleTvMediaPlayer(
This is a callback function from pyatv.interface.PushListener. This is a callback function from pyatv.interface.PushListener.
""" """
self._playing = playstatus self._playing = playstatus
self._playing_last_updated = dt_util.utcnow()
self.async_write_ha_state() self.async_write_ha_state()
@callback @callback
@ -316,7 +318,7 @@ class AppleTvMediaPlayer(
def media_position_updated_at(self) -> datetime | None: def media_position_updated_at(self) -> datetime | None:
"""Last valid time of media position.""" """Last valid time of media position."""
if self.state in {MediaPlayerState.PLAYING, MediaPlayerState.PAUSED}: if self.state in {MediaPlayerState.PLAYING, MediaPlayerState.PAUSED}:
return dt_util.utcnow() return self._playing_last_updated
return None return None
async def async_play_media( async def async_play_media(

View File

@ -41,6 +41,7 @@ async def async_setup_entry(
DemoTVShowPlayer(), DemoTVShowPlayer(),
DemoBrowsePlayer("Browse"), DemoBrowsePlayer("Browse"),
DemoGroupPlayer("Group"), DemoGroupPlayer("Group"),
DemoSearchPlayer("Search"),
] ]
) )
@ -95,6 +96,8 @@ NETFLIX_PLAYER_SUPPORT = (
BROWSE_PLAYER_SUPPORT = MediaPlayerEntityFeature.BROWSE_MEDIA BROWSE_PLAYER_SUPPORT = MediaPlayerEntityFeature.BROWSE_MEDIA
SEARCH_PLAYER_SUPPORT = MediaPlayerEntityFeature.SEARCH_MEDIA
class AbstractDemoPlayer(MediaPlayerEntity): class AbstractDemoPlayer(MediaPlayerEntity):
"""A demo media players.""" """A demo media players."""
@ -398,3 +401,9 @@ class DemoGroupPlayer(AbstractDemoPlayer):
| MediaPlayerEntityFeature.GROUPING | MediaPlayerEntityFeature.GROUPING
| MediaPlayerEntityFeature.TURN_OFF | MediaPlayerEntityFeature.TURN_OFF
) )
class DemoSearchPlayer(AbstractDemoPlayer):
"""A Demo media player that supports searching."""
_attr_supported_features = SEARCH_PLAYER_SUPPORT

View File

@ -68,7 +68,12 @@ from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from homeassistant.util.hass_dict import HassKey from homeassistant.util.hass_dict import HassKey
from .browse_media import BrowseMedia, async_process_play_media_url # noqa: F401 from .browse_media import ( # noqa: F401
BrowseMedia,
SearchMedia,
SearchMediaQuery,
async_process_play_media_url,
)
from .const import ( # noqa: F401 from .const import ( # noqa: F401
_DEPRECATED_MEDIA_CLASS_DIRECTORY, _DEPRECATED_MEDIA_CLASS_DIRECTORY,
_DEPRECATED_SUPPORT_BROWSE_MEDIA, _DEPRECATED_SUPPORT_BROWSE_MEDIA,
@ -107,10 +112,12 @@ from .const import ( # noqa: F401
ATTR_MEDIA_ENQUEUE, ATTR_MEDIA_ENQUEUE,
ATTR_MEDIA_EPISODE, ATTR_MEDIA_EPISODE,
ATTR_MEDIA_EXTRA, ATTR_MEDIA_EXTRA,
ATTR_MEDIA_FILTER_CLASSES,
ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_PLAYLIST,
ATTR_MEDIA_POSITION, ATTR_MEDIA_POSITION,
ATTR_MEDIA_POSITION_UPDATED_AT, ATTR_MEDIA_POSITION_UPDATED_AT,
ATTR_MEDIA_REPEAT, ATTR_MEDIA_REPEAT,
ATTR_MEDIA_SEARCH_QUERY,
ATTR_MEDIA_SEASON, ATTR_MEDIA_SEASON,
ATTR_MEDIA_SEEK_POSITION, ATTR_MEDIA_SEEK_POSITION,
ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_SERIES_TITLE,
@ -128,6 +135,7 @@ from .const import ( # noqa: F401
SERVICE_CLEAR_PLAYLIST, SERVICE_CLEAR_PLAYLIST,
SERVICE_JOIN, SERVICE_JOIN,
SERVICE_PLAY_MEDIA, SERVICE_PLAY_MEDIA,
SERVICE_SEARCH_MEDIA,
SERVICE_SELECT_SOUND_MODE, SERVICE_SELECT_SOUND_MODE,
SERVICE_SELECT_SOURCE, SERVICE_SELECT_SOURCE,
SERVICE_UNJOIN, SERVICE_UNJOIN,
@ -137,7 +145,7 @@ from .const import ( # noqa: F401
MediaType, MediaType,
RepeatMode, RepeatMode,
) )
from .errors import BrowseError from .errors import BrowseError, SearchError
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -291,6 +299,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
) )
websocket_api.async_register_command(hass, websocket_browse_media) websocket_api.async_register_command(hass, websocket_browse_media)
websocket_api.async_register_command(hass, websocket_search_media)
hass.http.register_view(MediaPlayerImageView(component)) hass.http.register_view(MediaPlayerImageView(component))
await component.async_setup(config) await component.async_setup(config)
@ -447,6 +456,22 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"async_browse_media", "async_browse_media",
supports_response=SupportsResponse.ONLY, supports_response=SupportsResponse.ONLY,
) )
component.async_register_entity_service(
SERVICE_SEARCH_MEDIA,
{
vol.Optional(ATTR_MEDIA_CONTENT_TYPE): cv.string,
vol.Optional(ATTR_MEDIA_CONTENT_ID): cv.string,
vol.Required(ATTR_MEDIA_SEARCH_QUERY): cv.string,
vol.Optional(ATTR_MEDIA_FILTER_CLASSES): vol.All(
cv.ensure_list,
[vol.In([m.value for m in MediaClass])],
lambda x: {MediaClass(item) for item in x},
),
},
"async_internal_search_media",
[MediaPlayerEntityFeature.SEARCH_MEDIA],
SupportsResponse.ONLY,
)
component.async_register_entity_service( component.async_register_entity_service(
SERVICE_SHUFFLE_SET, SERVICE_SHUFFLE_SET,
{vol.Required(ATTR_MEDIA_SHUFFLE): cv.boolean}, {vol.Required(ATTR_MEDIA_SHUFFLE): cv.boolean},
@ -1157,6 +1182,29 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
""" """
raise NotImplementedError raise NotImplementedError
async def async_internal_search_media(
self,
search_query: str,
media_content_type: MediaType | str | None = None,
media_content_id: str | None = None,
media_filter_classes: list[MediaClass] | None = None,
) -> SearchMedia:
return await self.async_search_media(
query=SearchMediaQuery(
search_query=search_query,
media_content_type=media_content_type,
media_content_id=media_content_id,
media_filter_classes=media_filter_classes,
)
)
async def async_search_media(
self,
query: SearchMediaQuery,
) -> SearchMedia:
"""Search the media player."""
raise NotImplementedError
def join_players(self, group_members: list[str]) -> None: def join_players(self, group_members: list[str]) -> None:
"""Join `group_members` as a player group with the current player.""" """Join `group_members` as a player group with the current player."""
raise NotImplementedError raise NotImplementedError
@ -1360,6 +1408,75 @@ async def websocket_browse_media(
connection.send_result(msg["id"], result) connection.send_result(msg["id"], result)
@websocket_api.websocket_command(
{
vol.Required("type"): "media_player/search_media",
vol.Required("entity_id"): cv.entity_id,
vol.Inclusive(
ATTR_MEDIA_CONTENT_TYPE,
"media_ids",
"media_content_type and media_content_id must be provided together",
): str,
vol.Inclusive(
ATTR_MEDIA_CONTENT_ID,
"media_ids",
"media_content_type and media_content_id must be provided together",
): str,
vol.Required(ATTR_MEDIA_SEARCH_QUERY): str,
vol.Optional(ATTR_MEDIA_FILTER_CLASSES): vol.All(
cv.ensure_list,
[vol.In([m.value for m in MediaClass])],
lambda x: {MediaClass(item) for item in x},
),
}
)
@websocket_api.async_response
async def websocket_search_media(
hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Search media available to the media_player entity.
To use, media_player integrations can implement
MediaPlayerEntity.async_search_media()
"""
player = hass.data[DATA_COMPONENT].get_entity(msg["entity_id"])
if player is None:
connection.send_error(msg["id"], "entity_not_found", "Entity not found")
return
if MediaPlayerEntityFeature.SEARCH_MEDIA not in player.supported_features_compat:
connection.send_message(
websocket_api.error_message(
msg["id"], ERR_NOT_SUPPORTED, "Player does not support searching media"
)
)
return
media_content_type = msg.get(ATTR_MEDIA_CONTENT_TYPE)
media_content_id = msg.get(ATTR_MEDIA_CONTENT_ID)
query = str(msg.get(ATTR_MEDIA_SEARCH_QUERY))
media_filter_classes = msg.get(ATTR_MEDIA_FILTER_CLASSES, [])
try:
payload = await player.async_internal_search_media(
query,
media_content_type,
media_content_id,
media_filter_classes,
)
except SearchError as err:
connection.send_message(
websocket_api.error_message(msg["id"], ERR_UNKNOWN_ERROR, str(err))
)
return
result = payload.as_dict()
connection.send_result(msg["id"], result)
_FETCH_TIMEOUT = aiohttp.ClientTimeout(total=10) _FETCH_TIMEOUT = aiohttp.ClientTimeout(total=10)

View File

@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence from collections.abc import Sequence
from dataclasses import dataclass, field
from datetime import timedelta from datetime import timedelta
import logging import logging
from typing import Any from typing import Any
@ -109,6 +110,7 @@ class BrowseMedia:
children_media_class: MediaClass | str | None = None, children_media_class: MediaClass | str | None = None,
thumbnail: str | None = None, thumbnail: str | None = None,
not_shown: int = 0, not_shown: int = 0,
can_search: bool = False,
) -> None: ) -> None:
"""Initialize browse media item.""" """Initialize browse media item."""
self.media_class = media_class self.media_class = media_class
@ -121,6 +123,7 @@ class BrowseMedia:
self.children_media_class = children_media_class self.children_media_class = children_media_class
self.thumbnail = thumbnail self.thumbnail = thumbnail
self.not_shown = not_shown self.not_shown = not_shown
self.can_search = can_search
def as_dict(self, *, parent: bool = True) -> dict[str, Any]: def as_dict(self, *, parent: bool = True) -> dict[str, Any]:
"""Convert Media class to browse media dictionary.""" """Convert Media class to browse media dictionary."""
@ -135,6 +138,7 @@ class BrowseMedia:
"children_media_class": self.children_media_class, "children_media_class": self.children_media_class,
"can_play": self.can_play, "can_play": self.can_play,
"can_expand": self.can_expand, "can_expand": self.can_expand,
"can_search": self.can_search,
"thumbnail": self.thumbnail, "thumbnail": self.thumbnail,
} }
@ -163,3 +167,27 @@ class BrowseMedia:
def __repr__(self) -> str: def __repr__(self) -> str:
"""Return representation of browse media.""" """Return representation of browse media."""
return f"<BrowseMedia {self.title} ({self.media_class})>" return f"<BrowseMedia {self.title} ({self.media_class})>"
@dataclass(kw_only=True, frozen=True)
class SearchMedia:
"""Represent search results."""
version: int = field(default=1)
result: list[BrowseMedia]
def as_dict(self, *, parent: bool = True) -> dict[str, Any]:
"""Convert SearchMedia class to browse media dictionary."""
return {
"result": [item.as_dict(parent=parent) for item in self.result],
}
@dataclass(kw_only=True, frozen=True)
class SearchMediaQuery:
"""Represent a search media file."""
search_query: str
media_content_type: MediaType | str | None = field(default=None)
media_content_id: str | None = None
media_filter_classes: list[MediaClass] | None = field(default=None)

View File

@ -26,6 +26,8 @@ ATTR_MEDIA_ARTIST = "media_artist"
ATTR_MEDIA_CHANNEL = "media_channel" ATTR_MEDIA_CHANNEL = "media_channel"
ATTR_MEDIA_CONTENT_ID = "media_content_id" ATTR_MEDIA_CONTENT_ID = "media_content_id"
ATTR_MEDIA_CONTENT_TYPE = "media_content_type" ATTR_MEDIA_CONTENT_TYPE = "media_content_type"
ATTR_MEDIA_SEARCH_QUERY = "search_query"
ATTR_MEDIA_FILTER_CLASSES = "media_filter_classes"
ATTR_MEDIA_DURATION = "media_duration" ATTR_MEDIA_DURATION = "media_duration"
ATTR_MEDIA_ENQUEUE = "enqueue" ATTR_MEDIA_ENQUEUE = "enqueue"
ATTR_MEDIA_EXTRA = "extra" ATTR_MEDIA_EXTRA = "extra"
@ -174,6 +176,7 @@ SERVICE_CLEAR_PLAYLIST = "clear_playlist"
SERVICE_JOIN = "join" SERVICE_JOIN = "join"
SERVICE_PLAY_MEDIA = "play_media" SERVICE_PLAY_MEDIA = "play_media"
SERVICE_BROWSE_MEDIA = "browse_media" SERVICE_BROWSE_MEDIA = "browse_media"
SERVICE_SEARCH_MEDIA = "search_media"
SERVICE_SELECT_SOUND_MODE = "select_sound_mode" SERVICE_SELECT_SOUND_MODE = "select_sound_mode"
SERVICE_SELECT_SOURCE = "select_source" SERVICE_SELECT_SOURCE = "select_source"
SERVICE_UNJOIN = "unjoin" SERVICE_UNJOIN = "unjoin"
@ -220,6 +223,7 @@ class MediaPlayerEntityFeature(IntFlag):
GROUPING = 524288 GROUPING = 524288
MEDIA_ANNOUNCE = 1048576 MEDIA_ANNOUNCE = 1048576
MEDIA_ENQUEUE = 2097152 MEDIA_ENQUEUE = 2097152
SEARCH_MEDIA = 4194304
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5. # These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.

View File

@ -9,3 +9,7 @@ class MediaPlayerException(HomeAssistantError):
class BrowseError(MediaPlayerException): class BrowseError(MediaPlayerException):
"""Error while browsing.""" """Error while browsing."""
class SearchError(MediaPlayerException):
"""Error while searching."""

View File

@ -29,10 +29,10 @@
"public_weather": { "public_weather": {
"data": { "data": {
"area_name": "Name of the area", "area_name": "Name of the area",
"lat_ne": "North-East corner latitude", "lat_ne": "Northeast corner latitude",
"lon_ne": "North-East corner longitude", "lon_ne": "Northeast corner longitude",
"lat_sw": "South-West corner latitude", "lat_sw": "Southwest corner latitude",
"lon_sw": "South-West corner longitude", "lon_sw": "Southwest corner longitude",
"mode": "Calculation", "mode": "Calculation",
"show_on_map": "Show on map" "show_on_map": "Show on map"
}, },
@ -175,7 +175,7 @@
"state": { "state": {
"frost_guard": "Frost guard", "frost_guard": "Frost guard",
"schedule": "Schedule", "schedule": "Schedule",
"manual": "Manual" "manual": "[%key:common::state::manual%]"
} }
} }
} }
@ -206,13 +206,13 @@
"name": "Wind direction", "name": "Wind direction",
"state": { "state": {
"n": "North", "n": "North",
"ne": "North-east", "ne": "Northeast",
"e": "East", "e": "East",
"se": "South-east", "se": "Southeast",
"s": "South", "s": "South",
"sw": "South-west", "sw": "Southwest",
"w": "West", "w": "West",
"nw": "North-west" "nw": "Northwest"
} }
}, },
"wind_angle": { "wind_angle": {

View File

@ -354,11 +354,11 @@
"robot_cleaner_cleaning_mode": { "robot_cleaner_cleaning_mode": {
"name": "Cleaning mode", "name": "Cleaning mode",
"state": { "state": {
"auto": "Auto", "stop": "[%key:common::action::stop%]",
"auto": "[%key:common::state::auto%]",
"manual": "[%key:common::state::manual%]",
"part": "Partial", "part": "Partial",
"repeat": "Repeat", "repeat": "Repeat",
"manual": "Manual",
"stop": "[%key:common::action::stop%]",
"map": "Map" "map": "Map"
} }
}, },

View File

@ -61,6 +61,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription( SensorEntityDescription(
key="fuel", key="fuel",
translation_key="fuel", translation_key="fuel",
device_class=SensorDeviceClass.VOLUME,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
), ),
SensorEntityDescription( SensorEntityDescription(

View File

@ -59,7 +59,7 @@
"name": "Lamp mode", "name": "Lamp mode",
"state": { "state": {
"automatic": "Automatic", "automatic": "Automatic",
"manual": "Manual" "manual": "[%key:common::state::manual%]"
} }
}, },
"aroma_therapy_slot": { "aroma_therapy_slot": {

View File

@ -75,7 +75,6 @@ class AirConEntity(WhirlpoolEntity, ClimateEntity):
_attr_hvac_modes = SUPPORTED_HVAC_MODES _attr_hvac_modes = SUPPORTED_HVAC_MODES
_attr_max_temp = SUPPORTED_MAX_TEMP _attr_max_temp = SUPPORTED_MAX_TEMP
_attr_min_temp = SUPPORTED_MIN_TEMP _attr_min_temp = SUPPORTED_MIN_TEMP
_attr_should_poll = False
_attr_supported_features = ( _attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE

View File

@ -12,6 +12,7 @@ class WhirlpoolEntity(Entity):
"""Base class for Whirlpool entities.""" """Base class for Whirlpool entities."""
_attr_has_entity_name = True _attr_has_entity_name = True
_attr_should_poll = False
def __init__(self, appliance: Appliance, unique_id_suffix: str = "") -> None: def __init__(self, appliance: Appliance, unique_id_suffix: str = "") -> None:
"""Initialize the entity.""" """Initialize the entity."""

View File

@ -174,8 +174,6 @@ async def async_setup_entry(
class WhirlpoolSensor(WhirlpoolEntity, SensorEntity): class WhirlpoolSensor(WhirlpoolEntity, SensorEntity):
"""A class for the Whirlpool sensors.""" """A class for the Whirlpool sensors."""
_attr_should_poll = False
def __init__( def __init__(
self, appliance: Appliance, description: WhirlpoolSensorEntityDescription self, appliance: Appliance, description: WhirlpoolSensorEntityDescription
) -> None: ) -> None:

View File

@ -355,6 +355,7 @@ async def test_browse_media(
"children_media_class": "app", "children_media_class": "app",
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"not_shown": 0, "not_shown": 0,
"children": [ "children": [
@ -366,6 +367,7 @@ async def test_browse_media(
"children_media_class": None, "children_media_class": None,
"can_play": False, "can_play": False,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": "https://www.youtube.com/icon.png", "thumbnail": "https://www.youtube.com/icon.png",
}, },
{ {
@ -376,6 +378,7 @@ async def test_browse_media(
"children_media_class": None, "children_media_class": None,
"can_play": False, "can_play": False,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": "", "thumbnail": "",
}, },
], ],

View File

@ -1323,6 +1323,7 @@ async def test_async_play_media_url_m3u(
"media_content_id": "media-source://media_source/local/test.mp3", "media_content_id": "media-source://media_source/local/test.mp3",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
}, },
@ -1337,6 +1338,7 @@ async def test_async_play_media_url_m3u(
"media_content_id": ("media-source://media_source/local/test.mp4"), "media_content_id": ("media-source://media_source/local/test.mp4"),
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
}, },

View File

@ -4,6 +4,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': '', 'media_content_id': '',
@ -18,6 +19,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'music', 'media_class': 'music',
'media_content_id': '1', 'media_content_id': '1',
@ -28,6 +30,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'music', 'media_class': 'music',
'media_content_id': '2', 'media_content_id': '2',

View File

@ -1037,6 +1037,7 @@ async def test_entity_browse_media(
), ),
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }
@ -1049,6 +1050,7 @@ async def test_entity_browse_media(
"media_content_id": "media-source://media_source/local/test.mp3", "media_content_id": "media-source://media_source/local/test.mp3",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }
@ -1107,6 +1109,7 @@ async def test_entity_browse_media_audio_only(
"media_content_id": "media-source://media_source/local/test.mp3", "media_content_id": "media-source://media_source/local/test.mp3",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }
@ -2208,6 +2211,7 @@ async def test_cast_platform_browse_media(
"media_content_id": "", "media_content_id": "",
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": "https://brands.home-assistant.io/_/spotify/logo.png", "thumbnail": "https://brands.home-assistant.io/_/spotify/logo.png",
"children_media_class": None, "children_media_class": None,
} }
@ -2232,6 +2236,7 @@ async def test_cast_platform_browse_media(
"media_content_id": "", "media_content_id": "",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"children_media_class": None, "children_media_class": None,
"thumbnail": None, "thumbnail": None,
"children": [], "children": [],

View File

@ -4,6 +4,7 @@ from unittest.mock import AsyncMock
from devolo_plc_api.exceptions.device import DeviceUnavailable from devolo_plc_api.exceptions.device import DeviceUnavailable
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
from homeassistant.components.device_tracker import DOMAIN as PLATFORM from homeassistant.components.device_tracker import DOMAIN as PLATFORM
@ -25,6 +26,7 @@ STATION = CONNECTED_STATIONS[0]
SERIAL = DISCOVERY_INFO.properties["SN"] SERIAL = DISCOVERY_INFO.properties["SN"]
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_device_tracker( async def test_device_tracker(
hass: HomeAssistant, hass: HomeAssistant,
mock_device: MockDevice, mock_device: MockDevice,
@ -42,14 +44,6 @@ async def test_device_tracker(
freezer.tick(LONG_UPDATE_INTERVAL) freezer.tick(LONG_UPDATE_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
# Enable entity
entity_registry.async_update_entity(state_key, disabled_by=None)
await hass.async_block_till_done()
freezer.tick(LONG_UPDATE_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert hass.states.get(state_key) == snapshot assert hass.states.get(state_key) == snapshot
# Emulate state change # Emulate state change

View File

@ -1058,6 +1058,7 @@ async def test_browse_media(
), ),
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }
@ -1070,6 +1071,7 @@ async def test_browse_media(
"media_content_id": "media-source://media_source/local/test.mp3", "media_content_id": "media-source://media_source/local/test.mp3",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }
@ -1153,6 +1155,7 @@ async def test_browse_media_unfiltered(
), ),
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }
@ -1163,6 +1166,7 @@ async def test_browse_media_unfiltered(
"media_content_id": "media-source://media_source/local/test.mp3", "media_content_id": "media-source://media_source/local/test.mp3",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }

View File

@ -103,6 +103,7 @@ ENTITY_IDS_BY_NUMBER = {
"26": "light.living_room_rgbww_lights", "26": "light.living_room_rgbww_lights",
"27": "media_player.group", "27": "media_player.group",
"28": "media_player.browse", "28": "media_player.browse",
"29": "media_player.search",
} }
ENTITY_NUMBERS_BY_ID = {v: k for k, v in ENTITY_IDS_BY_NUMBER.items()} ENTITY_NUMBERS_BY_ID = {v: k for k, v in ENTITY_IDS_BY_NUMBER.items()}

View File

@ -184,6 +184,7 @@ async def test_browse_media(
"media_content_id": "media-source://media_source/local/test.mp3", "media_content_id": "media-source://media_source/local/test.mp3",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": None, "children_media_class": None,
} }

View File

@ -259,6 +259,13 @@ DEMO_DEVICES = [
"type": "action.devices.types.SETTOP", "type": "action.devices.types.SETTOP",
"willReportState": False, "willReportState": False,
}, },
{
"id": "media_player.search",
"name": {"name": "Search"},
"traits": ["action.devices.traits.MediaState", "action.devices.traits.OnOff"],
"type": "action.devices.types.SETTOP",
"willReportState": False,
},
{ {
"id": "fan.living_room_fan", "id": "fan.living_room_fan",
"name": {"name": "Living Room Fan"}, "name": {"name": "Living Room Fan"},

View File

@ -3,10 +3,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'track', 'media_class': 'track',
'media_content_id': 'heos://media/1/station?name=Today%27s+Hits+Radio&image_url=&playable=True&browsable=False&media_id=123456789', 'media_content_id': 'heos://media/1/station?name=Today%27s+Hits+Radio&image_url=&playable=True&browsable=False&media_id=123456789',
@ -28,6 +30,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
]), ]),
'children_media_class': None, 'children_media_class': None,
@ -43,10 +46,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'music', 'media_class': 'music',
'media_content_id': 'media-source://media_source/local/test.mp3', 'media_content_id': 'media-source://media_source/local/test.mp3',
@ -68,10 +73,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': 'heos://media/1/music_service?name=Pandora&image_url=&available=True&service_username=user', 'media_content_id': 'heos://media/1/music_service?name=Pandora&image_url=&available=True&service_username=user',
@ -82,6 +89,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': 'heos://media/3/music_service?name=TuneIn&image_url=&available=False', 'media_content_id': 'heos://media/3/music_service?name=TuneIn&image_url=&available=False',
@ -92,6 +100,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': 'music', 'children_media_class': 'music',
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': 'media-source://media_source/local/.', 'media_content_id': 'media-source://media_source/local/.',
@ -113,10 +122,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': 'heos://media/1/music_service?name=Pandora&image_url=&available=True&service_username=user', 'media_content_id': 'heos://media/1/music_service?name=Pandora&image_url=&available=True&service_username=user',
@ -127,6 +138,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': 'heos://media/3/music_service?name=TuneIn&image_url=&available=False', 'media_content_id': 'heos://media/3/music_service?name=TuneIn&image_url=&available=False',
@ -148,6 +160,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
]), ]),
'children_media_class': 'directory', 'children_media_class': 'directory',

View File

@ -15,6 +15,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',
@ -31,6 +32,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',
@ -47,6 +49,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',
@ -63,6 +66,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',
@ -85,6 +89,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',
@ -101,6 +106,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',
@ -117,6 +123,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',
@ -133,6 +140,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children': None, 'children': None,
'children_media_class': None, 'children_media_class': None,
'domain': 'jellyfin', 'domain': 'jellyfin',

View File

@ -279,6 +279,7 @@ async def test_browse_media(
"media_content_id": "COLLECTION-FOLDER-UUID", "media_content_id": "COLLECTION-FOLDER-UUID",
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": "http://localhost/Items/c22fd826-17fc-44f4-9b04-1eb3e8fb9173/Images/Backdrop.jpg", "thumbnail": "http://localhost/Items/c22fd826-17fc-44f4-9b04-1eb3e8fb9173/Images/Backdrop.jpg",
"children_media_class": None, "children_media_class": None,
} }
@ -307,6 +308,7 @@ async def test_browse_media(
"media_content_id": "EPISODE-UUID", "media_content_id": "EPISODE-UUID",
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": "http://localhost/Items/c22fd826-17fc-44f4-9b04-1eb3e8fb9173/Images/Backdrop.jpg", "thumbnail": "http://localhost/Items/c22fd826-17fc-44f4-9b04-1eb3e8fb9173/Images/Backdrop.jpg",
"children_media_class": None, "children_media_class": None,
} }

View File

@ -12,13 +12,20 @@ from homeassistant.components import media_player
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_TYPE,
ATTR_MEDIA_FILTER_CLASSES,
ATTR_MEDIA_SEARCH_QUERY,
BrowseMedia, BrowseMedia,
MediaClass, MediaClass,
MediaPlayerEnqueue, MediaPlayerEnqueue,
MediaPlayerEntity, MediaPlayerEntity,
MediaPlayerEntityFeature, MediaPlayerEntityFeature,
SearchMedia,
SearchMediaQuery,
)
from homeassistant.components.media_player.const import (
SERVICE_BROWSE_MEDIA,
SERVICE_SEARCH_MEDIA,
) )
from homeassistant.components.media_player.const import SERVICE_BROWSE_MEDIA
from homeassistant.components.websocket_api import TYPE_RESULT from homeassistant.components.websocket_api import TYPE_RESULT
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -47,6 +54,7 @@ def _create_tuples(enum: type[Enum], constant_prefix: str) -> list[tuple[Enum, s
not in [ not in [
MediaPlayerEntityFeature.MEDIA_ANNOUNCE, MediaPlayerEntityFeature.MEDIA_ANNOUNCE,
MediaPlayerEntityFeature.MEDIA_ENQUEUE, MediaPlayerEntityFeature.MEDIA_ENQUEUE,
MediaPlayerEntityFeature.SEARCH_MEDIA,
] ]
] ]
@ -315,6 +323,7 @@ async def test_media_browse(
"media_content_id": "mock-id", "media_content_id": "mock-id",
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": None, "children_media_class": None,
"thumbnail": None, "thumbnail": None,
"not_shown": 0, "not_shown": 0,
@ -411,6 +420,119 @@ async def test_media_browse_service(hass: HomeAssistant) -> None:
assert browse_res.children[1].media_content_type == "album" assert browse_res.children[1].media_content_type == "album"
async def test_media_search(
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
) -> None:
"""Test browsing media."""
await async_setup_component(
hass, "media_player", {"media_player": {"platform": "demo"}}
)
await hass.async_block_till_done()
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.demo.media_player.DemoSearchPlayer.async_search_media",
return_value=SearchMedia(
result=[
BrowseMedia(
media_class=MediaClass.DIRECTORY,
media_content_id="mock-id",
media_content_type="mock-type",
title="Mock Title",
can_play=False,
can_expand=True,
)
]
),
) as mock_search_media:
await client.send_json(
{
"id": 7,
"type": "media_player/search_media",
"entity_id": "media_player.search",
"media_content_type": "album",
"media_content_id": "abcd",
"search_query": "query",
"media_filter_classes": ["album"],
}
)
msg = await client.receive_json()
assert msg["id"] == 7
assert msg["type"] == TYPE_RESULT
assert msg["success"]
assert msg["result"]["result"] == [
{
"title": "Mock Title",
"media_class": "directory",
"media_content_type": "mock-type",
"media_content_id": "mock-id",
"children_media_class": None,
"can_play": False,
"can_expand": True,
"can_search": False,
"thumbnail": None,
"not_shown": 0,
"children": [],
}
]
assert mock_search_media.mock_calls[0].kwargs["query"] == SearchMediaQuery(
search_query="query",
media_content_type="album",
media_content_id="abcd",
media_filter_classes={MediaClass.ALBUM},
)
async def test_media_search_service(hass: HomeAssistant) -> None:
"""Test browsing media."""
await async_setup_component(
hass, "media_player", {"media_player": {"platform": "demo"}}
)
await hass.async_block_till_done()
expected = [
BrowseMedia(
media_class=MediaClass.DIRECTORY,
media_content_id="mock-id",
media_content_type="mock-type",
title="Mock Title",
can_play=False,
can_expand=True,
children=[],
)
]
with patch(
"homeassistant.components.demo.media_player.DemoSearchPlayer.async_search_media",
return_value=SearchMedia(result=expected),
) as mock_search_media:
result = await hass.services.async_call(
"media_player",
SERVICE_SEARCH_MEDIA,
{
ATTR_ENTITY_ID: "media_player.search",
ATTR_MEDIA_CONTENT_TYPE: "album",
ATTR_MEDIA_CONTENT_ID: "title=Album*",
ATTR_MEDIA_SEARCH_QUERY: "query",
ATTR_MEDIA_FILTER_CLASSES: ["album"],
},
blocking=True,
return_response=True,
)
search_res: SearchMedia = result["media_player.search"]
assert search_res.version == 1
assert search_res.result == expected
assert mock_search_media.mock_calls[0].kwargs["query"] == SearchMediaQuery(
search_query="query",
media_content_type="album",
media_content_id="title=Album*",
media_filter_classes={MediaClass.ALBUM},
)
async def test_group_members_available_when_off(hass: HomeAssistant) -> None: async def test_group_members_available_when_off(hass: HomeAssistant) -> None:
"""Test that group_members are still available when media_player is off.""" """Test that group_members are still available when media_player is off."""
await async_setup_component( await async_setup_component(

View File

@ -104,6 +104,7 @@ async def test_async_browse_media_success(
"media_content_id": "media-source://motioneye", "media_content_id": "media-source://motioneye",
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": "directory", "children_media_class": "directory",
"thumbnail": None, "thumbnail": None,
"children": [ "children": [
@ -116,6 +117,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": "directory", "children_media_class": "directory",
} }
@ -132,6 +134,7 @@ async def test_async_browse_media_success(
"media_content_id": "media-source://motioneye/74565ad414754616000674c87bdc876c", "media_content_id": "media-source://motioneye/74565ad414754616000674c87bdc876c",
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": "directory", "children_media_class": "directory",
"thumbnail": None, "thumbnail": None,
"children": [ "children": [
@ -145,6 +148,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": "directory", "children_media_class": "directory",
} }
@ -164,6 +168,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": "directory", "children_media_class": "directory",
"thumbnail": None, "thumbnail": None,
"children": [ "children": [
@ -177,6 +182,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": "video", "children_media_class": "video",
}, },
@ -190,6 +196,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": "image", "children_media_class": "image",
}, },
@ -212,6 +219,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": "video", "children_media_class": "video",
"thumbnail": None, "thumbnail": None,
"children": [ "children": [
@ -225,6 +233,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"thumbnail": None, "thumbnail": None,
"children_media_class": "directory", "children_media_class": "directory",
} }
@ -247,6 +256,7 @@ async def test_async_browse_media_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": "video", "children_media_class": "video",
"thumbnail": None, "thumbnail": None,
"children": [ "children": [
@ -261,6 +271,7 @@ async def test_async_browse_media_success(
), ),
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": "http://movie", "thumbnail": "http://movie",
"children_media_class": None, "children_media_class": None,
}, },
@ -275,6 +286,7 @@ async def test_async_browse_media_success(
), ),
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": "http://movie", "thumbnail": "http://movie",
"children_media_class": None, "children_media_class": None,
}, },
@ -289,6 +301,7 @@ async def test_async_browse_media_success(
), ),
"can_play": True, "can_play": True,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": "http://movie", "thumbnail": "http://movie",
"children_media_class": None, "children_media_class": None,
}, },
@ -327,6 +340,7 @@ async def test_async_browse_media_images_success(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": "image", "children_media_class": "image",
"thumbnail": None, "thumbnail": None,
"children": [ "children": [
@ -341,6 +355,7 @@ async def test_async_browse_media_images_success(
), ),
"can_play": False, "can_play": False,
"can_expand": False, "can_expand": False,
"can_search": False,
"thumbnail": "http://image", "thumbnail": "http://image",
"children_media_class": None, "children_media_class": None,
} }
@ -487,6 +502,7 @@ async def test_async_resolve_media_failure(
), ),
"can_play": False, "can_play": False,
"can_expand": True, "can_expand": True,
"can_search": False,
"children_media_class": "video", "children_media_class": "video",
"thumbnail": None, "thumbnail": None,
"children": [], "children": [],

View File

@ -3,10 +3,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'album', 'media_class': 'album',
'media_content_id': 'object.container.album.musicAlbum', 'media_content_id': 'object.container.album.musicAlbum',
@ -17,6 +19,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'track', 'media_class': 'track',
'media_content_id': 'object.item.audioItem.audioBook', 'media_content_id': 'object.item.audioItem.audioBook',
@ -27,6 +30,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'genre', 'media_class': 'genre',
'media_content_id': 'object.item.audioItem.audioBroadcast', 'media_content_id': 'object.item.audioItem.audioBroadcast',
@ -48,10 +52,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'album', 'media_class': 'album',
'media_content_id': 'FV:2/8', 'media_content_id': 'FV:2/8',
@ -73,10 +79,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'track', 'media_class': 'track',
'media_content_id': 'FV:2/66', 'media_content_id': 'FV:2/66',
@ -99,6 +107,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'contributing_artist', 'media_class': 'contributing_artist',
'media_content_id': 'A:ARTIST', 'media_content_id': 'A:ARTIST',
@ -109,6 +118,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'artist', 'media_class': 'artist',
'media_content_id': 'A:ALBUMARTIST', 'media_content_id': 'A:ALBUMARTIST',
@ -119,6 +129,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'album', 'media_class': 'album',
'media_content_id': 'A:ALBUM', 'media_content_id': 'A:ALBUM',
@ -129,6 +140,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'genre', 'media_class': 'genre',
'media_content_id': 'A:GENRE', 'media_content_id': 'A:GENRE',
@ -139,6 +151,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'composer', 'media_class': 'composer',
'media_content_id': 'A:COMPOSER', 'media_content_id': 'A:COMPOSER',
@ -149,6 +162,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'track', 'media_class': 'track',
'media_content_id': 'A:TRACKS', 'media_content_id': 'A:TRACKS',
@ -159,6 +173,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'playlist', 'media_class': 'playlist',
'media_content_id': 'A:PLAYLISTS', 'media_content_id': 'A:PLAYLISTS',
@ -173,6 +188,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'album', 'media_class': 'album',
'media_content_id': "A:ALBUM/A%20Hard%20Day's%20Night", 'media_content_id': "A:ALBUM/A%20Hard%20Day's%20Night",
@ -183,6 +199,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'album', 'media_class': 'album',
'media_content_id': 'A:ALBUM/Abbey%20Road', 'media_content_id': 'A:ALBUM/Abbey%20Road',
@ -193,6 +210,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'album', 'media_class': 'album',
'media_content_id': 'A:ALBUM/Between%20Good%20And%20Evil', 'media_content_id': 'A:ALBUM/Between%20Good%20And%20Evil',
@ -203,6 +221,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'album', 'media_class': 'album',
'media_content_id': "A:ALBUM/Special%20Characters,'()+", 'media_content_id': "A:ALBUM/Special%20Characters,'()+",
@ -217,6 +236,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': '', 'media_content_id': '',
@ -227,6 +247,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': 'directory', 'media_class': 'directory',
'media_content_id': '', 'media_content_id': '',

View File

@ -3,10 +3,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.PLAYLIST: 'playlist'>, 'children_media_class': <MediaClass.PLAYLIST: 'playlist'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_playlists', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_playlists',
@ -17,6 +19,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.ARTIST: 'artist'>, 'children_media_class': <MediaClass.ARTIST: 'artist'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_followed_artists', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_followed_artists',
@ -27,6 +30,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.ALBUM: 'album'>, 'children_media_class': <MediaClass.ALBUM: 'album'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_saved_albums', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_saved_albums',
@ -37,6 +41,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_saved_tracks', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_saved_tracks',
@ -47,6 +52,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.PODCAST: 'podcast'>, 'children_media_class': <MediaClass.PODCAST: 'podcast'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_saved_shows', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_saved_shows',
@ -57,6 +63,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_recently_played', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_recently_played',
@ -67,6 +74,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.ARTIST: 'artist'>, 'children_media_class': <MediaClass.ARTIST: 'artist'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_top_artists', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_top_artists',
@ -77,6 +85,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_top_tracks', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/current_user_top_tracks',
@ -87,6 +96,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.ALBUM: 'album'>, 'children_media_class': <MediaClass.ALBUM: 'album'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/new_releases', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/new_releases',
@ -108,10 +118,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.PLAYLIST: 'playlist'>, 'media_class': <MediaClass.PLAYLIST: 'playlist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:4WkWJ0EjHEFASDevhM8oPw', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:4WkWJ0EjHEFASDevhM8oPw',
@ -122,6 +134,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.PLAYLIST: 'playlist'>, 'media_class': <MediaClass.PLAYLIST: 'playlist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:1RHirWgH1weMsBLi4KOK9d', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:1RHirWgH1weMsBLi4KOK9d',
@ -143,10 +156,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.PLAYLIST: 'playlist'>, 'media_class': <MediaClass.PLAYLIST: 'playlist'>,
'media_content_id': 'spotify://32oesphrnacjcf7vw5bf6odx3/spotify:playlist:4WkWJ0EjHEFASDevhM8oPw', 'media_content_id': 'spotify://32oesphrnacjcf7vw5bf6odx3/spotify:playlist:4WkWJ0EjHEFASDevhM8oPw',
@ -157,6 +172,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.PLAYLIST: 'playlist'>, 'media_class': <MediaClass.PLAYLIST: 'playlist'>,
'media_content_id': 'spotify://32oesphrnacjcf7vw5bf6odx3/spotify:playlist:1RHirWgH1weMsBLi4KOK9d', 'media_content_id': 'spotify://32oesphrnacjcf7vw5bf6odx3/spotify:playlist:1RHirWgH1weMsBLi4KOK9d',
@ -178,10 +194,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.APP: 'app'>, 'media_class': <MediaClass.APP: 'app'>,
'media_content_id': 'spotify://01J5TX5A0FF6G5V0QJX6HBC94T', 'media_content_id': 'spotify://01J5TX5A0FF6G5V0QJX6HBC94T',
@ -192,6 +210,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.APP: 'app'>, 'media_class': <MediaClass.APP: 'app'>,
'media_content_id': 'spotify://32oesphrnacjcf7vw5bf6odx3', 'media_content_id': 'spotify://32oesphrnacjcf7vw5bf6odx3',
@ -213,10 +232,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:6akJGriy4njdP8fZTPGjwz', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:6akJGriy4njdP8fZTPGjwz',
@ -227,6 +248,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:7N02bJK1amhplZ8yAapRS5', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:7N02bJK1amhplZ8yAapRS5',
@ -248,10 +270,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.ALBUM: 'album'>, 'media_class': <MediaClass.ALBUM: 'album'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:56jg3KJcYmfL7RzYmG2O1Q', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:56jg3KJcYmfL7RzYmG2O1Q',
@ -262,6 +286,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.ALBUM: 'album'>, 'media_class': <MediaClass.ALBUM: 'album'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:1l86t4bTNT2j1X0ZBCIv6R', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:1l86t4bTNT2j1X0ZBCIv6R',
@ -283,10 +308,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.ALBUM: 'album'>, 'children_media_class': <MediaClass.ALBUM: 'album'>,
'media_class': <MediaClass.ARTIST: 'artist'>, 'media_class': <MediaClass.ARTIST: 'artist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:0lLY20XpZ9yDobkbHI7u1y', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:0lLY20XpZ9yDobkbHI7u1y',
@ -297,6 +324,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.ALBUM: 'album'>, 'children_media_class': <MediaClass.ALBUM: 'album'>,
'media_class': <MediaClass.ARTIST: 'artist'>, 'media_class': <MediaClass.ARTIST: 'artist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:0p4nmQO2msCgU4IF37Wi3j', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:0p4nmQO2msCgU4IF37Wi3j',
@ -318,10 +346,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.PLAYLIST: 'playlist'>, 'media_class': <MediaClass.PLAYLIST: 'playlist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:4WkWJ0EjHEFASDevhM8oPw', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:4WkWJ0EjHEFASDevhM8oPw',
@ -332,6 +362,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.PLAYLIST: 'playlist'>, 'media_class': <MediaClass.PLAYLIST: 'playlist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:1RHirWgH1weMsBLi4KOK9d', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:playlist:1RHirWgH1weMsBLi4KOK9d',
@ -353,10 +384,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:71dMjqJ8UJV700zYs5YZCh', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:71dMjqJ8UJV700zYs5YZCh',
@ -367,6 +400,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:71dMjqJ8UJV700zYs5YZCh', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:71dMjqJ8UJV700zYs5YZCh',
@ -388,10 +422,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.ALBUM: 'album'>, 'media_class': <MediaClass.ALBUM: 'album'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:57MSBg5pBQZH5bfLVDmeuP', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:57MSBg5pBQZH5bfLVDmeuP',
@ -402,6 +438,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.ALBUM: 'album'>, 'media_class': <MediaClass.ALBUM: 'album'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:3DQueEd1Ft9PHWgovDzPKh', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:3DQueEd1Ft9PHWgovDzPKh',
@ -423,10 +460,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.EPISODE: 'episode'>, 'children_media_class': <MediaClass.EPISODE: 'episode'>,
'media_class': <MediaClass.PODCAST: 'podcast'>, 'media_class': <MediaClass.PODCAST: 'podcast'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:show:5OzkclFjD6iAjtAuo7aIYt', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:show:5OzkclFjD6iAjtAuo7aIYt',
@ -437,6 +476,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.EPISODE: 'episode'>, 'children_media_class': <MediaClass.EPISODE: 'episode'>,
'media_class': <MediaClass.PODCAST: 'podcast'>, 'media_class': <MediaClass.PODCAST: 'podcast'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:show:6XYRres0KZtnTqKcLavWR2', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:show:6XYRres0KZtnTqKcLavWR2',
@ -458,10 +498,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:2pj2A25YQK4uMxhZheNx7R', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:2pj2A25YQK4uMxhZheNx7R',
@ -472,6 +514,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:2lKOI1nwP5qZtZC7TGQVY8', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:2lKOI1nwP5qZtZC7TGQVY8',
@ -493,10 +536,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.ALBUM: 'album'>, 'children_media_class': <MediaClass.ALBUM: 'album'>,
'media_class': <MediaClass.ARTIST: 'artist'>, 'media_class': <MediaClass.ARTIST: 'artist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:74Yus6IHfa3tWZzXXAYtS2', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:74Yus6IHfa3tWZzXXAYtS2',
@ -507,6 +552,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.ALBUM: 'album'>, 'children_media_class': <MediaClass.ALBUM: 'album'>,
'media_class': <MediaClass.ARTIST: 'artist'>, 'media_class': <MediaClass.ARTIST: 'artist'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:6s5ubAp65wXoTZefE01RNR', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:artist:6s5ubAp65wXoTZefE01RNR',
@ -528,10 +574,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:3oRoMXsP2NRzm51lldj1RO', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:3oRoMXsP2NRzm51lldj1RO',
@ -542,6 +590,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:69zgu5rlAie3IPZOEXLxyS', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:69zgu5rlAie3IPZOEXLxyS',
@ -563,10 +612,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.ALBUM: 'album'>, 'media_class': <MediaClass.ALBUM: 'album'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:5SGtrmYbIo0Dsg4kJ4qjM6', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:5SGtrmYbIo0Dsg4kJ4qjM6',
@ -577,6 +628,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': <MediaClass.TRACK: 'track'>, 'children_media_class': <MediaClass.TRACK: 'track'>,
'media_class': <MediaClass.ALBUM: 'album'>, 'media_class': <MediaClass.ALBUM: 'album'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:713lZ7AF55fEFSQgcttj9y', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:album:713lZ7AF55fEFSQgcttj9y',
@ -598,10 +650,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:4rzfv0JLZfVhOhbSQ8o5jZ', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:4rzfv0JLZfVhOhbSQ8o5jZ',
@ -612,6 +666,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:5o3jMYOSbaVz3tkgwhELSV', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:5o3jMYOSbaVz3tkgwhELSV',
@ -622,6 +677,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:4Cy0NHJ8Gh0xMdwyM9RkQm', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:4Cy0NHJ8Gh0xMdwyM9RkQm',
@ -632,6 +688,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:6hvFrZNocdt2FcKGCSY5NI', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:6hvFrZNocdt2FcKGCSY5NI',
@ -642,6 +699,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.TRACK: 'track'>, 'media_class': <MediaClass.TRACK: 'track'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:2E2znCPaS8anQe21GLxcvJ', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:track:2E2znCPaS8anQe21GLxcvJ',
@ -652,6 +710,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.EPISODE: 'episode'>, 'media_class': <MediaClass.EPISODE: 'episode'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:episode:3o0RYoo5iOMKSmEbunsbvW', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:episode:3o0RYoo5iOMKSmEbunsbvW',
@ -673,10 +732,12 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': True, 'can_play': True,
'can_search': False,
'children': list([ 'children': list([
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.EPISODE: 'episode'>, 'media_class': <MediaClass.EPISODE: 'episode'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:episode:3ssmxnilHYaKhwRWoBGMbU', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:episode:3ssmxnilHYaKhwRWoBGMbU',
@ -687,6 +748,7 @@
dict({ dict({
'can_expand': False, 'can_expand': False,
'can_play': True, 'can_play': True,
'can_search': False,
'children_media_class': None, 'children_media_class': None,
'media_class': <MediaClass.EPISODE: 'episode'>, 'media_class': <MediaClass.EPISODE: 'episode'>,
'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:episode:1bbj9aqeeZ3UMUlcWN0S03', 'media_content_id': 'spotify://01j5tx5a0ff6g5v0qjx6hbc94t/spotify:episode:1bbj9aqeeZ3UMUlcWN0S03',

View File

@ -3,6 +3,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.DIRECTORY: 'directory'>, 'children_media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_type': '', 'media_content_type': '',
@ -15,6 +16,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.DIRECTORY: 'directory'>, 'children_media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_type': '', 'media_content_type': '',
@ -39,6 +41,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.DIRECTORY: 'directory'>, 'children_media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_type': '', 'media_content_type': '',
@ -51,6 +54,7 @@
dict({ dict({
'can_expand': True, 'can_expand': True,
'can_play': False, 'can_play': False,
'can_search': False,
'children_media_class': <MediaClass.DIRECTORY: 'directory'>, 'children_media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_class': <MediaClass.DIRECTORY: 'directory'>, 'media_class': <MediaClass.DIRECTORY: 'directory'>,
'media_content_type': '', 'media_content_type': '',