Files
espresso_frame/server/app/immich_client.py
T
tfaour 34a3293a07
Build and push server image / build-and-push (push) Failing after 2m33s
Fix /frame/image 404: Immich v3 doesn't embed assets in album responses
GET /api/albums/{id} was assumed to return an "assets" array alongside
the album metadata (that's what the original plan/prior art expected),
but on Immich 3.0.3 AlbumResponseDto only has assetCount -- no assets
field at all. Confirmed against the OpenAPI spec served at
/api/spec.json and by testing directly against a real instance: the
assumption was simply wrong for this API version, not a permissions
issue (the album metadata call succeeds fine with a valid 200).

Assets for an album now come from POST /api/search/metadata with an
albumIds filter, which returns them under assets.items. Verified
end-to-end against the real Immich instance and album -- /frame/image
now returns a proper 200 with exactly 192,000 bytes, spread across all
six panel colors (not a degenerate all-white/black response).

Only fetches the first page of search results; fine for a photo frame
cycling through an album, but would need nextPage handling for anyone
pointing this at a very large album.
2026-07-18 15:19:05 -04:00

45 lines
1.5 KiB
Python

"""Thin wrapper around the bits of the Immich API this project needs."""
from __future__ import annotations
import httpx
class ImmichClient:
def __init__(self, base_url: str, api_key: str):
self.base_url = base_url.rstrip("/")
self._headers = {"x-api-key": api_key}
def list_albums(self) -> list[dict]:
resp = httpx.get(f"{self.base_url}/api/albums", headers=self._headers, timeout=10)
resp.raise_for_status()
return resp.json()
def list_album_assets(self, album_id: str) -> list[dict]:
"""GET /api/albums/{id} doesn't embed assets in this Immich version
(AlbumResponseDto only has assetCount) -- assets live behind the
general search API instead, filtered by albumIds.
Only returns the first page. Fine for a photo frame cycling
through an album; worth adding nextPage pagination if someone
points this at an album large enough to need it.
"""
resp = httpx.post(
f"{self.base_url}/api/search/metadata",
headers=self._headers,
json={"albumIds": [album_id]},
timeout=15,
)
resp.raise_for_status()
return resp.json().get("assets", {}).get("items", [])
def download_asset_preview(self, asset_id: str) -> bytes:
resp = httpx.get(
f"{self.base_url}/api/assets/{asset_id}/thumbnail",
params={"size": "preview"},
headers=self._headers,
timeout=30,
)
resp.raise_for_status()
return resp.content