feat(immich): per-album scheduled/memory dispatch + template tooling
Dispatch: honor {kind}_collection_mode on TrackingConfig — "per_collection"
fans out one event per album; "combined" pools assets as before. Extract
build_immich_dispatch_events shared by cron and test paths.
Assets: collect_scheduled_assets attaches album_name/album_url/album_public_url
to each asset so combined-mode templates can attribute rows to their source
album. Default scheduled_assets templates render a multi-album header with
inline album list and per-row album link; memory_mode follows the same pattern.
UI: "Reset to default" buttons on notification and command template slots
(per-slot and whole-template), backed by new GET /*-template-configs/defaults
endpoints. tracking-configs "Preview template" now opens an inline preview
modal with locale tabs instead of navigating away; Edit button deep-links
with ?edit_slot=<name> so the destination auto-opens the config and scrolls
to the slot. Reset confirmations use ConfirmModal instead of window.confirm.
Fixes:
* NotificationDispatcher._session_ctx infinite recursion when no shared
aiohttp.ClientSession was passed — broke test dispatch for periodic/
scheduled/memory (cron path was unaffected).
* telegram-bots /chats/{id}/test now resolves chat.language_override /
language_code instead of using the raw ?locale query param, matching
the resolution the tracker-target test endpoint already used.
* scheduled_assets default template no longer emits a blank line between
header and the first asset when the multi-album branch is taken.
This commit is contained in:
@@ -76,16 +76,28 @@ def build_asset_dict(
|
||||
public_url: str = "",
|
||||
year: int | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build a rich asset dict for command templates from an ImmichAssetInfo or raw dict."""
|
||||
"""Build a rich asset dict for command templates from an ImmichAssetInfo or raw dict.
|
||||
|
||||
Asset-dict contract (shared with notification templates — see
|
||||
``notify_bridge_core.templates.context``): templates may read either
|
||||
``filename`` (the canonical field, used by notification defaults) or
|
||||
``originalFileName`` (the historical command-default field); both are
|
||||
populated so a custom template authored against either key keeps working.
|
||||
Same story for ``created_at`` / ``createdAt``.
|
||||
"""
|
||||
if isinstance(asset, dict):
|
||||
# Immich raw search responses nest geo under exifInfo — pull it out so
|
||||
# templates can use flat asset.city / asset.country.
|
||||
exif = asset.get("exifInfo") or {}
|
||||
fname = asset.get("originalFileName") or asset.get("filename") or ""
|
||||
created = asset.get("createdAt") or asset.get("created_at") or asset.get("fileCreatedAt") or ""
|
||||
d = {
|
||||
"id": asset.get("id", ""),
|
||||
"originalFileName": asset.get("originalFileName", asset.get("filename", "")),
|
||||
"filename": fname,
|
||||
"originalFileName": fname,
|
||||
"type": asset.get("type", "IMAGE"),
|
||||
"createdAt": asset.get("createdAt", asset.get("created_at", asset.get("fileCreatedAt", ""))),
|
||||
"created_at": created,
|
||||
"createdAt": created,
|
||||
"city": asset.get("city") or exif.get("city") or "",
|
||||
"country": asset.get("country") or exif.get("country") or "",
|
||||
"is_favorite": asset.get("is_favorite", asset.get("isFavorite", False)),
|
||||
@@ -97,8 +109,10 @@ def build_asset_dict(
|
||||
# ImmichAssetInfo dataclass
|
||||
return {
|
||||
"id": asset.id,
|
||||
"filename": asset.filename,
|
||||
"originalFileName": asset.filename,
|
||||
"type": asset.type,
|
||||
"created_at": asset.created_at,
|
||||
"createdAt": asset.created_at,
|
||||
"city": getattr(asset, "city", "") or "",
|
||||
"country": getattr(asset, "country", "") or "",
|
||||
|
||||
Reference in New Issue
Block a user