App Updates

This commit is contained in:
2026-07-21 11:19:17 -04:00
parent 87ffe648a5
commit 0ed1687503
180 changed files with 943 additions and 118 deletions
@@ -41,6 +41,9 @@ from .const import (
CONF_PHOTOPRISM_IMAGE_SIZE,
CONF_PHOTOPRISM_FILTER,
DEFAULT_PHOTOPRISM_IMAGE_SIZE,
CONF_ICLOUD_TOKEN,
CONF_ICLOUD_IMAGE_SIZE,
DEFAULT_ICLOUD_IMAGE_SIZE,
DEFAULT_REVERSE_GEOCODE,
DOMAIN,
PROVIDER_GOOGLE_SHARED,
@@ -48,6 +51,7 @@ from .const import (
PROVIDER_MEDIA_SOURCE,
PROVIDER_IMMICH,
PROVIDER_PHOTOPRISM,
PROVIDER_ICLOUD,
)
from .store import SlideshowStore
@@ -933,6 +937,8 @@ class AlbumCoordinator(DataUpdateCoordinator):
data = await self._update_immich()
elif self.provider == PROVIDER_PHOTOPRISM:
data = await self._update_photoprism()
elif self.provider == PROVIDER_ICLOUD:
data = await self._update_icloud()
else:
raise UpdateFailed(f"Unsupported provider: {self.provider}")
except UpdateFailed:
@@ -1445,6 +1451,67 @@ class AlbumCoordinator(DataUpdateCoordinator):
"items": items,
}
async def _update_icloud(self) -> dict[str, Any]:
"""Fetch photos from a public iCloud Shared Album.
The webstream response carries capture date and caption inline, so
there is no enrichment pass. Signed image URLs are resolved up front
and expire after roughly a day, so they are refreshed on every album
refresh (like Google Photos).
"""
from . import icloud as icloud_api
token = self.entry.data.get(CONF_ICLOUD_TOKEN)
size = self.entry.data.get(CONF_ICLOUD_IMAGE_SIZE, DEFAULT_ICLOUD_IMAGE_SIZE)
if not token:
raise UpdateFailed("iCloud provider is missing the album token")
client = icloud_api.IcloudClient(self.hass, token)
try:
photos = await client.async_get_photos()
asset_urls = await client.async_get_asset_urls(
[p["photoGuid"] for p in photos if p.get("photoGuid")]
)
except Exception as err:
raise UpdateFailed(f"Error querying iCloud album: {err}") from err
if not photos:
raise UpdateFailed("No images found in the iCloud album")
items: list[MediaItem] = []
for p in photos:
guid = p.get("photoGuid")
checksum = icloud_api.pick_checksum(p, size)
if not guid or not checksum:
continue
url = icloud_api.build_image_url(asset_urls.get(checksum))
if not url:
continue
meta = icloud_api.parse_photo_meta(p)
w = p.get("width")
h = p.get("height")
items.append(
MediaItem(
url=url,
width=int(w) if str(w).isdigit() else None,
height=int(h) if str(h).isdigit() else None,
mime_type=None,
filename=None,
captured_at=meta.get("captured_at"),
description=meta.get("description"),
source_id=guid,
exif_scanned=True,
)
)
if not items:
raise UpdateFailed("Could not resolve any iCloud image URLs")
return {
"title": self.entry.title,
"items": items,
}
async def _enrich_immich_item(self, item: MediaItem) -> None:
"""Fetch one Immich asset's detail and fill location/description."""
from . import immich as immich_api