fix(commands): enrich search assets, surface variables for all command slots

- UI: command-template-configs now resolves slot variables against the
  active provider first (varsRef[provider_type][slot]) before falling back
  to shared entries, so provider-specific slots like /search, /status,
  /repos, /issues, /boards show the Variables button and autocomplete.
- Backend: /search, /find, /person, /place now normalize raw Immich API
  responses through build_asset_dict, extracting city/country from
  exifInfo and mapping isFavorite -> is_favorite so templates render
  location and favorite indicators.
- Telegram: extract build_telegram_asset_entry into a shared helper so
  the notification dispatcher and command media groups agree on video
  typing and /video/playback URLs; videos no longer render as still
  thumbnails in /latest /random /favorites media mode.
- Commands: send_media_group now reuses the same Telegram file_id caches
  as the notification dispatcher, avoiding re-upload churn for repeated
  commands.
This commit is contained in:
2026-04-22 16:28:26 +03:00
parent 85311684d9
commit fab6169cf9
6 changed files with 146 additions and 54 deletions
@@ -393,30 +393,38 @@ async def send_media_group(
reply_to_message_id: int | None = None,
session: aiohttp.ClientSession | None = None,
) -> None:
"""Send media items via TelegramClient.send_notification."""
"""Send media items via TelegramClient.send_notification.
``media_items`` must already be in TelegramClient asset format — each
entry contains ``type`` (``"photo"``/``"video"``/``"document"``),
``url``, optional ``cache_key``, and optional ``headers``. Provider
command handlers build this format directly (via
``build_telegram_asset_entry``) so videos keep their ``"video"`` type
and point at a real video URL instead of a still thumbnail.
Reuses the same Telegram file_id caches as the notification dispatcher
so repeated ``/latest`` / ``/random`` commands don't re-upload bytes
for assets Telegram has already seen. If the cache hasn't been
initialized (no data dir configured) we fall through to a plain
upload — identical behavior to the notification path.
"""
if not media_items:
return
# Convert command handler media format to TelegramClient asset format
assets = []
for item in media_items:
assets.append({
"type": "photo",
"url": item.get("thumbnail_url", ""),
"cache_key": item.get("asset_id", ""),
"headers": {"x-api-key": item.get("api_key", "")},
})
# Build caption from first item
captions = [item.get("caption", "") for item in media_items if item.get("caption")]
caption = "\n".join(captions) if captions else None
if session is None:
from ..services.http_session import get_http_session
session = await get_http_session()
client = TelegramClient(session, bot_token)
from ..services.watcher import _get_telegram_caches
url_cache, asset_cache = await _get_telegram_caches()
client = TelegramClient(
session, bot_token,
url_cache=url_cache,
asset_cache=asset_cache,
)
result = await client.send_notification(
chat_id, assets=assets, caption=caption,
chat_id, assets=media_items,
reply_to_message_id=reply_to_message_id,
chat_action=None,
)