Skip to content

feat(server): improve checkAlbumAccess query performance#22467

Merged
mertalev merged 4 commits intoimmich-app:mainfrom
skatsubo:fix/slow-query-check-album-access
Oct 6, 2025
Merged

feat(server): improve checkAlbumAccess query performance#22467
mertalev merged 4 commits intoimmich-app:mainfrom
skatsubo:fix/slow-query-check-album-access

Conversation

@skatsubo
Copy link
Collaborator

Description

Fix slow SQL query in checkAlbumAccess() caused by the array overlap operator array && array:

  • '&&' cannot use indexes, hence PG planner cannot use this condition effectively on asset. So it resorts to SeqScan on album_asset and/or asset (thus more records -> slower the query).
  • '&&' is a little bit more CPU hungry vs simple comparison, although this difference manifests only for large number of IDs in the checked array, so it's of low importance.

Fixes #22389.
In essence, with partner sharing enabled, partner's photos take longer to load (in timeline or direct view) if I have a lot of assets in my albums. (Somewhat counterintuitive at first).

Original auto_explain from #22389 (49K rows in album_asset)
2025-09-26 06:45:49.354 UTC [57] LOG:  duration: 1740.506 ms  plan:
	Query Text: select "asset"."id", "asset"."livePhotoVideoId" from "album" inner join "album_asset" as "albumAssets" on "album"."id" = "albumAssets"."albumsId" inner join "asset" on "asset"."id" = "albumAssets"."assetsId" and "asset"."deletedAt" is null left join "album_user" as "albumUsers" on "albumUsers"."albumsId" = "album"."id" left join "user" on "user"."id" = "albumUsers"."usersId" and "user"."deletedAt" is null where array["asset"."id", "asset"."livePhotoVideoId"] && array[$1]::uuid[]  and ("album"."ownerId" = $2 or "user"."id" = $3) and "album"."deletedAt" is null
	Nested Loop  (cost=18.48..2141.31 rows=1 width=32)
	  ->  Hash Left Join  (cost=18.05..1242.69 rows=198 width=16)
	        Hash Cond: ("albumUsers"."usersId" = "user".id)
	        Filter: ((album."ownerId" = 'cb10fabd-5fce-497e-ab95-f29b6af0d0bb'::uuid) OR ("user".id = 'cb10fabd-5fce-497e-ab95-f29b6af0d0bb'::uuid))
	        ->  Nested Loop Left Join  (cost=16.01..1239.58 rows=395 width=48)
	              ->  Hash Join  (cost=13.83..1198.86 rows=259 width=48)
	                    Hash Cond: ("albumAssets"."albumsId" = album.id)
	                    ->  Seq Scan on album_asset "albumAssets"  (cost=0.00..1054.35 rows=49235 width=32)
	                    ->  Hash  (cost=13.80..13.80 rows=2 width=32)
	                          ->  Seq Scan on album  (cost=0.00..13.80 rows=2 width=32)
	                                Filter: ("deletedAt" IS NULL)
	              ->  Memoize  (cost=2.18..9.29 rows=3 width=32)
	                    Cache Key: album.id
	                    Cache Mode: logical
	                    ->  Bitmap Heap Scan on album_user "albumUsers"  (cost=2.17..9.28 rows=3 width=32)
	                          Recheck Cond: ("albumsId" = album.id)
	                          ->  Bitmap Index Scan on "album_user_albumsId_idx"  (cost=0.00..2.17 rows=3 width=0)
	                                Index Cond: ("albumsId" = album.id)
	        ->  Hash  (cost=2.02..2.02 rows=2 width=16)
	              ->  Seq Scan on "user"  (cost=0.00..2.02 rows=2 width=16)
	                    Filter: ("deletedAt" IS NULL)
	  ->  Index Scan using asset_pkey on asset  (cost=0.42..4.54 rows=1 width=32)
	        Index Cond: (id = "albumAssets"."assetsId")
	        Filter: (("deletedAt" IS NULL) AND (ARRAY[id, "livePhotoVideoId"] && '{50ffba07-2b85-449e-9a9b-3ba1c02482ee}'::uuid[]))

How Has This Been Tested?

Test on my Immich instance

Tested the fix on my local Immich instance: shared partner assets are loading faster.

EXPLAIN with the fix
explain (analyze, verbose, buffers, settings)
select "asset"."id", "asset"."livePhotoVideoId" from "album" 
inner join "album_asset" as "albumAssets" on "album"."id" = "albumAssets"."albumsId" 
inner join "asset" on "asset"."id" = "albumAssets"."assetsId" and "asset"."deletedAt" is null 
left join "album_user" as "albumUsers" on "albumUsers"."albumsId" = "album"."id" 
left join "user" on "user"."id" = "albumUsers"."usersId" and "user"."deletedAt" is null 
where 
      ("asset"."id" in ('d011dd26-f03e-4fe1-8cc4-9244d81a308d') or "asset"."livePhotoVideoId" in ('d011dd26-f03e-4fe1-8cc4-9244d81a308d')) 
  and ("album"."ownerId" = 'dc14ff4e-654b-4edb-9f6c-3a6a0f59f3f1' or "user"."id" = 'dc14ff4e-654b-4edb-9f6c-3a6a0f59f3f1') 
  and "album"."deletedAt" is null;
                                                                                QUERY PLAN                                                                                 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=13.29..29.90 rows=580 width=32) (actual time=0.210..0.213 rows=0 loops=1)
   Output: asset.id, asset."livePhotoVideoId"
   Filter: ((album."ownerId" = 'dc14ff4e-654b-4edb-9f6c-3a6a0f59f3f1'::uuid) OR ("user".id = 'dc14ff4e-654b-4edb-9f6c-3a6a0f59f3f1'::uuid))
   Buffers: shared hit=15
   ->  Nested Loop  (cost=8.99..18.05 rows=1 width=64) (actual time=0.209..0.211 rows=0 loops=1)
         Output: album.id, album."ownerId", asset.id, asset."livePhotoVideoId"
         Buffers: shared hit=15
         ->  Bitmap Heap Scan on public.asset  (cost=8.71..12.73 rows=1 width=32) (actual time=0.095..0.097 rows=1 loops=1)
               Output: asset.id, asset."livePhotoVideoId"
               Recheck Cond: ((asset.id = 'd011dd26-f03e-4fe1-8cc4-9244d81a308d'::uuid) OR (asset."livePhotoVideoId" = 'd011dd26-f03e-4fe1-8cc4-9244d81a308d'::uuid))
               Filter: (asset."deletedAt" IS NULL)
               Heap Blocks: exact=1
               Buffers: shared hit=6
               ->  BitmapOr  (cost=8.71..8.71 rows=1 width=0) (actual time=0.082..0.083 rows=0 loops=1)
                     Buffers: shared hit=5
                     ->  Bitmap Index Scan on "asset_id_stackId_idx"  (cost=0.00..4.42 rows=1 width=0) (actual time=0.050..0.050 rows=1 loops=1)
                           Index Cond: (asset.id = 'd011dd26-f03e-4fe1-8cc4-9244d81a308d'::uuid)
                           Buffers: shared hit=3
                     ->  Bitmap Index Scan on "asset_livePhotoVideoId_idx"  (cost=0.00..4.29 rows=1 width=0) (actual time=0.030..0.030 rows=0 loops=1)
                           Index Cond: (asset."livePhotoVideoId" = 'd011dd26-f03e-4fe1-8cc4-9244d81a308d'::uuid)
                           Buffers: shared hit=2
         ->  Nested Loop  (cost=0.28..5.32 rows=1 width=48) (actual time=0.110..0.111 rows=0 loops=1)
               Output: album.id, album."ownerId", "albumAssets"."assetsId"
               Buffers: shared hit=9
               ->  Seq Scan on public.album  (cost=0.00..1.01 rows=1 width=32) (actual time=0.015..0.020 rows=4 loops=1)
                     Output: album.id, album."ownerId"
                     Filter: (album."deletedAt" IS NULL)
                     Buffers: shared hit=1
               ->  Index Only Scan using album_asset_pkey on public.album_asset "albumAssets"  (cost=0.28..4.30 rows=1 width=32) (actual time=0.021..0.021 rows=0 loops=4)
                     Output: "albumAssets"."albumsId", "albumAssets"."assetsId"
                     Index Cond: (("albumAssets"."albumsId" = album.id) AND ("albumAssets"."assetsId" = asset.id))
                     Heap Fetches: 0
                     Buffers: shared hit=8
   ->  Nested Loop Left Join  (cost=4.30..11.80 rows=3 width=32) (never executed)
         Output: "albumUsers"."albumsId", "user".id
         Inner Unique: true
         ->  Bitmap Heap Scan on public.album_user "albumUsers"  (cost=4.17..11.28 rows=3 width=32) (never executed)
               Output: "albumUsers"."albumsId", "albumUsers"."usersId"
               Recheck Cond: ("albumUsers"."albumsId" = album.id)
               ->  Bitmap Index Scan on "album_user_albumsId_idx"  (cost=0.00..4.17 rows=3 width=0) (never executed)
                     Index Cond: ("albumUsers"."albumsId" = album.id)
         ->  Index Scan using user_pkey on public."user"  (cost=0.13..0.17 rows=1 width=16) (never executed)
               Output: "user".id
               Index Cond: ("user".id = "albumUsers"."usersId")
               Filter: ("user"."deletedAt" IS NULL)
 Planning:
   Buffers: shared hit=346
 Planning Time: 5.202 ms
 Execution Time: 0.462 ms

