Allow shared Synology DSM Photo albums shown in media browser (#123613)
This commit is contained in:
parent
874ae15d6a
commit
e39bfeac08
5 changed files with 65 additions and 34 deletions
|
@ -7,7 +7,7 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/synology_dsm",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["synology_dsm"],
|
||||
"requirements": ["py-synologydsm-api==2.4.5"],
|
||||
"requirements": ["py-synologydsm-api==2.5.2"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Synology",
|
||||
|
|
|
@ -46,18 +46,24 @@ class SynologyPhotosMediaSourceIdentifier:
|
|||
self.cache_key = None
|
||||
self.file_name = None
|
||||
self.is_shared = False
|
||||
self.passphrase = ""
|
||||
|
||||
if parts:
|
||||
self.unique_id = parts[0]
|
||||
if len(parts) > 1:
|
||||
self.album_id = parts[1]
|
||||
if len(parts) > 2:
|
||||
self.cache_key = parts[2]
|
||||
if len(parts) > 3:
|
||||
self.file_name = parts[3]
|
||||
if self.file_name.endswith(SHARED_SUFFIX):
|
||||
self.is_shared = True
|
||||
self.file_name = self.file_name.removesuffix(SHARED_SUFFIX)
|
||||
self.unique_id = parts[0]
|
||||
|
||||
if len(parts) > 1:
|
||||
album_parts = parts[1].split("_")
|
||||
self.album_id = album_parts[0]
|
||||
if len(album_parts) > 1:
|
||||
self.passphrase = parts[1].replace(f"{self.album_id}_", "")
|
||||
|
||||
if len(parts) > 2:
|
||||
self.cache_key = parts[2]
|
||||
|
||||
if len(parts) > 3:
|
||||
self.file_name = parts[3]
|
||||
if self.file_name.endswith(SHARED_SUFFIX):
|
||||
self.is_shared = True
|
||||
self.file_name = self.file_name.removesuffix(SHARED_SUFFIX)
|
||||
|
||||
|
||||
class SynologyPhotosMediaSource(MediaSource):
|
||||
|
@ -135,7 +141,7 @@ class SynologyPhotosMediaSource(MediaSource):
|
|||
ret.extend(
|
||||
BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=f"{item.identifier}/{album.album_id}",
|
||||
identifier=f"{item.identifier}/{album.album_id}_{album.passphrase}",
|
||||
media_class=MediaClass.DIRECTORY,
|
||||
media_content_type=MediaClass.IMAGE,
|
||||
title=album.name,
|
||||
|
@ -149,7 +155,7 @@ class SynologyPhotosMediaSource(MediaSource):
|
|||
|
||||
# Request items of album
|
||||
# Get Items
|
||||
album = SynoPhotosAlbum(int(identifier.album_id), "", 0)
|
||||
album = SynoPhotosAlbum(int(identifier.album_id), "", 0, identifier.passphrase)
|
||||
try:
|
||||
album_items = await diskstation.api.photos.get_items_from_album(
|
||||
album, 0, 1000
|
||||
|
@ -170,7 +176,12 @@ class SynologyPhotosMediaSource(MediaSource):
|
|||
ret.append(
|
||||
BrowseMediaSource(
|
||||
domain=DOMAIN,
|
||||
identifier=f"{identifier.unique_id}/{identifier.album_id}/{album_item.thumbnail_cache_key}/{album_item.file_name}{suffix}",
|
||||
identifier=(
|
||||
f"{identifier.unique_id}/"
|
||||
f"{identifier.album_id}_{identifier.passphrase}/"
|
||||
f"{album_item.thumbnail_cache_key}/"
|
||||
f"{album_item.file_name}{suffix}"
|
||||
),
|
||||
media_class=MediaClass.IMAGE,
|
||||
media_content_type=mime_type,
|
||||
title=album_item.file_name,
|
||||
|
@ -197,7 +208,12 @@ class SynologyPhotosMediaSource(MediaSource):
|
|||
if identifier.is_shared:
|
||||
suffix = SHARED_SUFFIX
|
||||
return PlayMedia(
|
||||
f"/synology_dsm/{identifier.unique_id}/{identifier.cache_key}/{identifier.file_name}{suffix}",
|
||||
(
|
||||
f"/synology_dsm/{identifier.unique_id}/"
|
||||
f"{identifier.cache_key}/"
|
||||
f"{identifier.file_name}{suffix}/"
|
||||
f"{identifier.passphrase}"
|
||||
),
|
||||
mime_type,
|
||||
)
|
||||
|
||||
|
@ -231,18 +247,24 @@ class SynologyDsmMediaView(http.HomeAssistantView):
|
|||
if not self.hass.data.get(DOMAIN):
|
||||
raise web.HTTPNotFound
|
||||
# location: {cache_key}/{filename}
|
||||
cache_key, file_name = location.split("/")
|
||||
cache_key, file_name, passphrase = location.split("/")
|
||||
image_id = int(cache_key.split("_")[0])
|
||||
|
||||
if shared := file_name.endswith(SHARED_SUFFIX):
|
||||
file_name = file_name.removesuffix(SHARED_SUFFIX)
|
||||
|
||||
mime_type, _ = mimetypes.guess_type(file_name)
|
||||
if not isinstance(mime_type, str):
|
||||
raise web.HTTPNotFound
|
||||
|
||||
diskstation: SynologyDSMData = self.hass.data[DOMAIN][source_dir_id]
|
||||
assert diskstation.api.photos is not None
|
||||
item = SynoPhotosItem(image_id, "", "", "", cache_key, "", shared)
|
||||
item = SynoPhotosItem(image_id, "", "", "", cache_key, "xl", shared, passphrase)
|
||||
try:
|
||||
image = await diskstation.api.photos.download_item(item)
|
||||
if passphrase:
|
||||
image = await diskstation.api.photos.download_item_thumbnail(item)
|
||||
else:
|
||||
image = await diskstation.api.photos.download_item(item)
|
||||
except SynologyDSMException as exc:
|
||||
raise web.HTTPNotFound from exc
|
||||
return web.Response(body=image, content_type=mime_type)
|
||||
|
|
|
@ -1662,7 +1662,7 @@ py-schluter==0.1.7
|
|||
py-sucks==0.9.10
|
||||
|
||||
# homeassistant.components.synology_dsm
|
||||
py-synologydsm-api==2.4.5
|
||||
py-synologydsm-api==2.5.2
|
||||
|
||||
# homeassistant.components.zabbix
|
||||
py-zabbix==1.1.7
|
||||
|
|
|
@ -1354,7 +1354,7 @@ py-nightscout==1.2.2
|
|||
py-sucks==0.9.10
|
||||
|
||||
# homeassistant.components.synology_dsm
|
||||
py-synologydsm-api==2.4.5
|
||||
py-synologydsm-api==2.5.2
|
||||
|
||||
# homeassistant.components.hdmi_cec
|
||||
pyCEC==0.5.2
|
||||
|
|
|
@ -48,11 +48,15 @@ def dsm_with_photos() -> MagicMock:
|
|||
dsm.surveillance_station.update = AsyncMock(return_value=True)
|
||||
dsm.upgrade.update = AsyncMock(return_value=True)
|
||||
|
||||
dsm.photos.get_albums = AsyncMock(return_value=[SynoPhotosAlbum(1, "Album 1", 10)])
|
||||
dsm.photos.get_albums = AsyncMock(
|
||||
return_value=[SynoPhotosAlbum(1, "Album 1", 10, "")]
|
||||
)
|
||||
dsm.photos.get_items_from_album = AsyncMock(
|
||||
return_value=[
|
||||
SynoPhotosItem(10, "", "filename.jpg", 12345, "10_1298753", "sm", False),
|
||||
SynoPhotosItem(10, "", "filename.jpg", 12345, "10_1298753", "sm", True),
|
||||
SynoPhotosItem(
|
||||
10, "", "filename.jpg", 12345, "10_1298753", "sm", False, ""
|
||||
),
|
||||
SynoPhotosItem(10, "", "filename.jpg", 12345, "10_1298753", "sm", True, ""),
|
||||
]
|
||||
)
|
||||
dsm.photos.get_item_thumbnail_url = AsyncMock(
|
||||
|
@ -96,17 +100,22 @@ async def test_resolve_media_bad_identifier(
|
|||
[
|
||||
(
|
||||
"ABC012345/10/27643_876876/filename.jpg",
|
||||
"/synology_dsm/ABC012345/27643_876876/filename.jpg",
|
||||
"/synology_dsm/ABC012345/27643_876876/filename.jpg/",
|
||||
"image/jpeg",
|
||||
),
|
||||
(
|
||||
"ABC012345/12/12631_47189/filename.png",
|
||||
"/synology_dsm/ABC012345/12631_47189/filename.png",
|
||||
"/synology_dsm/ABC012345/12631_47189/filename.png/",
|
||||
"image/png",
|
||||
),
|
||||
(
|
||||
"ABC012345/12/12631_47189/filename.png_shared",
|
||||
"/synology_dsm/ABC012345/12631_47189/filename.png_shared",
|
||||
"/synology_dsm/ABC012345/12631_47189/filename.png_shared/",
|
||||
"image/png",
|
||||
),
|
||||
(
|
||||
"ABC012345/12_dmypass/12631_47189/filename.png",
|
||||
"/synology_dsm/ABC012345/12631_47189/filename.png/dmypass",
|
||||
"image/png",
|
||||
),
|
||||
],
|
||||
|
@ -250,7 +259,7 @@ async def test_browse_media_get_albums(
|
|||
assert result.children[0].identifier == "mocked_syno_dsm_entry/0"
|
||||
assert result.children[0].title == "All images"
|
||||
assert isinstance(result.children[1], BrowseMedia)
|
||||
assert result.children[1].identifier == "mocked_syno_dsm_entry/1"
|
||||
assert result.children[1].identifier == "mocked_syno_dsm_entry/1_"
|
||||
assert result.children[1].title == "Album 1"
|
||||
|
||||
|
||||
|
@ -382,7 +391,7 @@ async def test_browse_media_get_items(
|
|||
assert len(result.children) == 2
|
||||
item = result.children[0]
|
||||
assert isinstance(item, BrowseMedia)
|
||||
assert item.identifier == "mocked_syno_dsm_entry/1/10_1298753/filename.jpg"
|
||||
assert item.identifier == "mocked_syno_dsm_entry/1_/10_1298753/filename.jpg"
|
||||
assert item.title == "filename.jpg"
|
||||
assert item.media_class == MediaClass.IMAGE
|
||||
assert item.media_content_type == "image/jpeg"
|
||||
|
@ -391,7 +400,7 @@ async def test_browse_media_get_items(
|
|||
assert item.thumbnail == "http://my.thumbnail.url"
|
||||
item = result.children[1]
|
||||
assert isinstance(item, BrowseMedia)
|
||||
assert item.identifier == "mocked_syno_dsm_entry/1/10_1298753/filename.jpg_shared"
|
||||
assert item.identifier == "mocked_syno_dsm_entry/1_/10_1298753/filename.jpg_shared"
|
||||
assert item.title == "filename.jpg"
|
||||
assert item.media_class == MediaClass.IMAGE
|
||||
assert item.media_content_type == "image/jpeg"
|
||||
|
@ -435,24 +444,24 @@ async def test_media_view(
|
|||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
||||
with pytest.raises(web.HTTPNotFound):
|
||||
await view.get(request, "", "10_1298753/filename")
|
||||
await view.get(request, "", "10_1298753/filename/")
|
||||
|
||||
# exception in download_item()
|
||||
dsm_with_photos.photos.download_item = AsyncMock(
|
||||
side_effect=SynologyDSMException("", None)
|
||||
)
|
||||
with pytest.raises(web.HTTPNotFound):
|
||||
await view.get(request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg")
|
||||
await view.get(request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg/")
|
||||
|
||||
# success
|
||||
dsm_with_photos.photos.download_item = AsyncMock(return_value=b"xxxx")
|
||||
with patch.object(tempfile, "tempdir", tmp_path):
|
||||
result = await view.get(
|
||||
request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg"
|
||||
request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg/"
|
||||
)
|
||||
assert isinstance(result, web.Response)
|
||||
with patch.object(tempfile, "tempdir", tmp_path):
|
||||
result = await view.get(
|
||||
request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg_shared"
|
||||
request, "mocked_syno_dsm_entry", "10_1298753/filename.jpg_shared/"
|
||||
)
|
||||
assert isinstance(result, web.Response)
|
||||
|
|
Loading…
Add table
Reference in a new issue