Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix downloading shared photos from shared album #344

Merged
merged 8 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ max-line-length = 80
max-complexity = 10
docstring-convention = google
per-file-ignores =
tests/*:S101
tests/*:S101,S105
tests/**/const_*.py:B950
src/synology_dsm/const.py:B950
83 changes: 57 additions & 26 deletions src/synology_dsm/api/photos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,26 @@ async def get_albums(
"""Get a list of all albums."""
albums: list[SynoPhotosAlbum] = []
raw_data = await self._dsm.get(
self.BROWSE_ALBUMS_API_KEY, "list", {"offset": offset, "limit": limit}
self.BROWSE_ALBUMS_API_KEY,
"list",
{"offset": offset, "limit": limit, "category": "normal_share_with_me"},
mib1185 marked this conversation as resolved.
Show resolved Hide resolved
)
if not isinstance(raw_data, dict) or (data := raw_data.get("data")) is None:
return None

alexismarquis marked this conversation as resolved.
Show resolved Hide resolved
for album in data["list"]:
albums.append(
SynoPhotosAlbum(album["id"], album["name"], album["item_count"])
SynoPhotosAlbum(
album["id"],
album["name"],
album["item_count"],
album["passphrase"] if album["passphrase"] else None,
)
)
return albums

def _raw_data_to_items(
self, raw_data: bytes | dict | str
self, raw_data: bytes | dict | str, passphrase: str | None = None
) -> list[SynoPhotosItem] | None:
"""Parse the raw data response to a list of photo items."""
items: list[SynoPhotosItem] = []
Expand All @@ -62,6 +69,7 @@ def _raw_data_to_items(
item["additional"]["thumbnail"]["cache_key"],
size,
item["owner_user_id"] == 0,
passphrase,
)
)
return items
Expand All @@ -70,17 +78,22 @@ async def get_items_from_album(
self, album: SynoPhotosAlbum, offset: int = 0, limit: int = 100
) -> list[SynoPhotosItem] | None:
"""Get a list of all items from given album."""
params = {
"offset": offset,
"limit": limit,
"additional": '["thumbnail"]',
}
if album.passphrase:
params["passphrase"] = album.passphrase
else:
params["album_id"] = album.album_id
mib1185 marked this conversation as resolved.
Show resolved Hide resolved

raw_data = await self._dsm.get(
self.BROWSE_ITEM_API_KEY,
"list",
{
"album_id": album.album_id,
"offset": offset,
"limit": limit,
"additional": '["thumbnail"]',
},
params,
)
return self._raw_data_to_items(raw_data)
return self._raw_data_to_items(raw_data, album.passphrase)

