mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Set correct BrowseMedia title in immich (#178388)
This commit is contained in:
@@ -140,33 +140,33 @@ class ImmichMediaSource(MediaSource):
|
||||
if item.identifier:
|
||||
can_search = bool(ImmichMediaSourceIdentifier(item.identifier).unique_id)
|
||||
|
||||
title, children = await self._async_build_immich(item, entries)
|
||||
|
||||
return BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=item.identifier,
|
||||
media_class=MediaClass.DIRECTORY,
|
||||
media_content_type=MediaClass.IMAGE,
|
||||
title="Immich",
|
||||
title=title,
|
||||
can_play=False,
|
||||
can_expand=True,
|
||||
can_search=can_search,
|
||||
search_media_classes=[MediaClass.IMAGE, MediaClass.VIDEO],
|
||||
children_media_class=MediaClass.DIRECTORY,
|
||||
children=[
|
||||
*await self._async_build_immich(item, entries),
|
||||
],
|
||||
children=children,
|
||||
)
|
||||
|
||||
async def _async_build_immich(
|
||||
self, item: MediaSourceItem, entries: list[ConfigEntry]
|
||||
) -> list[BrowseMediaSource]:
|
||||
"""Handle browsing different immich instances."""
|
||||
) -> tuple[str, list[BrowseMediaSource]]:
|
||||
"""Return the title and the children of the browsed item."""
|
||||
|
||||
# --------------------------------------------------------
|
||||
# root level, render immich instances
|
||||
# --------------------------------------------------------
|
||||
if not item.identifier:
|
||||
LOGGER.debug("Render all Immich instances")
|
||||
return [
|
||||
return "Immich", [
|
||||
BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=entry.unique_id,
|
||||
@@ -193,7 +193,7 @@ class ImmichMediaSource(MediaSource):
|
||||
|
||||
if identifier.collection is None:
|
||||
LOGGER.debug("Render all collections for %s", entry.title)
|
||||
return [
|
||||
return entry.title, [
|
||||
BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=f"{identifier.unique_id}|{collection}",
|
||||
@@ -221,9 +221,9 @@ class ImmichMediaSource(MediaSource):
|
||||
translation_placeholders={"msg": str(err)},
|
||||
) from err
|
||||
except ImmichError:
|
||||
return []
|
||||
return identifier.collection, []
|
||||
|
||||
return [
|
||||
return identifier.collection, [
|
||||
BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=f"{identifier.unique_id}|albums|{album.album_id}",
|
||||
@@ -248,9 +248,9 @@ class ImmichMediaSource(MediaSource):
|
||||
translation_placeholders={"msg": str(err)},
|
||||
) from err
|
||||
except ImmichError:
|
||||
return []
|
||||
return identifier.collection, []
|
||||
|
||||
return [
|
||||
return identifier.collection, [
|
||||
BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=f"{identifier.unique_id}|tags|{tag.tag_id}",
|
||||
@@ -274,9 +274,9 @@ class ImmichMediaSource(MediaSource):
|
||||
translation_placeholders={"msg": str(err)},
|
||||
) from err
|
||||
except ImmichError:
|
||||
return []
|
||||
return identifier.collection, []
|
||||
|
||||
return [
|
||||
return identifier.collection, [
|
||||
BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=f"{identifier.unique_id}|people|{person.person_id}",
|
||||
@@ -295,6 +295,7 @@ class ImmichMediaSource(MediaSource):
|
||||
# --------------------------------------------------------
|
||||
assert identifier.collection_id is not None
|
||||
assets: list[ImmichAsset] = []
|
||||
title = identifier.collection
|
||||
if identifier.collection == "albums":
|
||||
LOGGER.debug(
|
||||
"Render all assets of album %s for %s",
|
||||
@@ -302,6 +303,9 @@ class ImmichMediaSource(MediaSource):
|
||||
entry.title,
|
||||
)
|
||||
try:
|
||||
album = await immich_api.albums.async_get_album_info(
|
||||
identifier.collection_id
|
||||
)
|
||||
assets = await immich_api.search.async_get_all_by_album_ids(
|
||||
[identifier.collection_id]
|
||||
)
|
||||
@@ -312,7 +316,9 @@ class ImmichMediaSource(MediaSource):
|
||||
translation_placeholders={"msg": str(err)},
|
||||
) from err
|
||||
except ImmichError:
|
||||
return []
|
||||
return title, []
|
||||
|
||||
title = album.album_name
|
||||
|
||||
elif identifier.collection == "tags":
|
||||
LOGGER.debug(
|
||||
@@ -320,6 +326,9 @@ class ImmichMediaSource(MediaSource):
|
||||
identifier.collection_id,
|
||||
)
|
||||
try:
|
||||
tag = await immich_api.tags.async_get_tag_by_id(
|
||||
identifier.collection_id
|
||||
)
|
||||
assets = await immich_api.search.async_get_all_by_tag_ids(
|
||||
[identifier.collection_id]
|
||||
)
|
||||
@@ -330,7 +339,9 @@ class ImmichMediaSource(MediaSource):
|
||||
translation_placeholders={"msg": str(err)},
|
||||
) from err
|
||||
except ImmichError:
|
||||
return []
|
||||
return title, []
|
||||
|
||||
title = tag.name
|
||||
|
||||
elif identifier.collection == "people":
|
||||
LOGGER.debug(
|
||||
@@ -338,6 +349,9 @@ class ImmichMediaSource(MediaSource):
|
||||
identifier.collection_id,
|
||||
)
|
||||
try:
|
||||
person = await immich_api.people.async_get_person_by_id(
|
||||
identifier.collection_id
|
||||
)
|
||||
assets = await immich_api.search.async_get_all_by_person_ids(
|
||||
[identifier.collection_id]
|
||||
)
|
||||
@@ -348,7 +362,10 @@ class ImmichMediaSource(MediaSource):
|
||||
translation_placeholders={"msg": str(err)},
|
||||
) from err
|
||||
except ImmichError:
|
||||
return []
|
||||
return title, []
|
||||
|
||||
title = person.name
|
||||
|
||||
elif identifier.collection == "favorites":
|
||||
LOGGER.debug("Render all assets for favorites collection")
|
||||
try:
|
||||
@@ -360,9 +377,9 @@ class ImmichMediaSource(MediaSource):
|
||||
translation_placeholders={"msg": str(err)},
|
||||
) from err
|
||||
except ImmichError:
|
||||
return []
|
||||
return title, []
|
||||
|
||||
return _parse_assets(assets, identifier)
|
||||
return title, _parse_assets(assets, identifier)
|
||||
|
||||
@override
|
||||
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
|
||||
|
||||
@@ -82,6 +82,7 @@ def mock_immich_albums() -> AsyncMock:
|
||||
"""Mock the Immich server."""
|
||||
mock = AsyncMock(spec=ImmichAlbums)
|
||||
mock.async_get_all_albums.return_value = [ALBUM_DATA]
|
||||
mock.async_get_album_info.return_value = ALBUM_DATA
|
||||
mock.async_add_assets_to_album.return_value = [
|
||||
ImmichAddAssetsToAlbumResponse.from_dict(
|
||||
{"id": "abcdef-0123456789", "success": True}
|
||||
@@ -151,6 +152,7 @@ def mock_immich_people() -> AsyncMock:
|
||||
}
|
||||
),
|
||||
]
|
||||
mock.async_get_person_by_id.return_value = mock.async_get_all_people.return_value[0]
|
||||
mock.async_get_person_thumbnail.return_value = b"yyyy"
|
||||
return mock
|
||||
|
||||
@@ -257,6 +259,7 @@ def mock_immich_tags() -> AsyncMock:
|
||||
},
|
||||
),
|
||||
]
|
||||
mock.async_get_tag_by_id.return_value = mock.async_get_all_tags.return_value[0]
|
||||
return mock
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from unittest.mock import Mock, patch
|
||||
|
||||
from aiohttp import web
|
||||
from aioimmich.assets.models import AssetType
|
||||
from aioimmich.exceptions import ImmichError, ImmichForbiddenError
|
||||
from aioimmich.exceptions import ImmichError, ImmichForbiddenError, ImmichNotFoundError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.immich.const import DOMAIN
|
||||
@@ -130,6 +130,7 @@ async def test_browse_media_get_root(
|
||||
root_media_source = await source.async_browse_media(item)
|
||||
|
||||
assert root_media_source
|
||||
assert root_media_source.title == "Immich"
|
||||
assert root_media_source.can_search is False
|
||||
assert len(root_media_source.children) == 1
|
||||
media_file = root_media_source.children[0]
|
||||
@@ -144,6 +145,7 @@ async def test_browse_media_get_root(
|
||||
root_media_source = await source.async_browse_media(item)
|
||||
|
||||
assert root_media_source
|
||||
assert root_media_source.title == "Someone"
|
||||
assert root_media_source.can_search is True
|
||||
assert len(root_media_source.children) == 4
|
||||
|
||||
@@ -226,6 +228,7 @@ async def test_browse_media_collections(
|
||||
root_media_source = await source.async_browse_media(item)
|
||||
|
||||
assert root_media_source
|
||||
assert root_media_source.title == collection
|
||||
assert root_media_source.can_search is True
|
||||
assert len(root_media_source.children) == len(children)
|
||||
for idx, child in enumerate(children):
|
||||
@@ -363,11 +366,12 @@ async def test_browse_media_collection_items_error(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("collection", "collection_id", "children"),
|
||||
("collection", "collection_id", "title", "children"),
|
||||
[
|
||||
(
|
||||
"albums",
|
||||
"721e1a4b-aa12-441e-8d3b-5ac7ab283bb6",
|
||||
"My Album",
|
||||
[
|
||||
{
|
||||
"original_file_name": "filename.jpg",
|
||||
@@ -388,6 +392,7 @@ async def test_browse_media_collection_items_error(
|
||||
],
|
||||
),
|
||||
(
|
||||
"favorites",
|
||||
"favorites",
|
||||
"favorites",
|
||||
[
|
||||
@@ -412,6 +417,7 @@ async def test_browse_media_collection_items_error(
|
||||
(
|
||||
"people",
|
||||
"6176838a-ac5a-4d1f-9a35-91c591d962d8",
|
||||
"Me",
|
||||
[
|
||||
{
|
||||
"original_file_name": "20250714_201122.jpg",
|
||||
@@ -433,7 +439,8 @@ async def test_browse_media_collection_items_error(
|
||||
),
|
||||
(
|
||||
"tags",
|
||||
"6176838a-ac5a-4d1f-9a35-91c591d962d8",
|
||||
"67301cb8-cb73-4e8a-99e9-475cb3f7e7b5",
|
||||
"Halloween",
|
||||
[
|
||||
{
|
||||
"original_file_name": "20110306_025024.jpg",
|
||||
@@ -461,6 +468,7 @@ async def test_browse_media_collection_get_items(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
collection: str,
|
||||
collection_id: str,
|
||||
title: str,
|
||||
children: list[dict],
|
||||
) -> None:
|
||||
"""Test browse_media returning albums."""
|
||||
@@ -480,6 +488,7 @@ async def test_browse_media_collection_get_items(
|
||||
root_media_source = await source.async_browse_media(item)
|
||||
|
||||
assert root_media_source
|
||||
assert root_media_source.title == title
|
||||
assert len(root_media_source.children) == len(children)
|
||||
|
||||
for idx, child in enumerate(children):
|
||||
@@ -500,6 +509,52 @@ async def test_browse_media_collection_get_items(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("collection", "mocked_get_fn"),
|
||||
[
|
||||
pytest.param("albums", ("albums", "async_get_album_info"), id="albums"),
|
||||
pytest.param("people", ("people", "async_get_person_by_id"), id="people"),
|
||||
pytest.param("tags", ("tags", "async_get_tag_by_id"), id="tags"),
|
||||
],
|
||||
)
|
||||
async def test_browse_media_title_of_unknown_collection_item(
|
||||
hass: HomeAssistant,
|
||||
mock_immich: Mock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
collection: str,
|
||||
mocked_get_fn: tuple[str, str],
|
||||
) -> None:
|
||||
"""Test browse_media falls back to the collection name for unknown items."""
|
||||
assert await async_setup_component(hass, "media_source", {})
|
||||
|
||||
with patch("homeassistant.components.immich.PLATFORMS", []):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
getattr(
|
||||
getattr(mock_immich, mocked_get_fn[0]), mocked_get_fn[1]
|
||||
).side_effect = ImmichNotFoundError(
|
||||
{
|
||||
"message": "Not found or no permission",
|
||||
"error": "Bad Request",
|
||||
"statusCode": 400,
|
||||
"correlationId": "e0hlizyl",
|
||||
}
|
||||
)
|
||||
|
||||
source = await async_get_media_source(hass)
|
||||
|
||||
item = MediaSourceItem(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"{mock_config_entry.unique_id}|{collection}|unknown-id",
|
||||
None,
|
||||
)
|
||||
root_media_source = await source.async_browse_media(item)
|
||||
|
||||
assert root_media_source.title == collection
|
||||
assert len(root_media_source.children) == 0
|
||||
|
||||
|
||||
async def test_media_view(
|
||||
hass: HomeAssistant,
|
||||
tmp_path: Path,
|
||||
|
||||
Reference in New Issue
Block a user