diff --git a/Common/Immich Album Watcher/README.md b/Common/Immich Album Watcher/README.md index fd78854..2b69f17 100644 --- a/Common/Immich Album Watcher/README.md +++ b/Common/Immich Album Watcher/README.md @@ -71,6 +71,9 @@ Each item in `added_assets` contains: | `playback_url` | Video playback URL (for VIDEO assets only, if shared link exists) | | `photo_url` | Photo preview URL (for IMAGE assets only, if shared link exists) | | `people` | List of people detected in this specific asset | +| `city` | City name from reverse geocoding (if available) | +| `state` | State/region name from reverse geocoding (if available) | +| `country` | Country name from reverse geocoding (if available) | ## Message Template Variables @@ -86,6 +89,7 @@ All message templates support these placeholder variables (use single braces): | `{assets}` | Formatted list of added assets (using asset item template) | | `{video_warning}` | Warning about video size limits (Telegram only, empty otherwise) | | `{common_date}` | Common date formatted with template if all assets share same date, empty otherwise | +| `{common_location}` | Common location formatted with template if all assets share same location (requires city, state, country), empty otherwise | ## Asset Item Template Variables @@ -107,6 +111,11 @@ These variables can be used in the image and video asset templates. Also used fo | `{album_name}` | Name of the album | | `{is_favorite}` | Favorite indicator (using template) if asset is favorite, empty otherwise | | `{rating}` | User rating (1-5) or empty if not rated | +| `{location}` | Formatted location string (using location format template), empty if location data incomplete | +| `{location_if_unique}` | Location formatted with template if locations differ between assets, empty if all same or data incomplete | +| `{city}` | City name from reverse geocoding, empty if not available | +| `{state}` | State/region name from reverse geocoding, empty if not available | +| `{country}` | Country name from reverse geocoding, empty if not available | ## Telegram Media Attachments diff --git a/Common/Immich Album Watcher/blueprint.yaml b/Common/Immich Album Watcher/blueprint.yaml index 5a3e2c9..0c480d3 100644 --- a/Common/Immich Album Watcher/blueprint.yaml +++ b/Common/Immich Album Watcher/blueprint.yaml @@ -147,8 +147,8 @@ blueprint: name: "Assets Added Message" description: > Message sent when assets are added to an album. - Variables: `{album_name}`, `{album_url}`, `{added_count}`, `{people}`, `{assets}`, `{video_warning}`, `{common_date}` - default: "📷 {added_count} new photo(s) added to album \"{album_name}\".{people}{assets}{video_warning}" + Variables: `{album_name}`, `{album_url}`, `{added_count}`, `{people}`, `{assets}`, `{video_warning}`, `{common_date}`, `{common_location}` + default: "📷 {added_count} new photo(s) added to album \"{album_name}\"{common_date}{common_location}.{people}{assets}{video_warning}" selector: text: multiline: true @@ -276,6 +276,37 @@ blueprint: selector: text: + common_location_template: + name: "Common Location Template" + description: > + Template for showing the common location in the header message when all assets + share the same location. Only used when ALL location fields (city, state, country) + are present for all assets. Use `{location}` as the placeholder. + Leave empty to not show common location in header. + default: " in {location}" + selector: + text: + + location_if_unique_template: + name: "Location If Unique Template" + description: > + Template for showing location in asset items when assets have different locations. + Only used when ALL location fields (city, state, country) are present. + Use `{location}` as the placeholder. + Leave empty to not show per-asset locations. + default: " 📍 {location}" + selector: + text: + + location_format: + name: "Location Format" + description: > + Format for displaying asset locations. Available placeholders: + `{city}`, `{state}`, `{country}`. Only shown when all three fields are present. + default: "{city}, {country}" + selector: + text: + # ------------------------------------------------------------------------- # Telegram Media Attachments # ------------------------------------------------------------------------- @@ -788,6 +819,9 @@ variables: common_date_template: !input common_date_template date_if_unique_template: !input date_if_unique_template favorite_indicator_template: !input favorite_indicator_template + common_location_template: !input common_location_template + location_if_unique_template: !input location_if_unique_template + location_format: !input location_format # --------------------------------------------------------------------------- # Event Data @@ -843,8 +877,8 @@ variables: unique_dates: > {% set ns = namespace(dates = []) %} {% for asset in filtered_assets %} - {% set raw_date = asset.created_at | default('') %} - {% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %} + {% 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 '' %} {% if formatted_date | length > 0 and formatted_date not in ns.dates %} {% set ns.dates = ns.dates + [formatted_date] %} @@ -863,6 +897,46 @@ variables: {{ '' }} {% endif %} + # Compute unique locations from filtered assets (only when all location fields present) + unique_locations: > + {% set ns = namespace(locations = []) %} + {% for asset in filtered_assets %} + {% set city = asset.city | default('') %} + {% set state = asset.state | default('') %} + {% set country = asset.country | default('') %} + {% if city | length > 0 and state | length > 0 and country | length > 0 %} + {% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) %} + {% if formatted_location not in ns.locations %} + {% set ns.locations = ns.locations + [formatted_location] %} + {% endif %} + {% endif %} + {% endfor %} + {{ ns.locations }} + + # 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 + all_assets_have_location: > + {% set ns = namespace(count = 0) %} + {% for asset in filtered_assets %} + {% set city = asset.city | default('') %} + {% set state = asset.state | default('') %} + {% set country = asset.country | default('') %} + {% if city | length > 0 and state | length > 0 and country | length > 0 %} + {% set ns.count = ns.count + 1 %} + {% endif %} + {% endfor %} + {{ ns.count == filtered_assets | length and filtered_assets | length > 0 }} + + # Common location for header (formatted with template if all same, empty otherwise) + common_location: > + {% if all_assets_have_location and unique_locations | length == 1 %} + {{ common_location_template | replace('{location}', unique_locations[0]) }} + {% else %} + {{ '' }} + {% endif %} + # Format assets list for notification assets_list: > {% if include_asset_details and filtered_assets | length > 0 %} @@ -871,12 +945,18 @@ variables: {% set assets_to_show = filtered_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('') %} - {% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %} + {% 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 '' %} {% set created_if_unique = '' if unique_dates | length == 1 else (date_if_unique_template | replace('{date}', formatted_date)) %} {% set is_favorite = favorite_indicator_template if asset.is_favorite | default(false) else '' %} {% set rating = asset.rating | default('') | string if asset.rating not in [none, ''] else '' %} + {% set city = asset.city | default('') %} + {% set state = asset.state | default('') %} + {% set country = asset.country | default('') %} + {% set has_full_location = city | length > 0 and state | length > 0 and country | length > 0 %} + {% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) if has_full_location else '' %} + {% set location_if_unique = '' if (not has_full_location) or (all_assets_have_location and unique_locations | length == 1) else (location_if_unique_template | replace('{location}', formatted_location)) %} {% set item = asset_template | replace('{filename}', asset.filename | default('Unknown')) | replace('{description}', asset.description | default('')) @@ -891,7 +971,12 @@ variables: | replace('{people}', (asset.people | default([])) | join(', ')) | replace('{album_name}', event_album_name) | replace('{is_favorite}', is_favorite) - | replace('{rating}', rating) %} + | replace('{rating}', rating) + | replace('{location}', formatted_location) + | replace('{location_if_unique}', location_if_unique) + | replace('{city}', city) + | replace('{state}', state) + | replace('{country}', country) %} {% set ns.items = ns.items ~ item %} {% endfor %} {% set more_count = filtered_assets | length - max_items %} @@ -1164,11 +1249,16 @@ action: {% set ns = namespace(items = '') %} {% for asset in fetched_assets %} {% set asset_template = message_asset_video_template if asset.type == 'VIDEO' else message_asset_image_template %} - {% set raw_date = asset.created_at | default('') %} - {% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %} + {% 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 '' %} {% set is_favorite = favorite_indicator_template if asset.is_favorite | default(false) else '' %} {% set rating = asset.rating | default('') | string if asset.rating not in [none, ''] else '' %} + {% set city = asset.city | default('') %} + {% set state = asset.state | default('') %} + {% set country = asset.country | default('') %} + {% set has_full_location = city | length > 0 and state | length > 0 and country | length > 0 %} + {% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) if has_full_location else '' %} {% set item = asset_template | replace('{filename}', asset.filename | default('Unknown')) | replace('{description}', asset.description | default('')) @@ -1183,7 +1273,12 @@ action: | replace('{people}', (asset.people | default([])) | join(', ')) | replace('{album_name}', current_album_name) | replace('{is_favorite}', is_favorite) - | replace('{rating}', rating) %} + | replace('{rating}', rating) + | replace('{location}', formatted_location) + | replace('{location_if_unique}', formatted_location) + | replace('{city}', city) + | replace('{state}', state) + | replace('{country}', country) %} {% set ns.items = ns.items ~ item %} {% endfor %} {{ ns.items }} @@ -1348,11 +1443,16 @@ action: {% set ns = namespace(items = '') %} {% for asset in all_fetched_assets %} {% set asset_template = message_asset_video_template if asset.type == 'VIDEO' else message_asset_image_template %} - {% set raw_date = asset.created_at | default('') %} - {% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %} + {% 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 '' %} {% set is_favorite = favorite_indicator_template if asset.is_favorite | default(false) else '' %} {% set rating = asset.rating | default('') | string if asset.rating not in [none, ''] else '' %} + {% set city = asset.city | default('') %} + {% set state = asset.state | default('') %} + {% set country = asset.country | default('') %} + {% set has_full_location = city | length > 0 and state | length > 0 and country | length > 0 %} + {% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) if has_full_location else '' %} {% set item = asset_template | replace('{filename}', asset.filename | default('Unknown')) | replace('{description}', asset.description | default('')) @@ -1367,7 +1467,12 @@ action: | replace('{people}', (asset.people | default([])) | join(', ')) | replace('{album_name}', combined_album_names) | replace('{is_favorite}', is_favorite) - | replace('{rating}', rating) %} + | replace('{rating}', rating) + | replace('{location}', formatted_location) + | replace('{location_if_unique}', formatted_location) + | replace('{city}', city) + | replace('{state}', state) + | replace('{country}', country) %} {% set ns.items = ns.items ~ item %} {% endfor %} {{ ns.items }} @@ -1523,7 +1628,8 @@ action: | replace('{people}', people_list) | replace('{assets}', assets_list) | replace('{video_warning}', video_warning_text) - | replace('{common_date}', common_date) }} + | replace('{common_date}', common_date) + | replace('{common_location}', common_location) }} - service: notify.send_message target: @@ -1748,7 +1854,8 @@ action: | replace('{people}', people_list) | replace('{assets}', assets_list) | replace('{video_warning}', video_warning_text) - | replace('{common_date}', common_date) }} + | replace('{common_date}', common_date) + | replace('{common_location}', common_location) }} # Build URLs list for media # URL preference: playback_url (videos), photo_url (images) > download_url > url diff --git a/manifest.json b/manifest.json index 498b8c1..91037bd 100644 --- a/manifest.json +++ b/manifest.json @@ -1,3 +1,3 @@ { - "version": "1.20.0" + "version": "1.21.0" }