mirror of
https://github.com/home-assistant/core.git
synced 2026-10-07 06:50:41 -04:00
Map Jellyfin search filter classes to item types (#183252)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
2125b81943
commit
ae965b48b6
@@ -20,6 +20,7 @@ from .const import (
|
||||
CONTENT_TYPE_MAP,
|
||||
MEDIA_CLASS_MAP,
|
||||
MEDIA_TYPE_NONE,
|
||||
SEARCH_ITEM_TYPE_MAP,
|
||||
SUPPORTED_COLLECTION_TYPES,
|
||||
)
|
||||
|
||||
@@ -165,9 +166,18 @@ async def search_items(
|
||||
|
||||
items: list[dict[str, Any]] = []
|
||||
# Search for items based on media filter classes (or all if none specified)
|
||||
media_types: list[MediaClass] | list[None] = []
|
||||
media_types: list[str] | list[None] = []
|
||||
if query.media_filter_classes:
|
||||
media_types = query.media_filter_classes
|
||||
# Jellyfin ignores unknown item types and returns unfiltered results,
|
||||
# so skip classes that have no Jellyfin item type. Classes can share an
|
||||
# item type, so search each item type once.
|
||||
media_types = list(
|
||||
dict.fromkeys(
|
||||
",".join(SEARCH_ITEM_TYPE_MAP[media_class])
|
||||
for media_class in query.media_filter_classes
|
||||
if media_class in SEARCH_ITEM_TYPE_MAP
|
||||
)
|
||||
)
|
||||
else:
|
||||
media_types = [None]
|
||||
|
||||
|
||||
@@ -82,7 +82,18 @@ MEDIA_CLASS_MAP = {
|
||||
"BoxSet": MediaClass.DIRECTORY,
|
||||
"Episode": MediaClass.EPISODE,
|
||||
"Season": MediaClass.SEASON,
|
||||
"Playlist": MediaClass.PLAYLIST,
|
||||
"Video": MediaClass.VIDEO,
|
||||
}
|
||||
# The track class has no item type of its own, so it searches Audio items too.
|
||||
SEARCH_ITEM_TYPE_MAP: dict[MediaClass, list[str]] = {
|
||||
media_class: [
|
||||
item_type
|
||||
for item_type, item_class in MEDIA_CLASS_MAP.items()
|
||||
if item_class == media_class
|
||||
]
|
||||
for media_class in set(MEDIA_CLASS_MAP.values())
|
||||
} | {MediaClass.TRACK: [ITEM_TYPE_AUDIO]}
|
||||
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE, Platform.SENSOR]
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.jellyfin.const import DOMAIN
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_ALBUM_ARTIST,
|
||||
@@ -531,6 +533,60 @@ async def test_search_media(
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("media_filter_classes", "expected_item_types"),
|
||||
[
|
||||
pytest.param([], [None], id="no_filter"),
|
||||
pytest.param(["album"], ["MusicAlbum"], id="album"),
|
||||
pytest.param(["artist"], ["MusicArtist"], id="artist"),
|
||||
pytest.param(["track"], ["Audio"], id="track"),
|
||||
pytest.param(["movie"], ["Movie"], id="movie"),
|
||||
pytest.param(["playlist"], ["Playlist"], id="playlist"),
|
||||
pytest.param(["video"], ["Video"], id="video"),
|
||||
pytest.param(
|
||||
["directory"],
|
||||
["CollectionFolder,AggregateFolder,Folder,BoxSet"],
|
||||
id="multiple_item_types",
|
||||
),
|
||||
pytest.param(
|
||||
["album", "tv_show"], ["MusicAlbum", "Series"], id="multiple_classes"
|
||||
),
|
||||
pytest.param(["music", "track"], ["Audio"], id="shared_item_type"),
|
||||
pytest.param(["podcast"], [], id="unmapped_class"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_search_media_item_types(
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
mock_api: MagicMock,
|
||||
media_filter_classes: list[str],
|
||||
expected_item_types: list[str | None],
|
||||
) -> None:
|
||||
"""Test Jellyfin search maps media filter classes to Jellyfin item types."""
|
||||
client = await hass_ws_client()
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "media_player/search_media",
|
||||
"entity_id": "media_player.jellyfin_device",
|
||||
"media_content_id": "",
|
||||
"media_content_type": "",
|
||||
"search_query": "Fake Item 1",
|
||||
"media_filter_classes": media_filter_classes,
|
||||
}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
# The filter classes arrive as a set, so the search order is not fixed.
|
||||
assert sorted(
|
||||
(
|
||||
search_call.kwargs["media"]
|
||||
for search_call in mock_api.search_media_items.call_args_list
|
||||
),
|
||||
key=str,
|
||||
) == sorted(expected_item_types, key=str)
|
||||
|
||||
|
||||
async def test_new_client_connected(
|
||||
hass: HomeAssistant,
|
||||
init_integration: MockConfigEntry,
|
||||
|
||||
Reference in New Issue
Block a user