Sort DMS results using only criteria supported by the device (#67475)

This commit is contained in:
Michael Chisholm 2022-03-03 02:54:47 +11:00 committed by GitHub
parent c5dd5e18c0
commit 797a9c3de5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 2 deletions

View File

@ -547,7 +547,7 @@ class DmsDeviceSource:
children = await self._device.async_browse_direct_children(
object_id,
metadata_filter=DLNA_BROWSE_FILTER,
sort_criteria=DLNA_SORT_CRITERIA,
sort_criteria=self._sort_criteria,
)
return self._didl_to_media_source(base_object, children)
@ -680,6 +680,27 @@ class DmsDeviceSource:
"""Make an identifier for BrowseMediaSource."""
return f"{self.source_id}/{action}{object_id}"
@property # type: ignore
@functools.cache
def _sort_criteria(self) -> list[str]:
"""Return criteria to be used for sorting results.
The device must be connected before reading this property.
"""
assert self._device
if self._device.sort_capabilities == ["*"]:
return DLNA_SORT_CRITERIA
# Filter criteria based on what the device supports. Strings in
# DLNA_SORT_CRITERIA are prefixed with a sign, while those in
# the device's sort_capabilities are not.
return [
criterion
for criterion in DLNA_SORT_CRITERIA
if criterion[1:] in self._device.sort_capabilities
]
class Action(StrEnum):
"""Actions that can be specified in a DMS media-source identifier."""

View File

@ -8,7 +8,7 @@ from async_upnp_client.profiles.dlna import ContentDirectoryErrorCode, DmsDevice
from didl_lite import didl_lite
import pytest
from homeassistant.components.dlna_dms.const import DOMAIN
from homeassistant.components.dlna_dms.const import DLNA_SORT_CRITERIA, DOMAIN
from homeassistant.components.dlna_dms.dms import (
ActionError,
DeviceConnectionError,
@ -686,6 +686,81 @@ async def test_browse_media_object(
assert not child.children
async def test_browse_object_sort_anything(
device_source_mock: DmsDeviceSource, dms_device_mock: Mock
) -> None:
"""Test sort criteria for children where device allows anything."""
dms_device_mock.sort_capabilities = ["*"]
object_id = "0"
dms_device_mock.async_browse_metadata.return_value = didl_lite.Container(
id="0", restricted="false", title="root"
)
dms_device_mock.async_browse_direct_children.return_value = DmsDevice.BrowseResult(
[], 0, 0, 0
)
await device_source_mock.async_browse_object("0")
# Sort criteria should be dlna_dms's default
dms_device_mock.async_browse_direct_children.assert_awaited_once_with(
object_id, metadata_filter=ANY, sort_criteria=DLNA_SORT_CRITERIA
)
async def test_browse_object_sort_superset(
device_source_mock: DmsDeviceSource, dms_device_mock: Mock
) -> None:
"""Test sorting where device allows superset of integration's criteria."""
dms_device_mock.sort_capabilities = [
"dc:title",
"upnp:originalTrackNumber",
"upnp:class",
"upnp:artist",
"dc:creator",
"upnp:genre",
]
object_id = "0"
dms_device_mock.async_browse_metadata.return_value = didl_lite.Container(
id="0", restricted="false", title="root"
)
dms_device_mock.async_browse_direct_children.return_value = DmsDevice.BrowseResult(
[], 0, 0, 0
)
await device_source_mock.async_browse_object("0")
# Sort criteria should be dlna_dms's default
dms_device_mock.async_browse_direct_children.assert_awaited_once_with(
object_id, metadata_filter=ANY, sort_criteria=DLNA_SORT_CRITERIA
)
async def test_browse_object_sort_subset(
device_source_mock: DmsDeviceSource, dms_device_mock: Mock
) -> None:
"""Test sorting where device allows subset of integration's criteria."""
dms_device_mock.sort_capabilities = [
"dc:title",
"upnp:class",
]
object_id = "0"
dms_device_mock.async_browse_metadata.return_value = didl_lite.Container(
id="0", restricted="false", title="root"
)
dms_device_mock.async_browse_direct_children.return_value = DmsDevice.BrowseResult(
[], 0, 0, 0
)
await device_source_mock.async_browse_object("0")
# Sort criteria should be reduced to only those allowed,
# and in the order specified by DLNA_SORT_CRITERIA
expected_criteria = ["+upnp:class", "+dc:title"]
dms_device_mock.async_browse_direct_children.assert_awaited_once_with(
object_id, metadata_filter=ANY, sort_criteria=expected_criteria
)
async def test_browse_media_path(
device_source_mock: DmsDeviceSource, dms_device_mock: Mock
) -> None: