Commit Graph
530 Commits
Author SHA1 Message Date
Varun Raj f37c19cc84 fix: add album_user.role to GROUP BY in albums list query
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.
2026-07-11 04:26:47 +00:00
Varun Raj 1afe0c858c feat: list shared albums (editor access) in workflow add/remove-album picker
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.
2026-07-10 16:20:37 +00:00
Varun Raj ManoharanandGitHub 58bb1d0e9d Merge pull request #268 from dgilperez/feat/potential-trips
feat(potential-albums): add Trips view that groups consecutive away days
2026-07-10 21:43:46 +05:30
Varun Raj 5068419e3b Merge remote-tracking branch 'origin/prerelease' into feat/potential-trips 2026-07-10 16:13:24 +00:00
Varun Raj a5a4528962 refactor: consolidate day/trip grouping into shared endpoints via groupBy param
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.
2026-07-10 16:11:22 +00:00
Varun Raj ef3ce79c5b fix: drop asset.deviceId from raw SQL select — column removed in Immich v3
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.
2026-07-10 15:55:41 +00:00
Varun Raj a9d2adc6a1 fix: measure trip gap tolerance in real calendar days, not array-index distance
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.
2026-07-10 15:53:04 +00:00
Varun Raj ManoharanandGitHub 7d2ee70f62 Merge pull request #301 from jonvdveen/feat/remove-tag-action
feat: Remove Tag workflow action to complement Add Tag
2026-07-10 21:20:43 +05:30
Varun Raj 40e9e0dde2 fix: resolve tag via Immich API instead of local DB table in remove_tag
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.
2026-07-10 15:48:29 +00:00
Varun Raj ManoharanandGitHub 4e1ad2d532 Merge pull request #275 from immich-power-tools/feat/ente-import-shared
feat: add support for Ente platform integration
2026-07-10 19:37:13 +05:30
Varun Raj ManoharanandGitHub 800751d493 Merge pull request #297 from jonvdveen/feat/paginate-workflow-triggers
feat: paginate workflow asset queries — remove the silent 10,000-asset cap
2026-07-10 19:36:36 +05:30
Varun Raj ManoharanandGitHub ca301ee8e4 Merge pull request #298 from jonvdveen/feat/tag-condition
feat: Tag condition for workflow IF/SWITCH nodes (hierarchical, any/all/none)
2026-07-10 19:35:57 +05:30
Varun Raj ManoharanandGitHub f26ed80e25 Merge pull request #299 from jonvdveen/feat/resolution-condition
feat: Resolution condition for workflow IF/SWITCH nodes (megapixels, short/long edge)
2026-07-10 19:35:14 +05:30
Varun Raj 2be22df94c Merge remote-tracking branch 'origin/prerelease' into feat/resolution-condition 2026-07-10 14:03:40 +00:00
Varun Raj ManoharanandGitHub ba14ab553e Merge pull request #300 from jonvdveen/feat/cleanup-conditions
feat: five library-cleanup conditions — file size, name/path, extension, face count, time of day
2026-07-10 19:31:06 +05:30
Varun Raj 9659c8b36b fix: remove stray argument breaking uploadAssetBuffer call
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.
2026-07-10 14:00:21 +00:00
Varun Raj b66c8218bc fix: parameterize tag contains_any IN-list to close SQL injection vector
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.
2026-07-10 13:59:52 +00:00
jonvdveenandClaude Fable 5 95ede2efd7 feat: Remove Tag workflow action to complement Add Tag
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>
2026-07-02 22:54:43 -06:00
jonvdveenandClaude Fable 5 1431b8523e feat: five library-cleanup conditions for workflow IF/SWITCH nodes
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>
2026-07-02 21:51:02 -06:00
jonvdveenandClaude Fable 5 b473bfdc5d feat: resolution condition for workflow IF/SWITCH nodes (megapixels, short/long edge)
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>
2026-07-02 21:23:57 -06:00
Sai charan GuntupalliandGitHub c1ab7c6099 Merge pull request #295 from jonvdveen/fix/immich-v3-api-key-permissions
fix: API key permissions for Immich v3 (albumAsset.*, tag.asset) and honor "all" in key validation
2026-07-03 08:38:36 +05:30
jonvdveenandClaude Fable 5 37e0606122 feat: tag condition for workflow IF/SWITCH nodes
Adds a "Tag" condition type that filters assets by Immich tags, with
the same match modes as the person condition (has any of / has all of /
has none of). Selected tags also match their child tags via Immich's
tag_closure hierarchy table, mirroring how tag browsing behaves in
Immich itself.

