Add asset sorting and favorites-only filter for notifications

New notification settings for "assets added" events:
- Sort Assets By: date, rating, name, or original order
- Sort Direction: ascending or descending
- Notify Favorites Only: filter to only favorite assets

Rating sort handles null values by placing unrated assets last
(descending) or first (ascending).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 14:54:36 +03:00
parent b48d78a188
commit dce47a9f75
3 changed files with 103 additions and 22 deletions

View File

@@ -7,6 +7,8 @@ This blueprint monitors Immich album changes and sends notifications when assets
- Filter by hub/instance name (for multi-hub setups)
- Monitor specific albums by name (whitelist)
- Filter by asset type (track images only, videos only, or both)
- Filter by favorites only (only notify about favorite assets)
- Sort assets by date, rating, name, or keep original order
- Send notifications to multiple targets simultaneously
- Customizable notification messages with template variables
- Separate templates for image and video assets
@@ -15,6 +17,7 @@ This blueprint monitors Immich album changes and sends notifications when assets
- Support for multiple change types (assets added, removed, changed)
- Optional: send photos/videos as Telegram media attachments
- Optional: periodic summary notifications with album list and share URLs
- Optional: memory mode (On This Day) for photos from today's date in previous years
## Event Data from Immich

View File

@@ -136,6 +136,46 @@ blueprint:
max: 50
mode: slider
assets_order_by:
name: Sort Assets By
description: >
How to sort assets in the notification list and media attachments.
Only applies to "assets added" notifications.
default: "none"
selector:
select:
options:
- label: "Original Order"
value: "none"
- label: "Date"
value: "date"
- label: "Rating"
value: "rating"
- label: "Name"
value: "name"
assets_order:
name: Sort Direction
description: "Sort direction for assets (only applies when Sort Assets By is not Original Order)"
default: "descending"
selector:
select:
options:
- label: "Descending (newest/highest first)"
value: "descending"
- label: "Ascending (oldest/lowest first)"
value: "ascending"
notify_favorites_only:
name: Notify Favorites Only
description: >
Only send notifications for assets marked as favorites.
Non-favorite assets will be filtered out from both the notification
text and any media attachments.
default: false
selector:
boolean:
# -------------------------------------------------------------------------
# Message Templates
# -------------------------------------------------------------------------
@@ -864,6 +904,9 @@ variables:
include_people: !input include_people
include_asset_details: !input include_asset_details
max_assets_to_show: !input max_assets_to_show
assets_order_by: !input assets_order_by
assets_order: !input assets_order
notify_favorites_only: !input notify_favorites_only
track_assets_added: !input track_assets_added
track_assets_removed: !input track_assets_removed
track_images: !input track_images
@@ -1021,26 +1064,61 @@ variables:
{{ '' }}
{% endif %}
# Filter assets based on track_images/track_videos settings
# Filter assets based on track_images/track_videos and notify_favorites_only settings
# Videos are excluded if they don't have a playback URL
filtered_assets: >
{% set ns = namespace(assets = []) %}
{% for asset in event_added_assets %}
{% if asset.type == 'IMAGE' and track_images %}
{% set ns.assets = ns.assets + [asset] %}
{% elif asset.type == 'VIDEO' and track_videos and (asset.playback_url | default('') | length > 0) %}
{% set ns.assets = ns.assets + [asset] %}
{% set is_favorite = asset.is_favorite | default(false) %}
{% set passes_favorite_filter = (not notify_favorites_only) or is_favorite %}
{% if passes_favorite_filter %}
{% if asset.type == 'IMAGE' and track_images %}
{% set ns.assets = ns.assets + [asset] %}
{% elif asset.type == 'VIDEO' and track_videos and (asset.playback_url | default('') | length > 0) %}
{% set ns.assets = ns.assets + [asset] %}
{% endif %}
{% endif %}
{% endfor %}
{{ ns.assets }}
# Count of filtered assets (for notifications)
filtered_added_count: "{{ filtered_assets | length }}"
# Sort filtered assets based on user settings
# Note: Rating sort handles null values by placing unrated assets last (descending) or first (ascending)
sorted_assets: >
{% if assets_order_by == 'none' %}
{{ filtered_assets }}
{% elif assets_order_by == 'date' %}
{% set reverse = assets_order == 'descending' %}
{{ filtered_assets | sort(attribute='created_at', reverse=reverse) | list }}
{% elif assets_order_by == 'rating' %}
{% set reverse = assets_order == 'descending' %}
{% set ns = namespace(rated = [], unrated = []) %}
{% for asset in filtered_assets %}
{% if asset.rating is not none and asset.rating | int > 0 %}
{% set ns.rated = ns.rated + [asset] %}
{% else %}
{% set ns.unrated = ns.unrated + [asset] %}
{% endif %}
{% endfor %}
{% set sorted_rated = ns.rated | sort(attribute='rating', reverse=reverse) | list %}
{% if reverse %}
{{ sorted_rated + ns.unrated }}
{% else %}
{{ ns.unrated + sorted_rated }}
{% endif %}
{% elif assets_order_by == 'name' %}
{% set reverse = assets_order == 'descending' %}
{{ filtered_assets | sort(attribute='filename', reverse=reverse) | list }}
{% else %}
{{ filtered_assets }}
{% endif %}
# Compute unique dates from filtered assets
# Count of filtered assets (for notifications)
filtered_added_count: "{{ sorted_assets | length }}"
# Compute unique dates from sorted assets
unique_dates: >
{% set ns = namespace(dates = []) %}
{% for asset in filtered_assets %}
{% for asset in sorted_assets %}
{% set raw_date = asset.created_at | default('', true) %}
{% set dt = raw_date | as_datetime(none) if raw_date is string and raw_date | length > 0 else none %}
{% set formatted_date = dt.strftime(date_format) if dt else '' %}
@@ -1053,10 +1131,10 @@ variables:
# Check if all assets share the same date
all_dates_same: "{{ unique_dates | length == 1 }}"
# Compute unique locations from filtered assets (only when all location fields present)
# Compute unique locations from sorted assets (only when all location fields present)
unique_locations: >
{% set ns = namespace(locations = []) %}
{% for asset in filtered_assets %}
{% for asset in sorted_assets %}
{% set city = asset.city | default('', true) %}
{% set state = asset.state | default('', true) %}
{% set country = asset.country | default('', true) %}
@@ -1072,10 +1150,10 @@ variables:
# Check if all assets with location data share the same location
all_locations_same: "{{ unique_locations | length == 1 }}"
# Check if all filtered assets have complete location data
# Check if all sorted assets have complete location data
all_assets_have_location: >
{% set ns = namespace(count = 0) %}
{% for asset in filtered_assets %}
{% for asset in sorted_assets %}
{% set city = asset.city | default('', true) %}
{% set state = asset.state | default('', true) %}
{% set country = asset.country | default('', true) %}
@@ -1083,14 +1161,14 @@ variables:
{% set ns.count = ns.count + 1 %}
{% endif %}
{% endfor %}
{{ ns.count == filtered_assets | length and filtered_assets | length > 0 }}
{{ ns.count == sorted_assets | length and sorted_assets | length > 0 }}
# Format assets list for notification
assets_list: >
{% if include_asset_details and filtered_assets | length > 0 %}
{% if include_asset_details and sorted_assets | length > 0 %}
{% set ns = namespace(items = '') %}
{% set max_items = max_assets_to_show if max_assets_to_show > 0 else filtered_assets | length %}
{% set assets_to_show = filtered_assets[:max_items] %}
{% set max_items = max_assets_to_show if max_assets_to_show > 0 else sorted_assets | length %}
{% set assets_to_show = sorted_assets[:max_items] %}
{% for asset in assets_to_show %}
{% set asset_template = message_asset_video_template if asset.type == 'VIDEO' else message_asset_image_template %}
{% set raw_date = asset.created_at | default('', true) %}
@@ -1129,7 +1207,7 @@ variables:
| replace('{country}', country) %}
{% set ns.items = ns.items ~ item %}
{% endfor %}
{% set more_count = filtered_assets | length - max_items %}
{% set more_count = sorted_assets | length - max_items %}
{% if more_count > 0 %}
{% set ns.items = ns.items ~ message_assets_more_template | replace('{more_count}', more_count | string) %}
{% endif %}
@@ -1138,15 +1216,15 @@ variables:
{{ '' }}
{% endif %}
# Check if filtered assets contain any videos (for Telegram warning)
# Check if sorted assets contain any videos (for Telegram warning)
has_videos_in_assets: >
{{ filtered_assets | selectattr('type', 'equalto', 'VIDEO') | list | length > 0 }}
{{ sorted_assets | selectattr('type', 'equalto', 'VIDEO') | list | length > 0 }}
# Filter assets that have valid URLs (for Telegram media)
# URL preference: playback_url (videos), photo_url (images) > download_url > url (viewer)
assets_with_urls: >
{% set ns = namespace(assets = []) %}
{% for asset in filtered_assets %}
{% for asset in sorted_assets %}
{% set playback_url = asset.playback_url | default('') %}
{% set photo_url = asset.photo_url | default('') %}
{% set download_url = asset.download_url | default('') %}

View File

@@ -1,3 +1,3 @@
{
"version": "1.25.0"
"version": "1.26.0"
}