- Defer quiet-hours dispatches into new deferred_dispatch table; drain
job + periodic catch-up scan re-fire at window end with coalescing on
(link, event_type, collection_id).
- Add ON DELETE SET NULL migration on event_log_id and partial unique
index on (link_id, collection_id, event_type) WHERE status='pending'.
- Add release-check provider abstraction (Gitea/GitHub) with SSRF-safe
URL validation, settings UI cassette, and scheduled polling.
- Replace importlib-only version lookup with version.py helper that
prefers the higher of installed metadata vs source pyproject so stale
editable dev installs stop misreporting.
- Aurora frontend polish: MetaStrip component, ReleaseCassette,
EventDetailModal expansion, and i18n additions.
Display filters (Immich tracking config):
- favorites_only drops events with no favorited new assets, or filters
added_assets to favorites only
- assets_order_by/assets_order sort the rendered list
(date / name / rating / random / none)
- max_assets_to_show caps rendered+attached media (default 5 -> 10)
- include_tags strips people from event extras and tags from each asset
- include_asset_details strips city/country/state/lat/lon/is_favorite/
rating/description; load-bearing fields (thumbhash, file_size,
playback_size, cache keys) preserved
- New apply_tracking_display_filters helper in dispatch_helpers; wired
into watcher, webhooks, scheduled/periodic/memory, and manual
test-dispatch
- Targets sharing a TrackingConfig dispatch together; targets with
different TCs each see their own shaped event
Adaptive polling:
- Replace NotificationTracker.batch_duration with adaptive_max_skip
- Per-tracker opt-in: NULL/0 disables back-off (every tick runs);
positive N caps the skip factor at (N-1)-in-N after long idle
- Scheduler caches the cap in module state for the tick fast-path
- Migration adds the new column; API schemas/responses, frontend types,
i18n, and the tracker form updated to match
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.
The scheduled_enabled / scheduled_times (and the periodic / memory
counterparts) on TrackingConfig had been wired into the model, the
API, and the test-dispatch path — but no production scheduler ever
read them, so users saw the slot in the UI and only ever got fires
through "Test". This adds the missing cron jobs and the dispatch
fan-out, both keyed off the app-level IANA timezone.
* services/scheduled_dispatch.py — production fan-out reusing the
test-path event builders, picking the slot template per kind, and
writing an EventLog row per fire so the dashboard reflects it.
* services/scheduler.py — _load_immich_dispatch_jobs builds one
CronTrigger per (tracker, kind, HH:MM) from the tracker's default
TrackingConfig; reschedule_immich_dispatch_jobs rebuilds them all
on any relevant CRUD or timezone change.
* tracker / link / tracking-config CRUD endpoints now invalidate.
Also: skip dispatch when scheduled/memory yield zero matching assets
(prevents header-only "On this day:" spam), and update the EN/RU
default scheduled_assets templates to surface that the delivery is
a scheduled random selection.