- conditionBuilder: tag case with EXISTS subqueries on tag_asset,
  descendant-inclusive via tag_closure (plus a direct id check so a
  missing self-closure row can't drop matches)
- ConditionEditor: Tag entry + TagPicker multi-select (PersonPicker
  pattern) with tag color chips
- conditionSummary: node label ("Tag any of: ...")
- new GET /api/tags route + client handler + drizzle schema for
  Immich's tag table

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 19:43:02 -06:00
jonvdveenandClaude Fable 5 00aa50ba66 feat: paginate workflow queries so large libraries are fully scanned
All workflow trigger queries were capped at .limit(10000) with no
ORDER BY and no pagination, so on libraries larger than 10k assets an
all_assets ("full library scan") trigger silently evaluated an
arbitrary 10k subset. The IF/SWITCH condition queries had the same cap,
and bulk actions sent every asset id in a single Immich API request.

- resolveAssetTrigger: keyset pagination (ORDER BY id, batches of 5000)
  for all three trigger types
- IF/SWITCH: evaluate conditions against the incoming asset set in
  chunks instead of one capped inArray query
- actionExecutor: call Immich bulk endpoints in batches of 1000 ids

Verified on a 26,063-asset library (Immich v3.0.1): all_assets now
resolves 26,063 (previously 10,000), a person-filter IF matched 813
assets vs 142 under the cap, and a 25,250-asset remove_from_album
branch completed via batched requests.

Fixes #296.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 18:56:25 -06:00
jonvdveenandClaude Fable 5 1cc5d44e76 fix: update API key permissions for Immich v3 granular permissions
Immich v3 split album-membership rights out of album.update into
albumAsset.create / albumAsset.delete, and assigning tags to assets
requires tag.asset. The hardcoded WORKFLOW_PERMISSIONS list predates
this, so auto-generated workflow keys 403 at runtime on Add to Album
(and Add Tag) actions:

    Immich API error 403: {"message":"Missing required permission: albumAsset.create"}

Also, validate-api-key.ts literal-matched required permissions against
the key's permission list, so a key created with the "All" checkbox
(stored as ["all"]) failed validation for every permission — and the
settings page refuses to save a key that fails validation, making it
impossible to work around with a full-permission key.

- Add albumAsset.create, albumAsset.delete, tag.asset to
  WORKFLOW_PERMISSIONS (matching the endpoints actionExecutor.ts
  actually calls: PUT/DELETE /albums/:id/assets, PUT /tags/:id/assets)
- Add albumAsset.create to IMPORT_PERMISSIONS for the same reason
- Treat a granted "all" permission as satisfying all requirements in
  validate-api-key.ts

Follow-up to #294.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 18:26:43 -06:00
Sai charan GuntupalliandGitHub f0d8a88c27 Merge pull request #270 from dgilperez/fix/people-filters-reset-page
fix(people): reset page to 1 on filter change; drop stale onClick on sort menu
2026-07-02 20:57:46 +05:30
Sai charan GuntupalliandGitHub bbf48b9933 Merge pull request #294 from immich-power-tools/fix/immich-v3-compat
v0.22.0
2026-07-02 20:29:43 +05:30
sai80082 46768eea7a fix: update README and asset upload for Immich v3.0 compatibility 2026-07-02 20:26:53 +05:30
sai80082 af4cfceb47 fix: migrate to Immich v3.0 API and DB schema
- Updated albumColumns and AlbumThumbnail components to display placeholder when no thumbnail is available.
- added album_user table (album ownership moved here, role='owner')
- drop asset.deviceId/deviceAssetId and albums.ownerId
- asset.duration varchar -> integer (milliseconds)
- removed isArchived instead use visibility
2026-07-02 20:13:29 +05:30
Sai charan GuntupalliandGitHub 303abc72cd Merge pull request #293 from immich-power-tools/prerelease
Prerelease
2026-06-28 14:02:16 +05:30
Sai charan GuntupalliandGitHub ece805137e Merge pull request #269 from dgilperez/fix/asset-grid-selection-visual
fix(AssetGrid): dim non-selected siblings during selection mode
2026-05-27 16:10:06 +05:30
Sai charan GuntupalliandGitHub d0974679e6 Merge pull request #291 from immich-power-tools/prerelease
v0.21.4
2026-05-21 15:50:21 +05:30
Sai charan GuntupalliandGitHub 49ad6cc55d Merge pull request #290 from tikibozo/fix/albums-by-assets-413
fix(api): raise bodyParser limit on albums-by-assets to handle large duplicate sets
2026-05-21 15:46:41 +05:30
tikibozo e3c7170448 fix(api): raise bodyParser limit on albums-by-assets
The /api/assets/albums-by-assets route accepts a POST body shaped
{ assetIds: string[] } and is invoked from the bulk-duplicate-finder
page with every asset across every duplicate group. With a large
duplicate set the body easily exceeds Next.js's default 1 MB
bodyParser limit and the route returns 413 before the handler runs.

Concrete case: a library with ~14,500 duplicate groups produces a
request body around 1.4-1.8 MB, which is rejected. The page renders
but never finishes loading (413 in browser console).

Fix: add the per-route Next.js bodyParser config to bump the limit
to 10 MB, leaving plenty of headroom for very large libraries while
still bounding worst-case request size.
2026-05-21 00:37:39 -07:00
sai80082 b2aa043ab6 feat: enhance error handling for database and API connections with detailed descriptions 2026-05-05 14:41:36 +05:30
Sai charan GuntupalliandGitHub b8dee930b5 Merge pull request #271 from dgilperez/feat/analytics-heatmap-year-selector
feat(analytics): year selector on asset heatmap
2026-05-01 16:31:56 +05:30
sai80082 54038c2512 feat: enhance release workflow with multi-platform support 2026-05-01 16:17:03 +05:30
Sai charan GuntupalliandGitHub 244dcd4c5c Merge pull request #281 from immich-power-tools/prerelease
Prerelease
2026-05-01 15:22:14 +05:30
David Gil Pérez 9df6d78501 fix(AssetGrid): dim non-selected siblings during selection mode
Upstream already shows the selected ring + clickable check badge on each
thumbnail. This adds the missing piece for dense grids: a slight opacity
reduction on non-selected items while a selection is in progress, so the
selected set stays visible at a glance.

Implemented by passing selectionMode={selectable && selectedIds.length > 0}
from AssetGrid to LazyGridImage; LazyGridImage applies opacity-60 to its
container when selectionMode && !photo.isSelected.
2026-04-27 00:18:27 +02:00
sai80082 9195ceb70d feat: add support for Ente platform integration
- Updated importJobs schema to include 'ente' as a valid platform.
- Enhanced import-shared API to handle Ente shared links, including detection and fetching of albums.
- Introduced new ente.ts module for Ente-specific decryption and API interactions.
- Implemented thumbnail and video handling for Ente assets in import-shared API.
- Created EnteSharedLinkProcessor for processing Ente shared links during import jobs.
- Updated asset interfaces to accommodate Ente-specific encryption artifacts.
- Registered EnteSharedLinkProcessor in the import processors registry.
- Added necessary dependencies (bs58, libsodium-wrappers-sumo) to package.json and bun.lock.
2026-04-24 15:54:40 +05:30
sai80082 c32e38bac7 feat: implement permissions management with new Permission interface and refactor API key handlers 2026-04-24 15:48:34 +05:30
sai80082 28cd757366 feat: replace react-mentions with rc-mentions in FindInput component
- Updated FindInput component to use rc-mentions for better performance and flexibility.
- Removed unused styles related to react-mentions and added new styles for rc-mentions in globals.scss.
- Enhanced search functionality to handle mentions and clear input more effectively.
- Updated FindPage to manage searched queries and loading state.
2026-04-24 15:13:19 +05:30
Sai charan GuntupalliandGitHub 5fbc296931 Merge pull request #264 from dgilperez/fix/ai-drop-hallucinated-future-dates
fix(find): drop hallucinated future-date filters from AI output
2026-04-24 14:29:59 +05:30
Sai charan GuntupalliandGitHub 0756f008c4 Merge pull request #263 from dgilperez/fix/find-auth-use-getuserheaders
fix(find): use getUserHeaders() for Immich smart search auth
2026-04-24 14:27:12 +05:30
Sai charan GuntupalliandGitHub 0017dee23c Merge pull request #256 from immich-power-tools/fix/permissions-validation
feat: enhance API key validation with detailed permission checks and …
2026-04-24 13:54:54 +05:30
Varun Raj ManoharanandGitHub 8c8b68e52b Merge pull request #273 from immich-power-tools/fix-gallery-selection
fix: restore gallery grid selection UI
2026-04-23 15:02:28 +05:30
Varun c99a2c6854 fix: resolve block-scoped variable usage before declaration 2026-04-23 14:53:33 +05:30
Varun 23333c51d5 fix: restore gallery grid selection UI and enable starting selection 2026-04-23 14:48:06 +05:30
David Gil Pérez 3806ac3892 feat(analytics): year selector on asset heatmap
Today the Analytics heatmap hard-codes "last 12 months" both in the SQL
(`WHERE fileCreatedAt >= CURRENT_DATE - INTERVAL '1 YEAR'`) and in the
frontend's month labels loop. Users with multi-year libraries can't look
back at 2018 or 2020 from this view.

Changes:
- API `/api/analytics/statistics/heatmap` now accepts an optional `year`
  query param. With it, the query spans the full calendar year (Jan 1 to
  Dec 31) and `fillMissingDates` walks that range. Without it, the legacy
  rolling 12-month behaviour is preserved (including the "skip the
  repeating current month from last year" seam guard).
- Response shape changed from `[]` to `{ data, year, availableYears }`.
  `availableYears` comes from a DISTINCT EXTRACT(YEAR ...) over the user's
  assets so the UI can render a correct dropdown without guessing.
- Component shows a Select next to the heatmap: "Last 12 months" (default)
  plus one entry per year that actually has assets. Month labels and the
  `weeksPerMonth` computation switch accordingly.
- No migrations needed; drizzle query only.

Callers of `getHeatMapData()` now receive an object — there was only one
caller (the heatmap component) so the change is localised.
2026-04-21 00:26:40 +02:00
David Gil Pérez 49ee4ee4d8 fix(people): reset page to 1 when filters change; drop stale onClick on sort menu
Two small fixes in PeopleFilters:

1. `handleChange` preserved the current page when any other filter changed.
   Symptom: the user is on page 2, types a name in the search box, and the
   list shows zero results until they click back to page 1 manually. The new
   query returns fewer total people, so page=2 is past the end.
   Fix: force page=1 whenever the change doesn't explicitly set `page`.

2. The DropdownMenuTrigger for the sort menu had a leftover
   `onClick={() => handleChange({ page: nextPage })}` — looks copy-pasted
   from the "next page" button right above it. Opening the sort menu would
   silently advance the pagination. Dropped; Radix's DropdownMenuTrigger
   opens the popover by itself.
2026-04-21 00:05:14 +02:00
David Gil Pérez 09decb7198 fix(potential-trips): pre-fill suggested album name reliably
The previous guard 'only set if formData.albumName is empty' lost races against the dialog mount cycle: in some orderings the formData starts empty, the effect fires while config.suggestedAlbumName is undefined, then later when the suggestion arrives the guard refuses to update because prev.albumName == ''. Net effect: the user clicks a trip, selects photos, opens Add to Album, and the Album Name field is empty.

Track the last applied suggestion via a ref and write the value whenever the suggestion changes to a new non-empty string. Doesn't overwrite mid-typing because the suggestion only flips when the user picks a different trip; doesn't overwrite when the user picks the same trip twice; works on mount-after-suggestion (which was the broken path).
2026-04-20 23:19:24 +02:00