Synthetic tests

I ran simplified EXPLAINs on synthetic data to compare plans and performance of &&, IN, ANY.

The script below creates a minimal table with 100K records and runs simplified SELECTs using &&, IN, ANY and various number of checked IDs.

Script to test `&&`, `IN`, `ANY` on synthetic data
#!/usr/bin/env bash

image=ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:c44be5f2871c59362966d71eab4268170eb6f5653c0e6170184e72b38ffdf107
container=testdb
db_storage_type=HDD

# explain command and queries to explain
explain="explain (analyze, verbose, buffers)"
# &&    where array[id, "livePhotoVideoId"] && array['10758605-...','d5533ca8-...']::uuid[]
query_overlap="select id, \"livePhotoVideoId\" from a where array[id, \"livePhotoVideoId\"] && array[:uuid_csv]::uuid[]"
# in    where id in ('10758605-...','d5533ca8-...') or "livePhotoVideoId" in ('10758605-...','d5533ca8-...')
query_in="select id, \"livePhotoVideoId\" from a where id in (:uuid_csv) or \"livePhotoVideoId\" in (:uuid_csv)"
# any   where id = any(array['10758605-...','d5533ca8-...']::uuid[]) or "livePhotoVideoId" = any(array['10758605-...','d5533ca8-...']::uuid[])
query_any="select id, \"livePhotoVideoId\" from a where id = any(array[:uuid_csv]::uuid[]) or \"livePhotoVideoId\" = any(array[:uuid_csv]::uuid[])"

# functions
psql_cmd() {
  docker exec -i "$container" psql -U postgres --tuples-only "$@"
}

run_testdb() {
  echo "==== create testdb ===="
  docker run -d --rm --name "$container" -e DB_STORAGE_TYPE="$db_storage_type" -e DB_PASSWORD=postgres "$image"
  sleep 2
  until docker exec "$container" pg_isready ; do
    sleep 1
  done
}

fin_testdb() {
  echo "==== remove testdb ===="
  docker stop "$container"
}

create_dataset() {
  echo "==== create dataset ===="

  known_asset_id=$(docker exec "$container" cat /proc/sys/kernel/random/uuid)
  known_live_id=$(docker exec "$container" cat /proc/sys/kernel/random/uuid)

  query_create=$(cat <<EOF
    CREATE UNLOGGED TABLE a (
      id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
      "livePhotoVideoId" uuid
    );

    -- every 10th record is live photo
    INSERT INTO a ("livePhotoVideoId")
    SELECT CASE WHEN random() < 0.1 THEN gen_random_uuid() END
    FROM generate_series(1, 99998);

    -- insert known IDs to be queried later
    INSERT INTO a (id) VALUES ('$known_asset_id');
    INSERT INTO a ("livePhotoVideoId") VALUES ('$known_live_id');

    CREATE INDEX "a_livePhotoVideoId_idx" ON a USING btree ("livePhotoVideoId");
    ANALYZE a;

    -- just bunch of uuids to be queried
    CREATE UNLOGGED TABLE id (
      id uuid DEFAULT gen_random_uuid() PRIMARY KEY
    );
    INSERT INTO id 
    SELECT
    FROM generate_series(1, 5000);
EOF
)
  psql_cmd <<<"$query_create"
}

gen_uuid_csv() {
  uuid_count=$1
  if [[ $uuid_count -eq 1 ]]; then
    uuid_csv="'$known_asset_id'"
  else
    query_uuid="select :'known_asset_id' id
      union (select :'known_live_id' limit (:uuid_count - 1))
      union (select id::text from id limit (:uuid_count - 2))
      limit (:uuid_count)"
    query_uuid_csv="select string_agg('''' || id::text || '''', ', ') from ($query_uuid) t"
    uuid_csv=$(psql_cmd -Aq -v known_asset_id="$known_asset_id" -v known_live_id="$known_live_id" -v uuid_count="$uuid_count" <<<"$query_uuid_csv")
  fi
}

explain() {
  uuid_count=$1
  gen_uuid_csv "$uuid_count"

  echo "==== explain: 'in' uuid_count: $uuid_count ===="
  psql_cmd -v uuid_csv="$uuid_csv" <<<"$explain $query_in"

  echo "==== explain: 'any' uuid_count: $uuid_count ===="
  psql_cmd -v uuid_csv="$uuid_csv" <<<"$explain $query_any"

  echo "==== explain: '&&' uuid_count: $uuid_count ===="
  psql_cmd -v uuid_csv="$uuid_csv" <<<"$explain $query_overlap"
}

# main
run_testdb
create_dataset
explain 1
explain 2
explain 50
explain 500 | cut -c-200
explain 3000 | cut -c-200
fin_testdb
Output: compare && / IN / ANY for 1 checked ID
==== explain: 'in' uuid_count: 1 ====
 Bitmap Heap Scan on public.a  (cost=8.73..16.40 rows=2 width=32) (actual time=0.029..0.030 rows=1 loops=1)
   Output: id, "livePhotoVideoId"
   Recheck Cond: ((a.id = 'a9e7388c-229a-4b92-88e1-bdd993026017'::uuid) OR (a."livePhotoVideoId" = 'a9e7388c-229a-4b92-88e1-bdd993026017'::uuid))
   Heap Blocks: exact=1
   Buffers: shared hit=4 read=2
   ->  BitmapOr  (cost=8.73..8.73 rows=2 width=0) (actual time=0.027..0.027 rows=0 loops=1)
         Buffers: shared hit=3 read=2
         ->  Bitmap Index Scan on a_pkey  (cost=0.00..4.43 rows=1 width=0) (actual time=0.019..0.019 rows=1 loops=1)
               Index Cond: (a.id = 'a9e7388c-229a-4b92-88e1-bdd993026017'::uuid)
               Buffers: shared hit=3
         ->  Bitmap Index Scan on "a_livePhotoVideoId_idx"  (cost=0.00..4.30 rows=1 width=0) (actual time=0.008..0.008 rows=0 loops=1)
               Index Cond: (a."livePhotoVideoId" = 'a9e7388c-229a-4b92-88e1-bdd993026017'::uuid)
               Buffers: shared read=2
 Planning:
   Buffers: shared hit=73 read=1
 Planning Time: 0.145 ms
 Execution Time: 0.076 ms

==== explain: 'any' uuid_count: 1 ====
 Bitmap Heap Scan on public.a  (cost=8.73..16.39 rows=2 width=32) (actual time=0.025..0.025 rows=1 loops=1)
   Output: id, "livePhotoVideoId"
   Recheck Cond: ((a.id = ANY ('{a9e7388c-229a-4b92-88e1-bdd993026017}'::uuid[])) OR (a."livePhotoVideoId" = ANY ('{a9e7388c-229a-4b92-88e1-bdd993026017}'::uuid[])))
   Heap Blocks: exact=1
   Buffers: shared hit=6
   ->  BitmapOr  (cost=8.73..8.73 rows=2 width=0) (actual time=0.023..0.023 rows=0 loops=1)
         Buffers: shared hit=5
         ->  Bitmap Index Scan on a_pkey  (cost=0.00..4.43 rows=1 width=0) (actual time=0.020..0.021 rows=1 loops=1)
               Index Cond: (a.id = ANY ('{a9e7388c-229a-4b92-88e1-bdd993026017}'::uuid[]))
               Buffers: shared hit=3
         ->  Bitmap Index Scan on "a_livePhotoVideoId_idx"  (cost=0.00..4.30 rows=1 width=0) (actual time=0.002..0.002 rows=0 loops=1)
               Index Cond: (a."livePhotoVideoId" = ANY ('{a9e7388c-229a-4b92-88e1-bdd993026017}'::uuid[]))
               Buffers: shared hit=2
 Planning:
   Buffers: shared hit=133 read=1
 Planning Time: 0.216 ms
 Execution Time: 0.063 ms

==== explain: '&&' uuid_count: 1 ====
 Seq Scan on public.a  (cost=0.00..1810.00 rows=500 width=32) (actual time=10.956..10.956 rows=1 loops=1)
   Output: id, "livePhotoVideoId"
   Filter: (ARRAY[a.id, a."livePhotoVideoId"] && '{a9e7388c-229a-4b92-88e1-bdd993026017}'::uuid[])
   Rows Removed by Filter: 99999
   Buffers: shared hit=567
 Planning:
   Buffers: shared hit=120
 Planning Time: 0.176 ms
 Execution Time: 10.984 ms
Output: compare && / IN / ANY for 500 checked IDs
==== explain: 'in' uuid_count: 500 ====
 Seq Scan on public.a  (cost=2.50..2562.50 rows=998 width=32) (actual time=6.833..6.834 rows=2 loops=1)
   Output: id, "livePhotoVideoId"
   Filter: ((a.id = ANY ('{1b3e299a-15c7-4b01-b9d7-fdc396913f7d,78fe5bbb-de0a-48d2-a24d-533b3bd537b8,959276f6-3cb3-4d24-a267-f6c4767e5b03,a2707f03-4927-4380-8afb-4565826210a8,7619425c-17e2-4551-8163-3
   Rows Removed by Filter: 99998
   Buffers: shared hit=560
 Planning:
   Buffers: shared hit=137
 Planning Time: 0.429 ms
 Execution Time: 6.862 ms

==== explain: 'any' uuid_count: 500 ====
 Seq Scan on public.a  (cost=2.50..2562.50 rows=998 width=32) (actual time=6.857..6.858 rows=2 loops=1)
   Output: id, "livePhotoVideoId"
   Filter: ((a.id = ANY ('{1b3e299a-15c7-4b01-b9d7-fdc396913f7d,78fe5bbb-de0a-48d2-a24d-533b3bd537b8,959276f6-3cb3-4d24-a267-f6c4767e5b03,a2707f03-4927-4380-8afb-4565826210a8,7619425c-17e2-4551-8163-3
   Rows Removed by Filter: 99998
   Buffers: shared hit=560
 Planning:
   Buffers: shared hit=137
 Planning Time: 0.416 ms
 Execution Time: 6.886 ms

==== explain: '&&' uuid_count: 500 ====
 Seq Scan on public.a  (cost=0.00..1810.00 rows=91843 width=32) (actual time=211.565..211.569 rows=2 loops=1)
   Output: id, "livePhotoVideoId"
   Filter: (ARRAY[a.id, a."livePhotoVideoId"] && '{1b3e299a-15c7-4b01-b9d7-fdc396913f7d,78fe5bbb-de0a-48d2-a24d-533b3bd537b8,959276f6-3cb3-4d24-a267-f6c4767e5b03,a2707f03-4927-4380-8afb-4565826210a8,7
   Rows Removed by Filter: 99998
   Buffers: shared hit=567
 Planning:
   Buffers: shared hit=120
 Planning Time: 0.266 ms
 Execution Time: 211.606 ms

Related

I've noticed a recent PR with access check performance improvements - #21827. Seemingly it does not overlap with this PR because it applies the changes at higher level and do not touch checkAlbumAccess internals.

Out of scope

A similar clause in checkSharedLinkAccess()

.where(
  sql`array["asset"."id", "asset"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"]`,
  '&&',
  sql`array[${sql.join([...assetIds])}]::uuid[] `,
)

Please describe to which degree, if any, an LLM was used in creating this pull request.

To research/compare &&, IN(), ANY() in Postgres.

@mertalev mertalev merged commit 9da138e into immich-app:main Oct 6, 2025
47 checks passed
Chaoscontrol pushed a commit to Chaoscontrol/immich that referenced this pull request Oct 14, 2025
…22467)

* Fix slow SQL query in checkAlbumAccess caused by the array overlap operator &&

* Update access.repository.sql

* Rewrite the query to pass assetIds once as a single array parameter
mertalev added a commit that referenced this pull request Oct 14, 2025
* feat: show per-user contribution counts on shared albums

Add API support and UI display for per-user asset contribution
counts on shared albums:
- server: add ContributorCountResponseDto and repository method to
  aggregate counts per user (excluding deleted assets), expose via
  album response only when shared and counts > 0
- web: display contributor counts in Album Users modal next to each
  member’s role

This helps users understand participation levels in shared albums.

* Add ContributorCountResponseDto and expose contributorCounts
on AlbumResponseDto in OpenAPI spec. Regenerate TypeScript SDK
and mobile OpenAPI clients to include new types.

No breaking changes; fields are additive.

* fix: shrink age view to fit and not overflow (#22405)

Co-authored-by: Alex <alex.tran1502@gmail.com>

* chore: post release tasks (#22587)

* chore: clean auth-user entity on reset (#22583)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>

* fix: mitigate database lock scenario when running full sync in splash screen page (#22608)

* fix: improve sync backup error indicator   (#22527)

* fix: improve sync indicator error

* prefer backup disabled icon before error

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* fix: bottom navigation bar overlay sheet info (#22610)

* fix: respect storage indicator setting (#22596)

* fix: respect storage indicator size setting

* remove black bar on the bottom of the setting scaffold page

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* fix: do not run multiple engines on cold startup (#22518)

fix: do not run multiple engines on app startup

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* fix: album selector in favorite view (#22612)

* chore(web): update translations (#22486)

Translate-URL: https://hosted.weblate.org/projects/immich/immich/ar/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/az/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/bg/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ca/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/cs/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/da/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/de/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/el/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/es/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/fr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/he/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/hu/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/it/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/kn/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ko/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lv/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ml/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/nl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pt/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ro/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ru/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sk/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sv/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ta/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/tr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_SIMPLIFIED/
Translation: Immich/immich

Co-authored-by: Arthur Bols <arthur@bols.dev>
Co-authored-by: Ben Kim <benkim1129@gmail.com>
Co-authored-by: César Gómez <cegomez@gmail.com>
Co-authored-by: DR <weblate-kavita.snowflake668@slmail.me>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Emil Friis Osmann <Emilfriisosmann@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Godwin T <godwintgn@protonmail.com>
Co-authored-by: Hristo T <hristotarnev@gmail.com>
Co-authored-by: Hurricane-32 <rodrigorimo@hotmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: KecskeTech <teonyitas@gmail.com>
Co-authored-by: Kiril Panayotov <eccyboo@protonmail.com>
Co-authored-by: Liviu Roman <contact@liviuroman.com>
Co-authored-by: Lorenzo <artale.lorenzo@outlook.it>
Co-authored-by: Marcelo Popper Costa <marcelo_popper@hotmail.com>
Co-authored-by: Matjaž T <matjaz@moj-svet.si>
Co-authored-by: Miryusif Rahimov <miryusifrahimov@gmail.com>
Co-authored-by: Msaood <msaood@msaood.com>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Pedro Vendeira <vendeira.pedro@gmail.com>
Co-authored-by: PontusÖsterlindh <pontus@osterlindh.com>
Co-authored-by: Rahees <ahdrahees.dev@gmail.com>
Co-authored-by: Sandeep R <sandeep1891995@gmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Tino Altmann <usinggrant@hotmail.de>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Vegard Fladby <vegard@fladby.org>
Co-authored-by: anton garcias <isaga.percompartir@gmail.com>
Co-authored-by: chamdim <chamdim@protonmail.com>
Co-authored-by: longlarry <weblate.gm@tuta.io>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: swever <swever@users.noreply.hosted.weblate.org>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>

* chore: version v2.0.1

* fix(docs): link to immich docs does not lead correctly to docs (#22687)

* fix(server): fix chunking Postgres query parameters (#22684)

* feat(server): improve checkAlbumAccess query performance (#22467)

* Fix slow SQL query in checkAlbumAccess caused by the array overlap operator &&

* Update access.repository.sql

* Rewrite the query to pass assetIds once as a single array parameter

* chore: mark VSCode tasks as background tasks (#22631)

VSCode expect tasks that aren't marked as background tasks to finish eventually. That's not how a dev-server is supposed to work, we expect it to run for basically infinite time.

By marking those tasks as background tasks, VSCode stops showing the infinite loading spinner on those processes.

* fix(ml): Resolve IPv6 startup crash and healthcheck failure (#22387)

* fix(ml): Resolve IPv6 startup crash and healthcheck failure

Fixes #13782

* fix(ml): updated the fix to use the std lib

* Apply code formatting to __main__.py

* fix(server): override reserved color metadata for video thumbnails (#22348)

override reserved metadata

* fix(mobile): trash description cut off (#22662)

* fix(mobile): empty album description does not save (#22649)

* fix(mobile): video player using ref after disposal (#21843)

check if disposed

* docs: add job order diagram (#22673)

* docs: add job order diagram

* wording

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>

* fix: missing responsive calculation in UserPageLayout (#22455)

* fix: use full-size image for non-web-compatible panoramas (#20359)

* fix(web): use full-size image for non-web-compatible panoramas

* always generate full-size image for panoramas

* add unit test

* fix formatting

---------

Co-authored-by: gergo= <gergo@pitty.hu>

* chore: update cli docs to pnpm (#22702)

update cli docs to pnpm

* chore(web): upgrade ESLint and plugins (#22495)

* chore(web): upgrade ESLint and plugins, simplify linting configuration

- Update eslint from ^9.18.0 to ^9.36.0
- Update eslint plugins:
  - eslint-plugin-svelte: ^3.9.0 → ^3.12.4
  - eslint-plugin-unicorn: ^60.0.0 → ^61.0.2
  - svelte-eslint-parser: ^1.2.0 → ^1.3.3
  - typescript-eslint: ^8.28.0 → ^8.45.0
- Remove eslint-p dependency in favor of native eslint concurrency
- Add unicorn/no-array-sort rule exception
- Update linting scripts to use eslint's native --concurrency flag
- Update Makefile and mise.toml to reflect simplified lint commands
- Update GitHub Actions workflow to use standard pnpm lint command

* pnpm dedupe

---------

Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>

* fix(web): do not notify on patch releases (#22591)

* chore: post release tasks (#22616)

* fix: hide view in timeline button on local timeline (#22713)

* chore(server): support vectorchord 0.5.x (#21602)

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>

* fix: Fix issue fail to download iOS live photos (#22708)

Co-authored-by: bwees <brandonwees@gmail.com>

* fix(docs): Remove immich_remove_offline_files as no longer functional (#21774)

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Brandon Wees <brandonwees@gmail.com>

* fix(mobile): closing editor goes back to main page (#22647)

Co-authored-by: bwees <brandonwees@gmail.com>

* docs: update TrueNAS migration instructions (#22463)

Co-authored-by: bo0tzz <git@bo0tzz.me>
Co-authored-by: Nicholas Flamy <30300649+NicholasFlamy@users.noreply.github.com>

* docs: update Synology install guide (#21996)

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>

* fix: improve the selected sidebar item text color in dark mode (#22640)

* chore(deps): update redis:6.2-alpine docker digest to 2185e74 (#22718)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore: update devcontainers for trixie, devenv changes (#22194)

* fix(deps): update dependency device_info_plus to v12 (#22724)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency flutter to v3.35.5 (#22720)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update github-actions (#22721)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: --no-git-checks on pnpm publish (#22715)

* fix: --no-git-checks on sdk publish

* fix: --no-git-checks on cli publish

* refactor(web): Clarify property names in Timeline and Scrubber (#22265)

refactor(web): Clarify property names in Timeline and Scrubber

  Renamed properties across Timeline/Scrubber components for clarity:
  - scrubOverallPercent → timelineScrollPercent
  - scrubberMonthPercent → viewportTopMonthScrollPercent
  - scrubberMonth → viewportTopMonth
  - leadout → isInLeadOutSection

  Additional changes:
  - Updated ScrubberListener signature to accept object parameter
  - Added detailed JSDoc comments for all Scrubber props
  - Fixed callback invocations to use new object syntax
  - Aligned Timeline's local state variables with Scrubber prop names

* fix: promote to foreground service before starting engine (#22517)

fix: show notification from native

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* refactor(web): extract timeline keyboard actions into separate component (#22266)

refactor(web): extract timeline keyboard actions into separate component

Extracts keyboard shortcuts and related functionality from Timeline component into a dedicated TimelineKeyboardActions component for better separation of concerns and maintainability.

* feat: make skeleton title optional (#22396)

feat: skeleton title is optional

feat: skeleton title optional

* refactor(web): extract asset viewer logic from Timeline into TimelineAssetViewer component (#22268)

refactor(web): extract asset viewer logic from Timeline into TimelineAssetViewer component

- Extracted asset viewer navigation and action handling logic from Timeline.svelte into a dedicated TimelineAssetViewer component
- Reduces Timeline.svelte complexity by ~150 lines and improves separation of concerns
- No functional changes - purely a refactoring to improve code organization

## Changes
- Created new TimelineAssetViewer.svelte component containing all asset viewer-related logic
- Moved handlePrevious, handleNext, handleRandom, handleClose, handlePreAction, and handleAction methods
- Timeline.svelte now only passes required props to the new component
- Maintained all existing functionality including navigation, asset actions, and stack management

* chore: track full actions/cache version in comment (#22359)

* fix(ml): ipv6 check (#22735)

* chore(deps): cache pnpm dependencies in prod build (#22555)

* cache pnpm dependencies

use different ids to be safe

unnecessary lines

* use buildcache folder

* chore: use isar immich fork (#22738)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>

* fix: bottom sheet blank with local assets that have remote counterparts (#22743)

* chore(deps): update dependency @types/node to ^22.18.8 (#22719)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency nodemailer to v7.0.7 [security] (#22740)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update dependency connectivity_plus to v7 (#22723)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* chore: use hosted isar flutter libs (#22757)

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>

* fix: skip local only assets in move to lock action (#22728)

* fix:prefer trashing to deletions

* skip local only assets in move to lock action

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* fix: brief flashing when swiping from video (#22187)

* fix(web): Uniform random distribution during shuffle (#19902)

feat: better random distribution

* fix: persist search page scroll offset between rebuilds (#22733)

fix: persist search scroll between rebuilds

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* docs: add some external library notes (#22776)

* feat(web): seconds and milliseconds in timestamps (#20337)

* fix(web): seconds in timestamps

* changed date-input step to provide millisecond precision

* feat(cli): add debug development config (#22712)

* add debug and change ts-node with tsx

* update pr changes

* update pnpm-lock

* remove ts-node from readme

* typo

* resolve conflicts

* remove tsx

* launch from dist

* add preLaunchTask

* update readme

* undo main in package.json

* remove typo

* Apply suggestion from @bwees

Co-authored-by: Brandon Wees <brandonwees@gmail.com>

* revert pnpm-lock changes

* @jrasm91 suggestions

* chore: run node with source maps

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
Co-authored-by: Brandon Wees <brandonwees@gmail.com>

* docs: add Immich-Stack to community-projects (#21563)

docs: add Immich Stack community project

Co-authored-by: Jason Rasmussen <jason@rasm.me>

* feat(web): Add upload to stack action (#19842)

* feat(web): Add upload to stack action

* Event handling and translation

* Update asset viewer instead

* lint, improve upload return type

* Add suggestions from code review

* Resolve merge conflicts

* Apply suggestions from code review

* feat(server): add `immich.users.total` metric (#21780)

* Add immich.users.total metric

* Fix tests & one lint error

* Lint

* Fix SQL Schema checks

* Fix nit

* Use workers argument in OnEvent hook and remove condition from method body

* feat(docs): add zh_TW Traditional Chinese version README (#22703)

docs: add zh_TW Traditional Chinese version README

* chore: ignore renovate major updates for postgres image (#22764)

* fix: remove postgres exclude datasource match (#22811)

* chore(deps): update github-actions (major) (#22810)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: revert terragrunt-action bump (#22812)

* chore: don't enforce runes (#22813)

* chore(deps): update base-image to v202510092146 (major) (#22818)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update typescript-projects (#22809)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>

* fix: only cast to device if the asset is a RemoteAsset (#22805)

* feat: (perf) remove scroll compensation (#22837)

* fix(deps): update dependency happy-dom to v20 [security] (#22846)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update github-actions (#22793)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: various typos (#22867)

Found via `codespell -q 3 -S "*.svg,./i18n,./docs/package-lock.json,./readme_i18n,./mobile/assets/i18n" -L afterall,devlop,finaly,inout,nd,optin,renderd,sade`

* fix: ios skip posting hash response after detached from engine (#22695)

* skip posting message after detached from engine

* review changes

* cancel plugin before destroying engine

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.3.0 docker digest to 6f3e9d2 (#22912)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 docker digest to bcf6335 (#22913)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: re-add scroll compensation (efficiently) (#22848)

* fix: re-add scroll compensation (efficient)

* Rename showSkeleton to invisible. Adjust skeleton margins, invisible support.

* Fix faulty logic, simplify

* Calculate ratios and determine compensation strategy: height comp for above/partiality visible, month-scroll comp within a fully visible month.

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>

* fix: shared album control permissions (#22435)

* fix: shared album control permissions

* fix: properly display "add photos"

* fix: dont allow modification of album order

* fix: album title/description edit from app bar

* chore: code review changes

* chore: format translations

* chore: lintings

* fix: show dialog before delete local action (#22280)

* fix: show dialog on delete local action

# Conflicts:
#	mobile/lib/repositories/asset_media.repository.dart

* button style

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* fix(deps): update dependency kysely-postgres-js to v3 (#22924)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update redis:6.2-alpine docker digest to 77697a7 (#22915)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update typescript-projects (#22918)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>

* feat: local album events notification (#22817)

* feat: local album events notification

* pr feedback

* show number of unread notification

* chore: refactor show view in timeline button (#22894)

* chore: refactor show view in timeline button

This refactor includes changes to notify asset viewer about where an asset was shown from.

* chore: realized I could just pull from the timelineProvider instead of storing it in the asset viewer state

* chore: rename enum to TimelineOrigin and update members

* fix: update isOwner condition

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>

* chore(web): update translations (#22623)

Translate-URL: https://hosted.weblate.org/projects/immich/immich/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ar/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/be/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/bn/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ca/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/cs/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/da/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/de/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/el/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/es/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/et/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/fi/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/fr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/gl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/he/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/hi/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/hr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/id/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/it/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ja/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ka/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lv/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/mr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/nl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pa/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pt/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ro/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ru/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sk/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sr_Latn/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sv/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ta/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/tr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/uk/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_SIMPLIFIED/
Translation: Immich/immich

Co-authored-by: Abhijeet Bonde <abhijeetbonde19@gmail.com>
Co-authored-by: Adam Uchmanowicz <auchmanowicz@gmail.com>
Co-authored-by: Adrian Hermida <adrian.hermida.baloira@gmail.com>
Co-authored-by: Aleksa Milošević <akimaki15@gmail.com>
Co-authored-by: Amin <amnsharif@gmail.com>
Co-authored-by: AndreiP28 <andreiprica28@gmail.com>
Co-authored-by: António Santos <antoniomsantos99@gmail.com>
Co-authored-by: Asger Mogensen <asgermog@gmail.com>
Co-authored-by: Christoph Auer <Christoph.Auer@pilsheim.de>
Co-authored-by: Denis Pacquier <denis.pacquier@gmail.com>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Eetu Mäenpää <me@eetumaenpaa.fi>
Co-authored-by: Felipe Garcia <garcia.o.felipe@gmail.com>
Co-authored-by: Filip Joković <filip@jokovic.dev>
Co-authored-by: Hurricane-32 <rodrigorimo@hotmail.com>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: Jason Song <songpeiheng@gmail.com>
Co-authored-by: Javier Villanueva García <jvg2203@gmail.com>
Co-authored-by: Jordy H <jordy@hoebergen.net>
Co-authored-by: Jorge Montejo <jorgemon.lopez@gmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Konstantinos D <kdemer@yahoo.com>
Co-authored-by: Leo Bottaro <github@leobottaro.com>
Co-authored-by: Linerly <linerly@proton.me>
Co-authored-by: Liviu Roman <contact@liviuroman.com>
Co-authored-by: Lorenz Baum <LorenzBaum@gmx.de>
Co-authored-by: Lukas Konsin <lukaskonsin@proton.me>
Co-authored-by: Mandeep <mandeeps708@gmail.com>
Co-authored-by: Marc Casillas <mcasillassu@gmail.com>
Co-authored-by: Marcelo Popper Costa <marcelo_popper@hotmail.com>
Co-authored-by: MatijaThe245th <matija245matakovic@gmail.com>
Co-authored-by: Matjaž T <matjaz@moj-svet.si>
Co-authored-by: Mees Frensel <meesfrensel@gmail.com>
Co-authored-by: Mirko <itzmirko@itzmirko.it>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Oleksandr Yurov <oyurov@icloud.com>
Co-authored-by: Orkun Sürel <orkunsurel@gmail.com>
Co-authored-by: Peter Dave Hello <hsu@peterdavehello.org>
Co-authored-by: Philipp Burndorfer <phi.bur@gmx.at>
Co-authored-by: Prasanth Baskar <bupdprasanth@gmail.com>
Co-authored-by: Roman Zhukov <Softver161@gmail.com>
Co-authored-by: Sayan Goswami <goswami.sayan47@gmail.com>
Co-authored-by: Sergey Katsubo <skatsubo@gmail.com>
Co-authored-by: Simon Bierwald <simon.bierwald@gmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Taiki M <vexingly-many-mace@duck.com>
Co-authored-by: Theodore Zhvania <zhvania@ted.ge>
Co-authored-by: Tim De Meyer <demeyer.tim@gmail.com>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Valentino Harpa <valen.ginga@gmail.com>
Co-authored-by: Vegard Fladby <vegard@fladby.org>
Co-authored-by: Willem Schipper <git@willem.page>
Co-authored-by: Yago Raña Gayoso <yago.rana.gayoso@gmail.com>
Co-authored-by: Zurab Sajaia <vavalomi@hotmail.com>
Co-authored-by: albanobattistella <albanobattistella@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: dark&white <darkwhite@users.noreply.hosted.weblate.org>
Co-authored-by: eav5jhl0 <eav5jhl0@users.noreply.hosted.weblate.org>
Co-authored-by: findussoft <sella_violett_8i@icloud.com>
Co-authored-by: kiwinho <kiwicaja@gmail.com>
Co-authored-by: millallo <millallo@tiscali.it>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: rokon001 <rnacc3579@gmail.com>
Co-authored-by: vaibhav kumar <catvaku@gmail.com>
Co-authored-by: waclaw66 <waclaw66@seznam.cz>
Co-authored-by: Максим Горпиніч <gorpinicmaksim0@gmail.com>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>

* chore: version v2.1.0

* refactor

* question marks are the enemy

* refactor count map

* update readme

* e2e

* count of 0 is impossible

* useless async

---------

Co-authored-by: Chaoscontrol <6642238+Chaoscontrol@users.noreply.github.com>
Co-authored-by: Brandon Wees <brandonwees@gmail.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Weblate (bot) <hosted@weblate.org>
Co-authored-by: Arthur Bols <arthur@bols.dev>
Co-authored-by: Ben Kim <benkim1129@gmail.com>
Co-authored-by: César Gómez <cegomez@gmail.com>
Co-authored-by: DR <weblate-kavita.snowflake668@slmail.me>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Emil Friis Osmann <Emilfriisosmann@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Godwin T <godwintgn@protonmail.com>
Co-authored-by: Hristo T <hristotarnev@gmail.com>
Co-authored-by: Hurricane-32 <rodrigorimo@hotmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: KecskeTech <teonyitas@gmail.com>
Co-authored-by: Kiril Panayotov <eccyboo@protonmail.com>
Co-authored-by: Liviu Roman <contact@liviuroman.com>
Co-authored-by: Lorenzo <artale.lorenzo@outlook.it>
Co-authored-by: Marcelo Popper Costa <marcelo_popper@hotmail.com>
Co-authored-by: Matjaž T <matjaz@moj-svet.si>
Co-authored-by: Miryusif Rahimov <miryusifrahimov@gmail.com>
Co-authored-by: Msaood <msaood@msaood.com>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Pedro Vendeira <vendeira.pedro@gmail.com>
Co-authored-by: PontusÖsterlindh <pontus@osterlindh.com>
Co-authored-by: Rahees <ahdrahees.dev@gmail.com>
Co-authored-by: Sandeep R <sandeep1891995@gmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Tino Altmann <usinggrant@hotmail.de>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Vegard Fladby <vegard@fladby.org>
Co-authored-by: anton garcias <isaga.percompartir@gmail.com>
Co-authored-by: chamdim <chamdim@protonmail.com>
Co-authored-by: longlarry <weblate.gm@tuta.io>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: swever <swever@users.noreply.hosted.weblate.org>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Xavier Dupuis <xavier0978@hotmail.fr>
Co-authored-by: Sergey Katsubo <skatsubo@gmail.com>
Co-authored-by: Adrian Jost <22987140+adrianjost@users.noreply.github.com>
Co-authored-by: Cokodayo <78474654+CaptainJack2491@users.noreply.github.com>
Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Yaros <thedj.launchpadder.dmx512@gmail.com>
Co-authored-by: USBAkimbo <71508071+USBAkimbo@users.noreply.github.com>
Co-authored-by: Min Idzelis <min123@gmail.com>
Co-authored-by: grgergo <gergo_g@proton.me>
Co-authored-by: gergo= <gergo@pitty.hu>
Co-authored-by: Jorge Montejo <jorgemon.lopez@gmail.com>
Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
Co-authored-by: Jason Rasmussen <jason@rasm.me>
Co-authored-by: Diogo Correia <me@diogotc.com>
Co-authored-by: CuberL <liaoziyue10@gmail.com>
Co-authored-by: Xantin <56741168+Xiticks@users.noreply.github.com>
Co-authored-by: bo0tzz <git@bo0tzz.me>
Co-authored-by: Nicholas Flamy <30300649+NicholasFlamy@users.noreply.github.com>
Co-authored-by: TDR001 <redp50@outlook.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Saschl <19493808+Saschl@users.noreply.github.com>
Co-authored-by: Pascal Sommer <Pascal-So@users.noreply.github.com>
Co-authored-by: kaziu687 <kaziu687@gmail.com>
Co-authored-by: Qhilm <3350433+Qhilm@users.noreply.github.com>
Co-authored-by: Sebastian Schneider <sese.tailor@gmx.net>
Co-authored-by: Tushar Harsora <tusharharsora95@gmail.com>
Co-authored-by: Peter Dave Hello <hsu@peterdavehello.org>
Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
Co-authored-by: luzpaz <luzpaz@users.noreply.github.com>
Co-authored-by: Abhijeet Bonde <abhijeetbonde19@gmail.com>
Co-authored-by: Adam Uchmanowicz <auchmanowicz@gmail.com>
Co-authored-by: Adrian Hermida <adrian.hermida.baloira@gmail.com>
Co-authored-by: Aleksa Milošević <akimaki15@gmail.com>
Co-authored-by: Amin <amnsharif@gmail.com>
Co-authored-by: AndreiP28 <andreiprica28@gmail.com>
Co-authored-by: António Santos <antoniomsantos99@gmail.com>
Co-authored-by: Asger Mogensen <asgermog@gmail.com>
Co-authored-by: Christoph Auer <Christoph.Auer@pilsheim.de>
Co-authored-by: Denis Pacquier <denis.pacquier@gmail.com>
Co-authored-by: Eetu Mäenpää <me@eetumaenpaa.fi>
Co-authored-by: Felipe Garcia <garcia.o.felipe@gmail.com>
Co-authored-by: Filip Joković <filip@jokovic.dev>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: Jason Song <songpeiheng@gmail.com>
Co-authored-by: Javier Villanueva García <jvg2203@gmail.com>
Co-authored-by: Jordy H <jordy@hoebergen.net>
Co-authored-by: Konstantinos D <kdemer@yahoo.com>
Co-authored-by: Leo Bottaro <github@leobottaro.com>
Co-authored-by: Linerly <linerly@proton.me>
Co-authored-by: Lorenz Baum <LorenzBaum@gmx.de>
Co-authored-by: Lukas Konsin <lukaskonsin@proton.me>
Co-authored-by: Mandeep <mandeeps708@gmail.com>
Co-authored-by: Marc Casillas <mcasillassu@gmail.com>
Co-authored-by: MatijaThe245th <matija245matakovic@gmail.com>
Co-authored-by: Mees Frensel <meesfrensel@gmail.com>
Co-authored-by: Mirko <itzmirko@itzmirko.it>
Co-authored-by: Oleksandr Yurov <oyurov@icloud.com>
Co-authored-by: Orkun Sürel <orkunsurel@gmail.com>
Co-authored-by: Philipp Burndorfer <phi.bur@gmx.at>
Co-authored-by: Prasanth Baskar <bupdprasanth@gmail.com>
Co-authored-by: Roman Zhukov <Softver161@gmail.com>
Co-authored-by: Sayan Goswami <goswami.sayan47@gmail.com>
Co-authored-by: Simon Bierwald <simon.bierwald@gmail.com>
Co-authored-by: Taiki M <vexingly-many-mace@duck.com>
Co-authored-by: Theodore Zhvania <zhvania@ted.ge>
Co-authored-by: Tim De Meyer <demeyer.tim@gmail.com>
Co-authored-by: Valentino Harpa <valen.ginga@gmail.com>
Co-authored-by: Willem Schipper <git@willem.page>
Co-authored-by: Yago Raña Gayoso <yago.rana.gayoso@gmail.com>
Co-authored-by: Zurab Sajaia <vavalomi@hotmail.com>
Co-authored-by: albanobattistella <albanobattistella@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: dark&white <darkwhite@users.noreply.hosted.weblate.org>
Co-authored-by: eav5jhl0 <eav5jhl0@users.noreply.hosted.weblate.org>
Co-authored-by: findussoft <sella_violett_8i@icloud.com>
Co-authored-by: kiwinho <kiwicaja@gmail.com>
Co-authored-by: millallo <millallo@tiscali.it>
Co-authored-by: rokon001 <rnacc3579@gmail.com>
Co-authored-by: vaibhav kumar <catvaku@gmail.com>
Co-authored-by: waclaw66 <waclaw66@seznam.cz>
Co-authored-by: Максим Горпиніч <gorpinicmaksim0@gmail.com>
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Oct 15, 2025
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/immich-app/immich-server](https://github.com/immich-app/immich) | minor | `v2.0.1` -> `v2.1.0` |

---

### Release Notes

<details>
<summary>immich-app/immich (ghcr.io/immich-app/immich-server)</summary>

### [`v2.1.0`](https://github.com/immich-app/immich/releases/tag/v2.1.0)

[Compare Source](immich-app/immich@v2.0.1...v2.1.0)

### v2.1.0

Welcome to release `v2.1.0` of Immich!

It’s been about 2 weeks since our [stable release](https://immich.app/blog/stable-release) 🎉 and the celebratory [Retro DVD](https://immich.store/products/immich-retro) 📀, which many of you are starting to receive. For those outside the US still waiting for theirs, we're currently awaiting a new batch of DVDs. Hopefully, those start shipping early next week at the latest.

We’d like to take a moment to thank everyone for the support, kind words, and  otherwise positive feedback. It’s been great to hear, and it means more to us than you know ♥️.

#### Highlights

This release contains mostly bug fixes, with a few minor enhancements. Here are some of the highlights:

- Improved slideshow shuffle order (web)
- Edit seconds and milliseconds (web)
- Upload to stack (web)
- Local album event notifications (web)
- Notable fix: mobile search page scrolls back to the top

As always, this release also contains the latest [translations](https://hosted.weblate.org/projects/immich/immich/).

##### Improved slideshow shuffle order (web)

This release improves on the algorithm used to implement the  `Shuffle` order for the slideshow feature on the web. You should now see a better distribution of photos (fewer repeats).

<img width="500" alt="image" src="https://github.com/user-attachments/assets/d1d3a3de-4814-49d5-a4aa-9cf9b3be0fff" />

##### Edit seconds and milliseconds (web)

The change date modal on the web now supports the option to specify seconds and milliseconds.

<img width="500" alt="image" src="https://github.com/user-attachments/assets/3f6bd91c-b61d-4f53-9cec-cd8480e6acfd" />

##### Upload to stack (web)

For stacks, a new menu option has been added: `Add upload to stack`. This action will open a file picker, allowing you to upload directly to the stack.

<img width="500" alt="image" src="https://github.com/user-attachments/assets/4a19edd4-0b6d-457d-96ae-f93ef7ffec72" />

##### Local album event notifications (web)

This release adds a new in-app notification type: `Shared Album Invitation` and `Shared Album Update`. When you are invited to a shared album, and when a new asset is added to it, all users will receive a notification about the update. You can click on the notification to go directly to the mentioned album.

The foundation for in-app (web) notifications was introduced in [#&#8203;17701](immich-app/immich#17701), and we’re still working on expanding it to include more event types, with the hope of eventually incorporating it into the mobile app as a push notification.

<img width="500" alt="image" src="https://github.com/user-attachments/assets/db0735f2-6ef6-48a7-86bc-2fc48aabaf02" />

##### Notable fix: mobile search page scrolls back to the top

The search page on mobile uses “infinite scroll”, which means new chunks of assets are automatically loaded in as you scroll down the page. However, prior to this release, the scroll position would reset to the top whenever new chunks were added, resulting in a frustrating user experience. This has been fixed, and the scroll position no longer resets.

#### Support Immich

<p align="center">

<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExbjY2eWc5Y2F0ZW56MmR4aWE0dDhzZXlidXRmYWZyajl1bWZidXZpcyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/87CKDqErVfMqY/giphy.gif" width="450" title="SUPPORT THE PROJECT!">

</p>

If you find the project helpful, you can support Immich by purchasing a product key at <https://buy.immich.app> or our merchandise at <https://immich.store>

#### What's Changed

##### 🌟 Enhancements

- feat(server): improve checkAlbumAccess query performance by [@&#8203;skatsubo](https://github.com/skatsubo) in [#&#8203;22467](immich-app/immich#22467)
- fix(web): do not notify on patch releases by [@&#8203;jrasm91](https://github.com/jrasm91) in [#&#8203;22591](immich-app/immich#22591)
- fix(web): Uniform random distribution during shuffle by [@&#8203;Pascal-So](https://github.com/Pascal-So) in [#&#8203;19902](immich-app/immich#19902)
- feat(web): seconds and milliseconds in timestamps by [@&#8203;kaziu687](https://github.com/kaziu687) in [#&#8203;20337](immich-app/immich#20337)
- feat(web): Add upload to stack action by [@&#8203;Sese-Schneider](https://github.com/Sese-Schneider) in [#&#8203;19842](immich-app/immich#19842)
- feat(server): add `immich.users.total` metric by [@&#8203;Tushar-Harsora](https://github.com/Tushar-Harsora) in [#&#8203;21780](immich-app/immich#21780)
- feat: local album events notification by [@&#8203;alextran1502](https://github.com/alextran1502) in [#&#8203;22817](immich-app/immich#22817)

##### 🐛 Bug fixes

- fix(docs): link to immich docs does not lead correctly to docs by [@&#8203;XavierDupuis](https://github.com/XavierDupuis) in [#&#8203;22687](immich-app/immich#22687)
- fix(server): fix chunking Postgres query parameters by [@&#8203;skatsubo](https://github.com/skatsubo) in [#&#8203;22684](immich-app/immich#22684)
- fix(ml): Resolve IPv6 startup crash and healthcheck failure by [@&#8203;CaptainJack2491](https://github.com/CaptainJack2491) in [#&#8203;22387](immich-app/immich#22387)
- fix(server): override reserved color metadata for video thumbnails by [@&#8203;mertalev](https://github.com/mertalev) in [#&#8203;22348](immich-app/immich#22348)
- fix(mobile): trash description cut off by [@&#8203;YarosMallorca](https://github.com/YarosMallorca) in [#&#8203;22662](immich-app/immich#22662)
- fix(mobile): empty album description does not save by [@&#8203;YarosMallorca](https://github.com/YarosMallorca) in [#&#8203;22649](immich-app/immich#22649)
- fix(mobile): video player using ref after disposal by [@&#8203;mertalev](https://github.com/mertalev) in [#&#8203;21843](immich-app/immich#21843)
- fix: missing responsive calculation in UserPageLayout by [@&#8203;midzelis](https://github.com/midzelis) in [#&#8203;22455](immich-app/immich#22455)
- fix: use full-size image for non-web-compatible panoramas by [@&#8203;grgergo1](https://github.com/grgergo1) in [#&#8203;20359](immich-app/immich#20359)
- fix: hide view in timeline button on local timeline by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22713](immich-app/immich#22713)
- fix: Fix issue fail to download iOS live photos by [@&#8203;CuberL](https://github.com/CuberL) in [#&#8203;22708](immich-app/immich#22708)
- fix(mobile): closing editor goes back to main page by [@&#8203;YarosMallorca](https://github.com/YarosMallorca) in [#&#8203;22647](immich-app/immich#22647)
- fix: improve the selected sidebar item text color in dark mode by [@&#8203;alextran1502](https://github.com/alextran1502) in [#&#8203;22640](immich-app/immich#22640)
- fix: promote to foreground service before starting engine by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22517](immich-app/immich#22517)
- fix: bottom sheet blank with local assets that have remote counterparts by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22743](immich-app/immich#22743)
- fix: skip local only assets in move to lock action by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22728](immich-app/immich#22728)
- fix: brief flashing when swiping from video by [@&#8203;Saschl](https://github.com/Saschl) in [#&#8203;22187](immich-app/immich#22187)
- fix: persist search page scroll offset between rebuilds by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22733](immich-app/immich#22733)
- fix: only cast to device if the asset is a RemoteAsset by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22805](immich-app/immich#22805)
- fix: ios skip posting hash response after detached from engine by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22695](immich-app/immich#22695)
- fix: shared album control permissions by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22435](immich-app/immich#22435)
- fix: show dialog before delete local action by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22280](immich-app/immich#22280)
- chore: refactor show view in timeline button by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22894](immich-app/immich#22894)

##### 📚 Documentation

- docs: update Synology install guide by [@&#8203;TDR001](https://github.com/TDR001) in [#&#8203;21996](immich-app/immich#21996)
- docs: add some external library notes by [@&#8203;jrasm91](https://github.com/jrasm91) in [#&#8203;22776](immich-app/immich#22776)
- docs: add Immich-Stack to community-projects by [@&#8203;Qhilm](https://github.com/Qhilm) in [#&#8203;21563](immich-app/immich#21563)

##### 🌐 Translations

- chore(web): update translations by [@&#8203;weblate](https://github.com/weblate) in [#&#8203;22623](immich-app/immich#22623)

#### New Contributors

- [@&#8203;XavierDupuis](https://github.com/XavierDupuis) made their first contribution in [#&#8203;22687](immich-app/immich#22687)
- [@&#8203;adrianjost](https://github.com/adrianjost) made their first contribution in [#&#8203;22631](immich-app/immich#22631)
- [@&#8203;CaptainJack2491](https://github.com/CaptainJack2491) made their first contribution in [#&#8203;22387](immich-app/immich#22387)
- [@&#8203;USBAkimbo](https://github.com/USBAkimbo) made their first contribution in [#&#8203;22673](immich-app/immich#22673)
- [@&#8203;MontejoJorge](https://github.com/MontejoJorge) made their first contribution in [#&#8203;22702](immich-app/immich#22702)
- [@&#8203;diogotcorreia](https://github.com/diogotcorreia) made their first contribution in [#&#8203;21602](immich-app/immich#21602)
- [@&#8203;CuberL](https://github.com/CuberL) made their first contribution in [#&#8203;22708](immich-app/immich#22708)
- [@&#8203;TDR001](https://github.com/TDR001) made their first contribution in [#&#8203;21996](immich-app/immich#21996)
- [@&#8203;PeterDaveHello](https://github.com/PeterDaveHello) made their first contribution in [#&#8203;22703](immich-app/immich#22703)

**Full Changelog**: <immich-app/immich@v2.0.1...v2.1.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xMzUuNCIsInVwZGF0ZWRJblZlciI6IjQxLjEzNS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbWFnZSJdfQ==-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/1765
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Oct 15, 2025
…0 (#1764)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/immich-app/immich-machine-learning](https://github.com/immich-app/immich) | minor | `v2.0.1` -> `v2.1.0` |

---

### Release Notes

<details>
<summary>immich-app/immich (ghcr.io/immich-app/immich-machine-learning)</summary>

### [`v2.1.0`](https://github.com/immich-app/immich/releases/tag/v2.1.0)

[Compare Source](immich-app/immich@v2.0.1...v2.1.0)

### v2.1.0

Welcome to release `v2.1.0` of Immich!

It’s been about 2 weeks since our [stable release](https://immich.app/blog/stable-release) 🎉 and the celebratory [Retro DVD](https://immich.store/products/immich-retro) 📀, which many of you are starting to receive. For those outside the US still waiting for theirs, we're currently awaiting a new batch of DVDs. Hopefully, those start shipping early next week at the latest.

We’d like to take a moment to thank everyone for the support, kind words, and  otherwise positive feedback. It’s been great to hear, and it means more to us than you know ♥️.

#### Highlights

This release contains mostly bug fixes, with a few minor enhancements. Here are some of the highlights:

- Improved slideshow shuffle order (web)
- Edit seconds and milliseconds (web)
- Upload to stack (web)
- Local album event notifications (web)
- Notable fix: mobile search page scrolls back to the top

As always, this release also contains the latest [translations](https://hosted.weblate.org/projects/immich/immich/).

##### Improved slideshow shuffle order (web)

This release improves on the algorithm used to implement the  `Shuffle` order for the slideshow feature on the web. You should now see a better distribution of photos (fewer repeats).

<img width="500" alt="image" src="https://github.com/user-attachments/assets/d1d3a3de-4814-49d5-a4aa-9cf9b3be0fff" />

##### Edit seconds and milliseconds (web)

The change date modal on the web now supports the option to specify seconds and milliseconds.

<img width="500" alt="image" src="https://github.com/user-attachments/assets/3f6bd91c-b61d-4f53-9cec-cd8480e6acfd" />

##### Upload to stack (web)

For stacks, a new menu option has been added: `Add upload to stack`. This action will open a file picker, allowing you to upload directly to the stack.

<img width="500" alt="image" src="https://github.com/user-attachments/assets/4a19edd4-0b6d-457d-96ae-f93ef7ffec72" />

##### Local album event notifications (web)

This release adds a new in-app notification type: `Shared Album Invitation` and `Shared Album Update`. When you are invited to a shared album, and when a new asset is added to it, all users will receive a notification about the update. You can click on the notification to go directly to the mentioned album.

The foundation for in-app (web) notifications was introduced in [#&#8203;17701](immich-app/immich#17701), and we’re still working on expanding it to include more event types, with the hope of eventually incorporating it into the mobile app as a push notification.

<img width="500" alt="image" src="https://github.com/user-attachments/assets/db0735f2-6ef6-48a7-86bc-2fc48aabaf02" />

##### Notable fix: mobile search page scrolls back to the top

The search page on mobile uses “infinite scroll”, which means new chunks of assets are automatically loaded in as you scroll down the page. However, prior to this release, the scroll position would reset to the top whenever new chunks were added, resulting in a frustrating user experience. This has been fixed, and the scroll position no longer resets.

#### Support Immich

<p align="center">

<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExbjY2eWc5Y2F0ZW56MmR4aWE0dDhzZXlidXRmYWZyajl1bWZidXZpcyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/87CKDqErVfMqY/giphy.gif" width="450" title="SUPPORT THE PROJECT!">

</p>

If you find the project helpful, you can support Immich by purchasing a product key at <https://buy.immich.app> or our merchandise at <https://immich.store>

#### What's Changed

##### 🌟 Enhancements

- feat(server): improve checkAlbumAccess query performance by [@&#8203;skatsubo](https://github.com/skatsubo) in [#&#8203;22467](immich-app/immich#22467)
- fix(web): do not notify on patch releases by [@&#8203;jrasm91](https://github.com/jrasm91) in [#&#8203;22591](immich-app/immich#22591)
- fix(web): Uniform random distribution during shuffle by [@&#8203;Pascal-So](https://github.com/Pascal-So) in [#&#8203;19902](immich-app/immich#19902)
- feat(web): seconds and milliseconds in timestamps by [@&#8203;kaziu687](https://github.com/kaziu687) in [#&#8203;20337](immich-app/immich#20337)
- feat(web): Add upload to stack action by [@&#8203;Sese-Schneider](https://github.com/Sese-Schneider) in [#&#8203;19842](immich-app/immich#19842)
- feat(server): add `immich.users.total` metric by [@&#8203;Tushar-Harsora](https://github.com/Tushar-Harsora) in [#&#8203;21780](immich-app/immich#21780)
- feat: local album events notification by [@&#8203;alextran1502](https://github.com/alextran1502) in [#&#8203;22817](immich-app/immich#22817)

##### 🐛 Bug fixes

- fix(docs): link to immich docs does not lead correctly to docs by [@&#8203;XavierDupuis](https://github.com/XavierDupuis) in [#&#8203;22687](immich-app/immich#22687)
- fix(server): fix chunking Postgres query parameters by [@&#8203;skatsubo](https://github.com/skatsubo) in [#&#8203;22684](immich-app/immich#22684)
- fix(ml): Resolve IPv6 startup crash and healthcheck failure by [@&#8203;CaptainJack2491](https://github.com/CaptainJack2491) in [#&#8203;22387](immich-app/immich#22387)
- fix(server): override reserved color metadata for video thumbnails by [@&#8203;mertalev](https://github.com/mertalev) in [#&#8203;22348](immich-app/immich#22348)
- fix(mobile): trash description cut off by [@&#8203;YarosMallorca](https://github.com/YarosMallorca) in [#&#8203;22662](immich-app/immich#22662)
- fix(mobile): empty album description does not save by [@&#8203;YarosMallorca](https://github.com/YarosMallorca) in [#&#8203;22649](immich-app/immich#22649)
- fix(mobile): video player using ref after disposal by [@&#8203;mertalev](https://github.com/mertalev) in [#&#8203;21843](immich-app/immich#21843)
- fix: missing responsive calculation in UserPageLayout by [@&#8203;midzelis](https://github.com/midzelis) in [#&#8203;22455](immich-app/immich#22455)
- fix: use full-size image for non-web-compatible panoramas by [@&#8203;grgergo1](https://github.com/grgergo1) in [#&#8203;20359](immich-app/immich#20359)
- fix: hide view in timeline button on local timeline by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22713](immich-app/immich#22713)
- fix: Fix issue fail to download iOS live photos by [@&#8203;CuberL](https://github.com/CuberL) in [#&#8203;22708](immich-app/immich#22708)
- fix(mobile): closing editor goes back to main page by [@&#8203;YarosMallorca](https://github.com/YarosMallorca) in [#&#8203;22647](immich-app/immich#22647)
- fix: improve the selected sidebar item text color in dark mode by [@&#8203;alextran1502](https://github.com/alextran1502) in [#&#8203;22640](immich-app/immich#22640)
- fix: promote to foreground service before starting engine by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22517](immich-app/immich#22517)
- fix: bottom sheet blank with local assets that have remote counterparts by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22743](immich-app/immich#22743)
- fix: skip local only assets in move to lock action by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22728](immich-app/immich#22728)
- fix: brief flashing when swiping from video by [@&#8203;Saschl](https://github.com/Saschl) in [#&#8203;22187](immich-app/immich#22187)
- fix: persist search page scroll offset between rebuilds by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22733](immich-app/immich#22733)
- fix: only cast to device if the asset is a RemoteAsset by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22805](immich-app/immich#22805)
- fix: ios skip posting hash response after detached from engine by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22695](immich-app/immich#22695)
- fix: shared album control permissions by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22435](immich-app/immich#22435)
- fix: show dialog before delete local action by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;22280](immich-app/immich#22280)
- chore: refactor show view in timeline button by [@&#8203;bwees](https://github.com/bwees) in [#&#8203;22894](immich-app/immich#22894)

##### 📚 Documentation

- docs: update Synology install guide by [@&#8203;TDR001](https://github.com/TDR001) in [#&#8203;21996](immich-app/immich#21996)
- docs: add some external library notes by [@&#8203;jrasm91](https://github.com/jrasm91) in [#&#8203;22776](immich-app/immich#22776)
- docs: add Immich-Stack to community-projects by [@&#8203;Qhilm](https://github.com/Qhilm) in [#&#8203;21563](immich-app/immich#21563)

##### 🌐 Translations

- chore(web): update translations by [@&#8203;weblate](https://github.com/weblate) in [#&#8203;22623](immich-app/immich#22623)

#### New Contributors

- [@&#8203;XavierDupuis](https://github.com/XavierDupuis) made their first contribution in [#&#8203;22687](immich-app/immich#22687)
- [@&#8203;adrianjost](https://github.com/adrianjost) made their first contribution in [#&#8203;22631](immich-app/immich#22631)
- [@&#8203;CaptainJack2491](https://github.com/CaptainJack2491) made their first contribution in [#&#8203;22387](immich-app/immich#22387)
- [@&#8203;USBAkimbo](https://github.com/USBAkimbo) made their first contribution in [#&#8203;22673](immich-app/immich#22673)
- [@&#8203;MontejoJorge](https://github.com/MontejoJorge) made their first contribution in [#&#8203;22702](immich-app/immich#22702)
- [@&#8203;diogotcorreia](https://github.com/diogotcorreia) made their first contribution in [#&#8203;21602](immich-app/immich#21602)
- [@&#8203;CuberL](https://github.com/CuberL) made their first contribution in [#&#8203;22708](immich-app/immich#22708)
- [@&#8203;TDR001](https://github.com/TDR001) made their first contribution in [#&#8203;21996](immich-app/immich#21996)
- [@&#8203;PeterDaveHello](https://github.com/PeterDaveHello) made their first contribution in [#&#8203;22703](immich-app/immich#22703)

**Full Changelog**: <immich-app/immich@v2.0.1...v2.1.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xMzUuNCIsInVwZGF0ZWRJblZlciI6IjQxLjEzNS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbWFnZSJdfQ==-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/1764
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Thumbnail of partner photo take 2 second to load in my timeline

3 participants