- v3.2.0 renamed asset_face.personId to personGroupId and dropped person.id
in favour of a (ownerId, personGroupId) key, breaking every face/person
query.
Smart Search produced takenAfter/takenBefore as plain YYYY-MM-DD strings,
but the Immich search API validates them as ISO 8601 datetimes and rejects
a bare date with invalid_format / format: datetime. Every search containing
a date failed with 'Failed to fetch assets'.
Expand date-only values after the existing today-guardrail (which compares
YYYY-MM-DD strings and must keep doing so): takenAfter to the start of the
day, takenBefore to the end of the day so a single-day range such as
'videos yesterday' is not empty. Values that already carry a time component
are left untouched.
.dockerignore did not list .env, so `docker build` copied any local .env —
credentials included — into the image build context (and into the image
wherever the Dockerfile does a broad `COPY . .`). Anyone building the image
with a populated .env present would bake their secrets into the resulting
image layers.
Add `.env` and `.env.*.local` to .dockerignore so local environment files
never enter the build context. Runtime configuration is still supplied the
intended way — environment variables / an env_file at `docker run` /
compose time — so nothing about normal deployment changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The myRole select added for issue #286 wasn't included in the GROUP BY
clause, causing a 500 ("column album_user.role must appear in the
GROUP BY clause") whenever includeShared=true was requested.
Closes#286. The album picker for add_to_album/remove_from_album only
showed albums owned by the current user. Add an opt-in ?includeShared
param to /api/albums/list that also includes albums where the user
has 'editor' role (upload access), and group the picker into 'My
Albums' / 'Shared Albums' sections. Other callers of listAlbums()
don't pass includeShared, so they keep the existing owner-only
behavior unchanged.
Merges potential-trips + potential-trip-assets into
potential-albums-dates + potential-albums-assets respectively, keyed
by a new ?groupBy=day|trip query param. The actual grouping logic is
extracted into two new helper files (potentialAlbumsGrouping.helper.ts,
potentialAlbumsAssets.helper.ts), each holding a day-mode and
trip-mode function the unified handlers call into.
Client-facing functions (listPotentialTrips, listPotentialTripAssets)
keep their existing names/signatures — only their internal path and a
groupBy param changed — so no frontend component call sites need
updating. Also drops the two now-unused LIST_POTENTIAL_TRIP*_PATH
route constants.
Addresses the maintainer's open caching-strategy question by removing
the need for a second endpoint/cache entirely: one endpoint per
concern, two grouping strategies.
Immich v3 dropped asset.deviceId/deviceAssetId (af4cfce on main).
This branch predates that migration, so its own SELECT_ORPHAN_RANGE
query still referenced the column, which would 500 on any current
Immich v3 instance. Match the field list already used by the sibling
potential-albums-assets.ts endpoint, which was updated for v3.
clusterTrips's gap check compared array-index adjacency between
DaySignal entries. Since `days` only contains dates with >=1 photo
(GROUP BY DATE(...) with no zero-photo rows), two trips separated by
months with no photos in between were array-adjacent (gapLen=0) and
got merged into one bogus trip. Compare actual elapsed calendar days
between the last and next away day instead.
The local tag table can lag behind Immich's own tag state (e.g. tags
created directly in Immich, or before this app's sync ran), causing
remove_tag to silently no-op on a tag that actually exists. Mirror the
sibling "tag" action's approach and resolve against the Immich API,
matching on the tag's full-path "value" field. Drops the now-unused
db/sql local lookup.
makeDeviceAssetId(item.assetId) was passed as an extra 5th argument
before tagId, which doesn't match uploadAssetBuffer's signature
(asset, payload, uploadHeaders, jsonHeaders, tagId?) and broke the
TS build. Match the call pattern used by the sibling nextcloud/immich
processors.
The id list was built via raw string interpolation into sql.raw().
Switch to sql.join with proper parameter binding, matching the
pattern used by the other match branches in this function.
Removes a tag (by name, matching the tag's full path value) from the
matched assets via DELETE /tags/:id/assets. The tag is looked up
without creating it — removing a tag that doesn't exist is a no-op
rather than an error, so add/remove pairs are safe in the same
workflow.
Registered in the node palette, action node, run-detail view, and
action config (same tag-name field as Add Tag).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds condition types common in smart-album systems of other photo
managers (Apple Photos, Lightroom, Google Photos):
- File Size: min/max in MB (fractional) over asset_exif.fileSizeInByte
- File Name / Path: contains / not contains / starts with / ends with
against originalFileName or originalPath (ILIKE, wildcards escaped so
user text matches literally)
- File Extension: is any of / is none of a comma-separated list,
case-insensitive, matched after the last dot
- Face Count: min/max visible, non-deleted detected faces — 'how many
people', complementing the person condition's 'who'
- Time of Day: from/to hour over localDateTime (capture wall-clock
time, no timezone math); From > To wraps past midnight (22 to 5)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a "Resolution" condition type filtering assets by image
dimensions, useful for isolating low-resolution images for cleanup or
selecting high-resolution ones.
Three metrics with optional min/max bounds:
- Megapixels (width x height / 1e6, fractional values allowed)
- Short edge in pixels (LEAST of width/height)
- Long edge in pixels (GREATEST of width/height)
Short/long edge are orientation-agnostic, so a 4000x3000 landscape and
a 3000x4000 portrait are treated identically. Uses the exifImageWidth/
exifImageHeight columns on asset_exif, which the condition queries
already join; assets without recorded dimensions never match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>