async def get_items_from_shared_space(
self, offset: int = 0, limit: int = 100
Expand Down Expand Up @@ -118,13 +131,19 @@ async def download_item(self, item: SynoPhotosItem) -> bytes | None:
download_api = self.DOWNLOAD_API_KEY
if item.is_shared:
download_api = self.DOWNLOAD_FOTOTEAM_API_KEY

params = {
"unit_id": f"[{item.item_id}]",
"cache_key": item.thumbnail_cache_key,
}

if item.passphrase:
params["passphrase"] = item.passphrase

raw_data = await self._dsm.get(
download_api,
"download",
{
"unit_id": f"[{item.item_id}]",
"cache_key": item.thumbnail_cache_key,
},
params,
)
if isinstance(raw_data, bytes):
return raw_data
Expand All @@ -135,15 +154,21 @@ async def download_item_thumbnail(self, item: SynoPhotosItem) -> bytes | None:
download_api = self.THUMBNAIL_API_KEY
if item.is_shared:
download_api = self.THUMBNAIL_FOTOTEAM_API_KEY

params = {
"id": item.item_id,
"cache_key": item.thumbnail_cache_key,
"size": item.thumbnail_size,
"type": "unit",
}

if item.passphrase:
params["passphrase"] = item.passphrase

raw_data = await self._dsm.get(
download_api,
"get",
{
"id": item.item_id,
"cache_key": item.thumbnail_cache_key,
"size": item.thumbnail_size,
"type": "unit",
},
params,
)
if isinstance(raw_data, bytes):
return raw_data
Expand All @@ -154,13 +179,19 @@ async def get_item_thumbnail_url(self, item: SynoPhotosItem) -> str:
download_api = self.THUMBNAIL_API_KEY
if item.is_shared:
download_api = self.THUMBNAIL_FOTOTEAM_API_KEY

params = {
"id": item.item_id,
"cache_key": item.thumbnail_cache_key,
"size": item.thumbnail_size,
"type": "unit",
}

if item.passphrase:
params["passphrase"] = item.passphrase

return await self._dsm.generate_url(
download_api,
"get",
{
"id": item.item_id,
"cache_key": item.thumbnail_cache_key,
"size": item.thumbnail_size,
"type": "unit",
},
params,
)
4 changes: 4 additions & 0 deletions src/synology_dsm/api/photos/model.py
alexismarquis marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Data models for Synology Photos Module."""

from __future__ import annotations

from dataclasses import dataclass


Expand All @@ -10,6 +12,7 @@ class SynoPhotosAlbum:
album_id: int
name: str
item_count: int
passphrase: str | None


@dataclass
Expand All @@ -23,3 +26,4 @@ class SynoPhotosItem:
thumbnail_cache_key: str
thumbnail_size: str
is_shared: bool
passphrase: str | None
5 changes: 4 additions & 1 deletion src/synology_dsm/synology_dsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,10 @@ async def _execute_request(
]:
return dict(await response.json(content_type=content_type))

if content_type.startswith("image"):
if (
content_type == "application/octet-stream"
or content_type.startswith("image")
):
return await response.read()

return await response.text()
Expand Down
6 changes: 5 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
DSM_7_FOTO_ALBUMS,
DSM_7_FOTO_ITEMS,
DSM_7_FOTO_ITEMS_SEARCHED,
DSM_7_FOTO_ITEMS_SHARED_ALBUM,
DSM_7_FOTO_SHARED_ITEMS,
)
from .const import (
Expand Down Expand Up @@ -285,7 +286,10 @@ async def _execute_request(self, method, url, params, **kwargs):
return DSM_7_FOTO_ALBUMS

if SynoPhotos.BROWSE_ITEM_API_KEY in url:
return DSM_7_FOTO_ITEMS
if "passphrase" in url:
return DSM_7_FOTO_ITEMS_SHARED_ALBUM
else:
return DSM_7_FOTO_ITEMS

if SynoPhotos.SEARCH_API_KEY in url:
return DSM_7_FOTO_ITEMS_SEARCHED
Expand Down
4 changes: 3 additions & 1 deletion tests/api_data/dsm_7/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""DSM 6 datas."""
"""DSM 7 datas."""

from .const_7_api_auth import (
DSM_7_AUTH_LOGIN,
Expand All @@ -15,6 +15,7 @@
DSM_7_FOTO_ALBUMS,
DSM_7_FOTO_ITEMS,
DSM_7_FOTO_ITEMS_SEARCHED,
DSM_7_FOTO_ITEMS_SHARED_ALBUM,
DSM_7_FOTO_SHARED_ITEMS,
)

Expand All @@ -29,6 +30,7 @@
"DSM_7_DSM_INFORMATION",
"DSM_7_FOTO_ALBUMS",
"DSM_7_FOTO_ITEMS",
"DSM_7_FOTO_ITEMS_SHARED_ALBUM",
"DSM_7_FOTO_ITEMS_SEARCHED",
"DSM_7_FOTO_SHARED_ITEMS",
]
2 changes: 1 addition & 1 deletion tests/api_data/dsm_7/core/const_7_core_external_usb.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""DSM 6 SYNO.Core.ExternalDevice.Storage.USB data."""
"""DSM 7 SYNO.Core.ExternalDevice.Storage.USB data."""

DSM_7_CORE_EXTERNAL_USB_DS1821_PLUS_NO_EXTERNAL_USB = {
"data": {"devices": []},
Expand Down
47 changes: 47 additions & 0 deletions tests/api_data/dsm_7/photos/const_7_photo.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@
"type": "normal",
"version": 195694,
},
{
"cant_migrate_condition": {},
"condition": {},
"create_time": 1718658534,
"end_time": 1719075481,
"freeze_album": False,
"id": 3,
"item_count": 1,
"name": "Album3",
"owner_user_id": 7,
"passphrase": "NiXlv1i2N",
alexismarquis marked this conversation as resolved.
Show resolved Hide resolved
"shared": False,
"sort_by": "default",
"sort_direction": "default",
"start_time": 1659724703,
"temporary_shared": False,
"type": "normal",
"version": 102886,
},
]
},
"success": True,
Expand Down Expand Up @@ -111,6 +130,34 @@
},
}

DSM_7_FOTO_ITEMS_SHARED_ALBUM = {
"success": True,
"data": {
"list": [
{
"id": 29807,
"filename": "20221115_185645.jpg",
"filesize": 2644859,
"time": 1668538602,
"indexed_time": 1668564550862,
"owner_user_id": 7,
"folder_id": 597,
"type": "photo",
"additional": {
"thumbnail": {
"m": "ready",
"xl": "ready",
"preview": "broken",
"sm": "ready",
"cache_key": "29810_1668560967",
"unit_id": 29807,
}
},
},
]
},
}

DSM_7_FOTO_SHARED_ITEMS = {
"success": True,
"data": {
Expand Down
32 changes: 31 additions & 1 deletion tests/test_synology_dsm_7.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,26 +179,39 @@ async def test_photos(self, dsm_7):
albums = await dsm_7.photos.get_albums()

assert albums
assert len(albums) == 2
assert len(albums) == 3
assert albums[0].album_id == 4
assert albums[0].name == "Album1"
assert albums[0].item_count == 3
assert albums[0].passphrase is None

assert albums[1].album_id == 1
assert albums[1].name == "Album2"
assert albums[1].item_count == 1
assert albums[1].passphrase is None

assert albums[2].album_id == 3
assert albums[2].name == "Album3"
assert albums[2].item_count == 1
assert albums[2].passphrase == "NiXlv1i2N"

items = await dsm_7.photos.get_items_from_album(albums[0])
assert items
assert len(items) == 3
assert items[0].file_name == "20221115_185642.jpg"
assert items[0].thumbnail_cache_key == "29807_1668560967"
assert items[0].thumbnail_size == "xl"
assert items[0].passphrase is None

assert items[1].file_name == "20221115_185643.jpg"
assert items[1].thumbnail_cache_key == "29808_1668560967"
assert items[1].thumbnail_size == "m"
assert items[1].passphrase is None

assert items[2].file_name == "20221115_185644.jpg"
assert items[2].thumbnail_cache_key == "29809_1668560967"
assert items[2].thumbnail_size == "sm"
assert items[2].passphrase is None

thumb_url = await dsm_7.photos.get_item_thumbnail_url(items[0])
assert thumb_url
Expand All @@ -218,6 +231,23 @@ async def test_photos(self, dsm_7):
"&_sid=session_id&SynoToken=Sy%C3%B10_T0k%E2%82%AC%C3%B1"
)

items = await dsm_7.photos.get_items_from_album(albums[2])
assert items
assert len(items) == 1
assert items[0].file_name == "20221115_185645.jpg"
assert items[0].thumbnail_cache_key == "29810_1668560967"
assert items[0].thumbnail_size == "xl"
assert items[0].passphrase == "NiXlv1i2N"

thumb_url = await dsm_7.photos.get_item_thumbnail_url(items[0])
assert thumb_url
assert thumb_url == (
"https://nas.mywebsite.me:443/webapi/entry.cgi?"
"id=29807&cache_key=29810_1668560967&size=xl&type=unit"
"&passphrase=NiXlv1i2N&api=SYNO.Foto.Thumbnail&version=2&method=get"
"&_sid=session_id&SynoToken=Sy%C3%B10_T0k%E2%82%AC%C3%B1"
)

items = await dsm_7.photos.get_items_from_search(albums[0])
assert items
assert len(items) == 2
Expand Down
Loading