Compare commits

...

767 Commits

Author SHA1 Message Date
timonrieger
fcd23ee043 refactor: use zod codec for response DTO serialization 2026-04-23 14:21:40 +02:00
renovate[bot]
350056dd1a fix(deps): update dependency uuid to v14 [security] (#28046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 11:24:33 +02:00
Alex
f0835d06f8 chore: migrate to FUTO Apple's account (#28020)
* chore: migrate to FUTO Apple's account

* chore: migrate to FUTO Apple's account

* chore: match widget and share extension

* chore: update app share group

* reuse group.app.immich.share
2026-04-22 11:53:20 -05:00
Alex
03b70cf029 fix: jump to timeline on new auto_router update (#28022) 2026-04-22 10:21:48 -05:00
Daniel Dietzler
4bfb8b36c2 chore!: migrate album owner to album_user (#27467)
Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-22 16:52:23 +02:00
renovate[bot]
dfacde5af8 fix(deps): update typescript-projects (#28025)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-22 16:49:28 +02:00
Junghwan
317afe9e3b fix(web): normalize underscore locale codes in dynamic language selection (#27900)
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-22 13:28:33 +00:00
Jason Rasmussen
1fb5f13237 fix: oauth prompt (#28021) 2026-04-22 09:19:28 -04:00
Luis Nachtigall
793a7054fb fix(mobile): thumbnail transition to asset viewer (#27850) 2026-04-21 15:54:40 -05:00
Luis Nachtigall
3a874dd441 fix(mobile): enable autoplay for motion photos in video viewer (#27961) 2026-04-21 15:53:21 -05:00
Luis Nachtigall
3dc7dc93d8 fix(mobile): clear local data on forced logout (#27957) 2026-04-21 15:52:00 -05:00
Yaros
70397dc5a6 fix(mobile): zero exposure (#28017) 2026-04-21 15:47:27 -05:00
Jason Rasmussen
a16d233a0c chore(web): sort imports (#27922)
* feat: sort imports

* fix: something?
2026-04-21 14:51:38 -04:00
Daniel Dietzler
bb0872afef chore: upgrade sql-tools (#27885) 2026-04-21 17:42:27 +00:00
Freddie Floydd
b9ca68f6e4 chore(web): rename components to PascalCase (#28013)
chore: rename components to PascalCase
2026-04-21 12:29:42 -04:00
Daniel Dietzler
837305da7e chore: un-skip tests (#28012) 2026-04-21 12:08:23 -04:00
Daniel Dietzler
e20fb44142 fix: web navigation/animation regression (#28011) 2026-04-21 14:51:37 +00:00
renovate[bot]
c2786978cd fix(deps): update typescript-projects (#28008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-21 15:29:34 +02:00
renovate[bot]
312bb91a4f chore(deps): update github-actions (#28005)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 13:35:55 +02:00
renovate[bot]
c1934b904c chore(deps): update dependency opentofu to v1.11.6 (#27999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 13:31:16 +02:00
renovate[bot]
47752d158a fix(deps): update dependency @mapbox/mapbox-gl-rtl-text to v0.4.0 (#28007)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 13:25:30 +02:00
renovate[bot]
6267322b9c chore(deps): update dependency exiftool-vendored to v35.17.0 (#28004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 13:08:12 +02:00
renovate[bot]
93c3cd49f3 chore(deps): update node.js to v24.15.0 (#28006)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 09:47:32 +00:00
renovate[bot]
f52825ab08 chore(deps): update prom/prometheus docker digest to 5550dc6 (#27998)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 11:23:24 +02:00
renovate[bot]
d74dc74f92 chore(deps): update dependency terragrunt to v1.0.1 (#28002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 11:23:07 +02:00
Luis Nachtigall
539a39ae49 refactor(mobile): Migrate durationInSeconds to durationMs (#26615) 2026-04-20 23:28:11 -04:00
Daniel Dietzler
f68cd424a7 chore: tags styling (#27984) 2026-04-20 22:06:43 -05:00
Alex
20c0cc7e73 fix: show neon light (#27994) 2026-04-20 20:12:54 -04:00
Aki Hakune
be1b9a5f67 feat(server): add MPO file type support (#27963)
* feat(server): add MPO file type support

* fix(server): document description error
2026-04-20 17:45:53 -04:00
shenlong
d9011c0829 refactor: test organisation and service test (#27991)
* refactor: test organisation

# Conflicts:
#	mobile/test/unit/utils/editor_test.dart

* regroup hash_service_test

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-20 17:45:20 -04:00
shenlong
f909648bce chore: pump flutter to 3.41.7 (#27990)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-20 14:51:27 +00:00
Min Idzelis
c78b1d8ab4 fix(web): prevent interaction with detail panel behind person side panel (#27309) 2026-04-20 15:26:06 +02:00
Jason Rasmussen
94a34436a3 chore: remove unused packages & code (#27925) 2026-04-20 08:39:46 -04:00
shenlong
0eef15a3ab chore(mobile): minor dependency updates (#27949)
* chore: minor dependency updates

* regenerate pod and remove unused imports

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-19 11:56:39 -05:00
shenlong
6982896549 feat: android periodic work manager task (#23563)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-19 11:55:07 -05:00
Matthew Momjian
2c812a2561 fix(docs): helmet file affected containers (#27939)
fix helmet file
2026-04-18 12:19:39 -04:00
Mert
0b1188e42e chore(server): separate ffmpeg arguments (#27937)
separate arguments
2026-04-18 15:33:44 +00:00
Freddie Floydd
be20cd2bf9 chore(web): bump svelte-check version to silence big warning stack trace (#27935)
chore: bump svelte-check version to silence big warning log
2026-04-18 14:42:47 +00:00
LJspice
b8591cb591 feat(server): add OIDC logout URL override option (#27389)
* feat(server): add OIDC logout URL override option
- Added toggle and field consistent with existing mobile redirect URI override.
- Existing auto-discovery remains default.
- Update tests and docs for new feature.

* fix(server): changes from review for OIDC logout URL override
- Rename 'logoutUri' to 'endSessionEndpoint'
- Remove toggle, just use override if provided
- Moved field in settings UI
2026-04-18 04:18:21 +00:00
Freddie Floydd
384d3a0984 fix(web): fix stale album page load (#27825)
* invalidate album data on album update to fix stale page load

* invalidate album data on album update to fix stale page load

* factor out callback, make async and await invalidate

* chore: formatting

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-04-17 21:24:33 -04:00
Freddie Floydd
03af669856 refactor(web): co-locate single-use components in /routes (#27921)
* co-locate single use components to /routes

* revert accidentally changed paths

* fix mangled path

* fmt

* fix accidentally moved multi-use components
2026-04-17 21:21:36 -04:00
renovate[bot]
b0e4850d76 chore(deps): update dependency flutter to v3.41.6 (#27915)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-18 05:14:44 +05:30
Freddie Floydd
36ebcaf00c fix(web): compute hashes for uploads in chunks (#27878)
* add @noble/hashes as a dep for web

* hash files in chunks

* drop old reference to crypto in test code

* use web worker for file hashing
2026-04-17 19:08:46 -04:00
shenlong
7a86f2b7b9 chore: remove stale mobile/.isar submodule entry (#27913)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-18 04:29:13 +05:30
sparsh985
55f2b3b6a0 feat(server): add configurable OAuth prompt parameter (#26755)
* feat(server): add configurable OAuth prompt parameter

Add a `prompt` field to the OAuth system config, allowing admins to
configure the OIDC `prompt` parameter (e.g. `select_account`, `login`,
`consent`). Defaults to empty string (no prompt sent), preserving
backward compatibility.

This is useful for providers like Google where users want to be prompted
to select an account when multiple accounts are signed in.

Discussed in #20762

* chore: regenerate OpenAPI spec and clients for OAuth prompt field

* Adding e2e test cases

* feat: web setting

* feat: docs

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-04-17 21:20:07 +00:00
shenlong
fd5e8d6521 chore: pump auto_route (#27876)
* chore: pump auto_route

* make build

* chore: use drift from pubdev (#27877)

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

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-17 20:28:36 +00:00
Freddie Floydd
6798d5df32 fix(server): require at least one field to be set when updating memory (#27842)
* add zod util to require one field is set in some schemas. appy to update memory endpoint

* add test
2026-04-17 20:18:48 +00:00
Min Idzelis
9d33853544 fix(web): respect abort signal after timeline bucket fetches (#27563)
Change-Id: I4bf7c7458883b50bd21484b1029d62526a6a6964
2026-04-17 16:18:14 -04:00
bo0tzz
a46e46452c fix: run profile picture through thumbnail pipeline (#27890)
* fix: run profile picture through thumbnail pipeline

* fix: format
2026-04-17 16:15:59 -04:00
santanoce
dbf30b77bf feat(server): added backchannel logout api endpoint (#26235)
* feat(server): added backchannel logout api endpoint

* test(server): fixed e2e tests

* fix(server): fixed suggested changes by reviewer

* feat(server): created function invalidateOAuth

* fix(server): fixed session.repository.sql

* test(server): added unit tests for backchannelLogout function

* test(server): added e2e tests for oidc backchnnel logout

* docs(server): added documentation on backchannel logout url

* docs(server): fixed typo

* feat(server): minor improvements of the oidc backchannel logout

* test(server): fixed tests after merge with main

* fix(server): fixed e2e test file

* refactor(server): tiny refactor of validateLogoutToken

* chore: cleanup

* fix: tests

* fix: make jwks extractable

---------

Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-17 18:45:33 +00:00
bo0tzz
8afca348ff fix: sanitize filenames before adding to zip (#27893)
* fix: sanitize filenames before adding to zip

* fix: lints

* chore: drop split()
2026-04-17 13:05:53 -04:00
shenlong
2070f775d6 refactor: remove riverpod generator (#27874)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-17 20:01:44 +05:30
bo0tzz
a456a05052 chore: make filesystem backup docs even more explicit (#27013) 2026-04-17 15:42:40 +02:00
Sergey Katsubo
b7eff33f90 chore(web): refactor date section of asset viewer (#24514)
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-17 12:56:39 +00:00
Mees Frensel
18c0228f1b fix(web): remove json title from duplicate tool thumbnail (#27886) 2026-04-17 08:37:04 -04:00
Jason Rasmussen
2f8be45fe0 chore!: remove /api/server/theme endpoint (#27880)
chore: remove server/theme endpoint
2026-04-17 08:30:03 -04:00
Daniel Dietzler
41968fdcac feat: cache shared link (#27889) 2026-04-17 08:17:39 -04:00
Yaros
79c392ceba fix(docs): instructions on how to use local immich ui (#27813) 2026-04-17 12:29:14 +02:00
Mees Frensel
8fbeb64c59 fix(web): use event for zooming out after opening face editor (#27789) 2026-04-17 12:14:24 +02:00
Mees Frensel
7d181f0686 fix!: set duration to null when not present (#26982) 2026-04-17 11:57:10 +02:00
shenlong
2172dde7dc chore: remove immich lint (#27873)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-16 15:55:40 -05:00
Luis Nachtigall
fce220b1d7 chore(mobile): update special format column detection (#27867) 2026-04-16 14:01:59 -04:00
shenlong
2a47c35eb7 chore: pump flutter to 3.41.6 (#27834)
* chore: pump flutter to 3.41.6

* more isar cleanup

* ignore experimental use for TableMigration

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-16 22:26:46 +05:30
Jason Rasmussen
6aadb7b5bd feat: dynamic languages (#27869)
Co-authored-by: xantin <github@xantin.be>
2026-04-16 12:37:37 -04:00
renovate[bot]
88bce52042 fix(deps): update dependency jose to v6 (#27862)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-16 18:36:07 +02:00
Timothy Dobras
d046f16860 fix(oauth): normalize email claim to lowercase and trim before account lookup and registration (#26841)
* fix(oauth): normalize email claim to lowercase before account lookup and registration

* test(auth): add test for OAuth email case normalization

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-04-16 15:41:42 +00:00
renovate[bot]
88815a0345 chore(deps): update base-image to v202604141125 (major) (#27858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 15:40:40 +00:00
Jason Rasmussen
57212f29bf chore: bump (#27866) 2026-04-16 11:32:33 -04:00
renovate[bot]
95fa8fbdab chore(deps): update machine-learning (#26970)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 11:32:08 -04:00
renovate[bot]
687b7cad6f chore(deps): update dependency terragrunt to v1 (#27860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 10:36:15 -04:00
Steven Massaro
ac2ebcee37 chore: improve randomness of /search/random endpoint (#27531) 2026-04-16 14:36:05 +00:00
bo0tzz
3356e81c85 fix!: do not allow insecure oauth requests by default (#27844)
* fix!: do not allow insecure oauth requests by default

* fix: format

* fix: make open-api

* fix: tests

* nit: casing

* chore: migration to allow insecure if current oauth uses http
2026-04-16 10:11:58 -04:00
renovate[bot]
9c642bd6fc chore(deps): update github-actions (#27857)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 15:29:48 +02:00
bo0tzz
9da0cb3cf4 chore: link to PR template in auto-close message (#27756) 2026-04-16 09:14:36 -04:00
renovate[bot]
4ff6cca4da fix(deps): update dependency pillow to >=12.2,<12.3 [security] (#27773)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 09:13:39 -04:00
renovate[bot]
2b7ae4981f chore(deps): update dependency pytest to v9.0.3 [security] (#27777)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 09:12:49 -04:00
renovate[bot]
e63df4121a chore(deps): update dependency @types/node to ^24.12.2 (#27856)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 13:12:38 +00:00
renovate[bot]
03b4ab2935 fix(deps): update dependency simple-icons to v16 (#27855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 13:07:35 +00:00
bo0tzz
facd3bd331 fix: oauth issuerUrl validation (#27848) 2026-04-16 09:06:55 -04:00
renovate[bot]
20ddf2e7d2 fix(deps): update dependency nestjs-cls to v6 (#27852)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 13:45:42 +02:00
renovate[bot]
7f0025b3fc chore(deps): update dependency @types/nodemailer to v8 (#27851)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 13:28:23 +02:00
Mees Frensel
60f4dedb29 chore(web): small fixes for location picker modal (#27822) 2026-04-16 11:49:24 +02:00
Min Idzelis
d5d2ebd9bf fix(web): close edit faces panel on Escape key press (#27519)
Move `showEditFaces` state to `assetViewerManager` so the edit faces
panel open/close state is globally accessible. Add Escape key handling
to `PersonSidePanel` that closes the assign-face sub-panel first, then
the edit faces panel. Guard the asset viewer's global Escape-to-close
action so it doesn't fire while either face panel is open.

Change-Id: I0c947fe0758aef0eac473f4cc72f6a3b6a6a6964
2026-04-15 20:22:20 -05:00
Yaros
37abbeba52 fix(mobile): readonly redirect when not logged in (#27728) 2026-04-15 20:20:08 -05:00
Min Idzelis
50557002b7 fix(web): stale adaptive image when original overlays preview (#27621)
AdaptiveImage stacks quality layers (thumbnail → preview → original) as they load. Without compositor layer promotion on the container, the browser could render a stale frame when the original-quality layer was overlaid on top of the preview-quality layer.

Add `will-change: transform` as a class on AdaptiveImage's root element so it gets a dedicated compositor layer from first paint. This also subsumes the imperative `style.willChange = 'transform'` that zoomImageAction was applying to the same element (the zoom target from photo-viewer is the AdaptiveImage root), so drop that now-redundant code.

Change-Id: Icd866a2bb5a5fce299c36404547fa0546a6a6964
2026-04-15 20:19:18 -05:00
Min Idzelis
4aa31d38bf fix(web): svelte regression - cancel video preview fetch when bind:this is cleared early (#27713)
fix(web): cancel video preview fetch when bind:this is cleared early

In Svelte 5.53.9, `bind:this` is now cleared earlier in the unmount
sequence ("better bind:this cleanup timing"). The video thumbnail's
$effect was relying on the old order to read the bound `player` element
and clear its `src` to abort the in-flight `/api/assets/{id}/video/playback`
range request — but the bind is now `undefined` by the time the effect
runs, so the cleanup is silently skipped. The detached <video> keeps
its src, and Firefox does not abort an in-flight media fetch when the
element is detached/GC'd. Long-lived 206 range requests then saturate
Firefox's 6-connection HTTP/1.1 per-host limit and freeze the timeline
(see #27585).

Capture the player reference inside the effect and tear down via the
effect cleanup return — Svelte runs the prior cleanup (with the captured
ref) before `bind:this` is cleared. Use the canonical
`pause() / removeAttribute('src') / load()` sequence which actually aborts
the fetch in Firefox, even on a detached element.

Fixes #27585

Change-Id: I4d9fba859955f5c54f603c345e61d4206a6a6964
2026-04-15 20:18:59 -05:00
Min Idzelis
3d8df74b43 refactor(web): turn thumbhash action into Thumbhash component (#27741)
refactor(web): extract thumbhash canvas into Thumbhash component

Change-Id: If78955bed48b6e690df398e5e2ae61fb6a6a6964
2026-04-15 20:18:49 -05:00
renovate[bot]
2ff9f95527 chore(deps): update dependency python-multipart to v0.0.26 [security] (#27838)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-15 19:06:27 -04:00
Jason Rasmussen
a69eecf3bc chore!: remove without assets (#27835)
* chore!: remove without assets

* fix: linting and e2e

---------

Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-15 18:34:22 -04:00
Jason Rasmussen
4ffa26c969 feat: auth logout page (#27831)
* feat: auth logout page

* feat: skip login if already logged in
2026-04-15 16:33:52 -04:00
Jason Rasmussen
ac06514db5 feat: album map markers endpoint (#27830) 2026-04-15 15:58:34 -04:00
Jason Rasmussen
792cb9148b chore!: rename API key schemas (#27828)
chore!: rename API schemas
2026-04-15 15:58:26 -04:00
Daniel Dietzler
8ee5d3039a chore!: remove deviceId and deviceAssetId (#27818)
chore: remove deviceId and deviceAssetId
2026-04-15 15:00:33 -04:00
Jason Rasmussen
d410131312 chore!: remove old timeline sync endpoints (#27804) 2026-04-15 13:58:48 -04:00
bo0tzz
5334a6254a fix: make web build stage deterministic (#27823) 2026-04-15 19:31:23 +02:00
shenlong
79fccdbee0 refactor: yeet old timeline (#27666)
* refactor: yank old timeline

# Conflicts:
#	mobile/lib/presentation/pages/editing/drift_edit.page.dart
#	mobile/lib/providers/websocket.provider.dart
#	mobile/lib/routing/router.dart

* more cleanup

* remove native code

* chore: bump sqlite-data version

* remove old background tasks from BGTaskSchedulerPermittedIdentifiers

* rebase

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-15 23:00:27 +05:30
Brandon Wees
6dd6053222 feat: mobile editing (#25397)
* feat: mobile editing

fix: openapi patch

this sucks :pepehands:

chore: migrate some changes from the filtering PR

chore: color tweak

fix: hide edit button on server versions

chore: translation

* chore: code review changes

chore: code review

* sealed class

* const constant

* enum

* concurrent queries

* chore: major cleanup to use riverpod provider

* fix: aspect ratio selection

* chore: typesafe websocket event parsing

* fix: wrong disable state for save button

* chore: remove isCancelled shim

* chore: cleanup postframe callback usage

* chore: clean import

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
2026-04-15 09:26:40 -05:00
Mees Frensel
8454cb2631 chore: exit open-api script on errors (#27815) 2026-04-15 10:09:51 -04:00
Daniel Dietzler
603fc7401f fix: redirect original (#27759) 2026-04-15 15:43:09 +02:00
Jason Rasmussen
ed70e0febf chore: stop using legacy partner create endpoint (#27806) 2026-04-15 16:16:43 +05:30
Jason Rasmussen
5f5e3344d5 chore!: remove unused token response param (#27805) 2026-04-15 00:58:00 -04:00
Brandon Wees
6da2d3d587 chore!: remove getRandom api endpoint (#27780)
* chore!: remove getRandom api endpoint

* chore: sync openapi

* fix: test

* chore: more cleanup
2026-04-14 21:32:12 -04:00
Jason Rasmussen
41d2d84b21 chore!: remove deprecated env variables (#27802) 2026-04-14 21:30:31 -04:00
Jason Rasmussen
6ba17bb86f refactor!: remove my shared link dto (#27023)
refactor!: remove deprecated shared link apis
2026-04-14 20:58:02 -04:00
Jason Rasmussen
e1a84d3ab6 refactor!: remove replace asset (#27022) 2026-04-14 20:21:05 -04:00
Timon
7d8f843be6 refactor!: migrate class-validator to zod (#26597) 2026-04-14 23:39:03 +02:00
OdinOxin
3753b7a4d1 feat: sort users alphabetically when adding to album (#27731) 2026-04-14 21:21:22 +02:00
Jonathan Jogenfors
84a1fb27ca feat(web): lazy load library and server statistics (#26406)
* feat: add offline library statistics

* fix comments

* feat: add offline library statistics

* fix comments

* fix Daniel's comments

* fix Daniels comment 2
2026-04-14 12:54:09 -04:00
Yaros
81780b0cc0 fix(web): add partner photo to album from multiselect (#27767)
* fix(web): add partner photo to album

* chore: fix formatting

* fix: run-job assets

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-04-14 16:48:39 +00:00
Min Idzelis
5e81a5a054 feat(web): remove delay from Skeleton (#27580)
Change-Id: I95a37f1af832c005a8f009d6f07df8ac6a6a6964
2026-04-14 12:47:37 -04:00
Miguel Raposo
e4e2f586b5 fix(server): render storage template date/time tokens in UTC (#24350) (#26917) 2026-04-14 18:45:14 +02:00
OdinOxin
a001adf14a feat: filter users on share (#27732)
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-14 16:43:45 +00:00
Daniel Dietzler
136814540a fix: asset multi select download shortcut (#27784) 2026-04-14 12:29:55 -04:00
Jason Rasmussen
fed5cc1ae1 feat: upgrade immich/ui (#27792) 2026-04-14 16:18:12 +00:00
Yaros
641ab51b80 fix(web): selection clearing on preview (#27702)
* fix(web): selection clearing on preview

* chore: remove unnecessary checks
2026-04-14 10:06:32 -05:00
Yaros
3b47ca1c37 fix(mobile): add keys for person tiles in search (#27689)
* fix(mobile): key for person tiles in search

* chore: add key to avatar

* chore: use simple personId

* chore: rename key in person page
2026-04-14 10:05:09 -05:00
Jason Rasmussen
8fb2c7755d feat: commands (#27546) 2026-04-14 09:34:46 -04:00
Jason Rasmussen
1ba0989e15 refactor: auth manager (#27638) 2026-04-14 08:49:24 -04:00
renovate[bot]
daed3f0966 chore(deps): update dependency @sveltejs/kit to v2.57.1 [security] (#27762)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 11:50:25 +02:00
renovate[bot]
46d612ad8c chore(deps): update dependency nodemailer to v8.0.5 [security] (#27623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 11:49:26 +02:00
renovate[bot]
513dead2c2 chore(deps): update dependency @nestjs/core to v11.1.18 [security] (#27544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 09:49:03 +00:00
renovate[bot]
ca006c1569 fix(deps): update typescript-projects (#27573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-14 11:41:09 +02:00
renovate[bot]
4e8e8304fd fix(deps): update react-email monorepo (major) (#27572)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-14 11:40:58 +02:00
Nicolas-micuda-becker
d377d2e145 fix(web): center images in RTL layouts (#27678) (#27753) 2026-04-13 13:29:35 -05:00
shenlong
9c9feddf7d refactor: folder page to use new models (#27657)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-13 12:19:44 -05:00
Andreas Heinz
bfcf34d8b5 feat(web): persist state of file path information in details panel (#27770)
feat(enhancement): persist state of file path info in details panel
2026-04-13 12:18:34 -05:00
github-actions
95e57a24cb chore: version v2.7.5 2026-04-13 14:27:31 +00:00
Weblate (bot)
eada662981 chore(web): update translations (#27589)
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ar/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/da/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/de_CH/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/eo/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lt/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lv/
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/th/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/uk/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/vi/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/yue_Hant/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hant/
Translation: Immich/immich

Co-authored-by: Aliyss Snow <mangoworksbeta@gmail.com>
Co-authored-by: Bannawat Thongbai <kaji.kanlapat99@gmail.com>
Co-authored-by: Carlo Beltrame <weblate@pendantmusic.ch>
Co-authored-by: Dawnsink <dai@cosmopeace.com>
Co-authored-by: Edmundas <edmius@gmail.com>
Co-authored-by: Happy <59247878+happy2452354@users.noreply.github.com>
Co-authored-by: Jeppe Nellemann <jepnel@proton.me>
Co-authored-by: Linerly <linerly@proton.me>
Co-authored-by: Link Notig <TestMailProtonWhyNot@protonmail.com>
Co-authored-by: Mees Frensel <meesfrensel@gmail.com>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Osama <laptooxz@proton.me>
Co-authored-by: PPNplus <ppnplus@protonmail.com>
Co-authored-by: Tim Morley <weblate.3919org@timsk.org>
Co-authored-by: UDP <udp@users.noreply.hosted.weblate.org>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Volodymyr Sakharov <savolodya@gmail.com>
Co-authored-by: Vykintas Vyšniauskas <vykintasv@gmail.com>
Co-authored-by: WellsTsai <dan50907@gmail.com>
Co-authored-by: brainheart95 <josephdm4d@gmail.com>
Co-authored-by: dvbthien <dvbthien@users.noreply.hosted.weblate.org>
Co-authored-by: 이찬웅 <lcw7527@gmail.com>
2026-04-13 14:25:01 +00:00
Zack Pollard
352f6ecc28 fix(server): add rate limit and deduplication to version check (#27747) 2026-04-13 12:35:46 +00:00
github-actions
bee49cef02 chore: version v2.7.4 2026-04-10 16:32:26 +00:00
shenlong
6d0c6a4008 chore: pump cronet version (#27685)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-10 16:29:05 +00:00
Luis Nachtigall
8a975e5ea9 refactor(mobile): cleanup iOS image loading pipeline (#27672)
* refactor: replace DispatchQueue + DispatchSemaphore with OperationQueue for image processing

* implement RequestRegistry and UnfairLock for managing cancellable requests

* implement requests registry for local and remote image processing

* remove Cancellable protocol and cancel method from request registry

* refactor: introduce ImageRequest base class with unified cancellation and finish helpers

* refactor: add get method to RequestRegistry and streamline request removal in image processing

* add guard to cancel to prevent double onCancel calls

* fix duplicate code merge issue

* refactor(ios): enhance finish method to return callback status

* remove unfitting methods form ImageRequest.swift and fix memory issue

* revert bad merge

* refactor(ios): resolve cancellation issues

* refactor(ios): streamline image request completion handling

* add return statements

* refactor(ios): simplify image request cancellation and registry handling

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-10 10:56:35 -05:00
Luis Nachtigall
d39e7da10d fix(mobile): fix flutter cache eviction on thumbnails (#27663)
* fix: add markFinished parameter to loadRequest and loadCodecRequest methods

* update loadRequest and loadCodecRequest methods to use isFinal

* Apply suggestions from code review

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

* remove redundant check

* fix: ensure isFinished is set correctly during cache eviction

* formatting

---------

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
2026-04-10 10:28:55 -05:00
Daniel Dietzler
bc400d68ac chore: move .tsbuildinfo file to dist folder (#27682) 2026-04-10 16:02:25 +02:00
renovate[bot]
d7f038ec60 chore(deps): update dependency eslint-plugin-unicorn to v64 (#27575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-10 10:23:42 +00:00
Mees Frensel
26957f37ce fix(server): hide original filename when not showing metadata (#27581) 2026-04-10 12:07:18 +02:00
github-actions
3254d31cd2 chore: version v2.7.3 2026-04-09 17:51:40 +00:00
Jason Rasmussen
7b269d1638 fix: ssr open graph tags (#27639)
fix: SSR open graph tags
2026-04-09 12:16:41 -04:00
Luis Nachtigall
b5bed02300 fix(mobile): get provider refs before async gaps in backup page (#27597)
* fix(mobile): get provider refs before async gaps in backup page

* fix(mobile): use previously created provider refs in start backup function
2026-04-08 20:55:53 -05:00
Zack Pollard
5553910236 fix(web): don't cache empty search results for people search (#27632) 2026-04-09 02:33:04 +01:00
Zack Pollard
8d67c1f820 fix(server): people search not showing for 3 or less characters (#27629)
Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
2026-04-09 01:56:07 +01:00
Matthew Momjian
ed0ec30917 fix(docs): updated docker deprecation link (#27633)
new link
2026-04-08 20:33:11 -04:00
Luis Nachtigall
2b0f6c9202 fix(mobile): improve image load cancellation handling (#27624)
fix(image): improve image load cancellation handling
2026-04-08 17:23:42 -04:00
André Erasmus
55ab8c65b6 fix(server): avoid false restore failures on large database imports (#27420)
* fix(server): increase restore health check timeout and reject with Error

* chore: clean up

---------

Co-authored-by: André Erasmus <25480506+NoBadDays@users.noreply.github.com>
Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-04-08 16:03:41 -04:00
Cullen Jennings
781d568f29 fix(docs): typo 'Start rating' to 'Star rating' (#27606) 2026-04-08 18:25:45 +00:00
Zack Pollard
6a361dae72 fix(server): use randomized cron for version check scheduling (#27626)
Also removes unnecessary rate limit
2026-04-08 19:15:38 +01:00
renovate[bot]
64766c8c06 chore(deps): update github-actions (#27560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-08 17:32:54 +02:00
github-actions
6a63e814a5 chore: version v2.7.2 2026-04-07 20:58:38 +00:00
Jason Rasmussen
6441c3b77c fix: server build (#27599) 2026-04-07 20:53:04 +00:00
github-actions
b03a649e74 chore: version v2.7.1 2026-04-07 20:22:28 +00:00
Mert
2903b2653b fix(server): library import batch size (#27595)
* lower batch size

* update test
2026-04-07 15:58:03 -04:00
Mert
9ba9a22c40 fix(ml): downgrade numpy (#27591)
downgrade numpy
2026-04-07 15:57:42 -04:00
bo0tzz
f1882c2926 fix: csp quotes (#27592) 2026-04-07 15:54:30 -04:00
Daniel Dietzler
4278789083 chore: git ignore tsBuildInfo (#27594) 2026-04-07 15:53:10 -04:00
renovate[bot]
921c8a8de3 chore(deps): update dependency typescript to v6 (#27577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-04-07 17:15:55 +02:00
github-actions
afec61addc chore: version v2.7.0 2026-04-07 15:08:18 +00:00
Weblate (bot)
a1a03efbcd chore(web): update translations (#27483)
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ar/
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/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/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/it/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ko/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lt/
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/nn/
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/ru/
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/th/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/tr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hans/
Translation: Immich/immich

Co-authored-by: Dawnsink <dai@cosmopeace.com>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Francesco Fiorentino <gallgricela+trotter@gmail.com>
Co-authored-by: Frank Paul Silye <frankps@gmail.com>
Co-authored-by: Gianni De Wachter <gianni.dewachter@gmail.com>
Co-authored-by: HackingAll <hacking.all.YT@gmail.com>
Co-authored-by: Haru Ijima <haruijimakun@gmail.com>
Co-authored-by: Hurricane_32 <rodrigorimo@hotmail.com>
Co-authored-by: Jarek Iwanus <jiwanus@proton.me>
Co-authored-by: Junghyuk Kwon <kwon@junghy.uk>
Co-authored-by: Krastyo Krastev <roshavi4ak@gmail.com>
Co-authored-by: Luis Peregrina <luis.a.peregrina@gmail.com>
Co-authored-by: MarcSerraPeralta <marcserraperalta@gmail.com>
Co-authored-by: Matjaž T. <matjaz@moj-svet.si>
Co-authored-by: Petri Hämäläinen <petri.hamalainen@mailbox.org>
Co-authored-by: Simen Haugen <simen00@gmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: TA <tobi@warsnich.de>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Veerasak Kritsanapraphan <veerasak.kritsanapraphan@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: chamdim <chamdim@protonmail.com>
Co-authored-by: miksuk28 <mikhail@sukhanik.no>
Co-authored-by: muziqaz <muziqaz@users.noreply.hosted.weblate.org>
Co-authored-by: nanai <ivagamerytmc@gmail.com>
Co-authored-by: naxxerd <top.gear2951@dsme.no>
Co-authored-by: ray ra <verdonsky22@gmail.com>
Co-authored-by: 张建涛 <app521@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>
2026-04-07 15:05:52 +00:00
Dominik Szymański
1d0e5cf18d fix: allow bots to access /s/ urls (#27579)
#27548 Add Allow directive for custom share links social media preview
2026-04-07 09:22:53 -05:00
Min Idzelis
de9ec95db1 fix(web): handle unhandled promise rejection in CancellableTask (#27553)
When a concurrent caller awaits `this.complete` inside `execute()` and
`cancel()` is called, the promise rejects with `undefined` outside of any
try/catch, causing "Uncaught (in promise) undefined" console spam during
rapid timeline scrolling.

- Wrap the `await this.complete` path in try/catch, returning 'CANCELED'
- Guard the `finally` block to only null `cancelToken` if it still belongs
  to this call, preventing a race condition with `cancel()` to `init()`

Change-Id: I65764dd664eb408433fc6e5fc2be4df56a6a6964
2026-04-07 09:22:29 -05:00
renovate[bot]
7f784952eb chore(deps): update dependency rollup-plugin-visualizer to v7 (#27576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 11:01:23 +00:00
renovate[bot]
3d6c7ba353 chore(deps): update dependency @types/supertest to v7 (#27574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 10:57:45 +00:00
renovate[bot]
3be97db118 fix(deps): update react monorepo to v19 (major) (#27571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 12:53:33 +02:00
renovate[bot]
8f3a99ffbc chore(deps): update dependency eslint-plugin-compat to v7 (#27570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 10:52:25 +00:00
renovate[bot]
e6d114af10 chore(deps): update dependency terragrunt to v0.99.5 (#27567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 12:37:29 +02:00
renovate[bot]
4e28811f09 chore(deps): update prom/prometheus docker digest to dda13e2 (#27566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 11:49:05 +02:00
renovate[bot]
4987032e62 chore(deps): update docker.io/valkey/valkey:9 docker digest to 3b55fba (#27559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 11:48:06 +02:00
renovate[bot]
572bad8ede chore(deps): update dependency vite to v8.0.5 [security] (#27543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-06 18:20:10 +00:00
Afonso Mendonça Ribeiro
95c1f0efeb fix: timestamp handling for database backup in Web UI (#27359)
* Fix #26502: Fix timestamp handling for database backup in Web UI

Frontend parsed backup timestamps as UTC, but they were in the
server's local timezone, causing wrong relative times.

Add `timezone` field to DatabaseBackupDto to expose server timezone.
Update frontend to parse timestamps using this timezone.
Convert timestamps to user's local timezone before rendering.
Fallback to browser timezone if server timezone is missing.

Ensures correct relative time display in Web UI.

* fix: regenerate open-api types and remove custom backup type

- Ran `make open-api` to update types based on backend changes
- Removed custom BackupWithTimezone type in MaintenanceBackupsList
- Updated timezone props to use the newly generated native type

* fix: simplify timezone handling for database backups

- Updated DatabaseBackupDto to make timezone a required property
- Removed manual DateTime.local().zoneName fallbacks
- Cleaned up type casts after regenerating OpenAPI types

* fix: Add missing newline at end of spec file
2026-04-06 17:27:48 +02:00
Thomas
fbe631fe91 fix(mobile): convert video controls from hook to stateful widget (#27514)
We are generally looking to move away from hooks as they are hard to
reason about and have weird bugs. In particular, the timer did not
properly capture the ref of the callback, and so it would execute on old
state. A standard stateful widget does not have this problem, and is
easier to organise.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-06 10:13:45 -05:00
Putu Prema
2143a0c935 fix(mobile): transparent system navbar when trash bottom bar is visible (#27093)
* disable bottom safe area on trash bottom bar so that it extends below the system nav bar

* remove manual padding calculations

* re-add static vertical padding to maintain previous bottom bar height
2026-04-06 09:28:07 -05:00
Ray
136bd1e2eb feat(server): Add support for .ts files (#27529) 2026-04-06 15:50:05 +02:00
Min Idzelis
564065a3ed fix(web): reset cursor style when slideshow bar unmounts (#27521) 2026-04-06 14:07:49 +02:00
Min Idzelis
9bcce59719 fix(e2e): fix search gallery delete tests (#27536) 2026-04-06 14:00:50 +02:00
Luis Nachtigall
cd86a83c33 refactor(mobile): introduce image request registry on iOS (#27486)
* refactor: replace DispatchQueue + DispatchSemaphore with OperationQueue for image processing

* implement RequestRegistry and UnfairLock for managing cancellable requests

* implement requests registry for local and remote image processing

* remove Cancellable protocol and cancel method from request registry

* use mutex

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
2026-04-05 18:55:43 -04:00
Min Idzelis
f29c06799f fix(web): isFullScreen initial value check is incorrect (#27520) 2026-04-05 21:19:58 +00:00
Luis Nachtigall
6fcf651d76 refactor(mobile): IOS replace DispatchQueue + DispatchSemaphore with OperationQueue for image processing (#27471)
refactor: replace DispatchQueue + DispatchSemaphore with OperationQueue for image processing
2026-04-05 16:11:02 -04:00
Zack Pollard
196307bca5 chore(server): use dev version check endpoint for non-production environments (#27508) 2026-04-05 10:52:59 +01:00
Thomas
776b9cbad5 fix(mobile): use key on video controls (#27512)
The widget is not recreated correctly when videoPlayName changes, which
can cause weird behaviour with hooks and timers (like timers not firing
when they should do). Using a key forces flutter to recreate the widget
properly and allow the assumptions in build to work correctly.
2026-04-04 21:54:34 -05:00
Thomas
960be0c27a fix(mobile): don't update search filters in-place (#26965)
* fix(mobile): don't update search filters in-place

Search filters are currently modified in-place, which can feel quite
janky. The chips behind the bottom sheet update instantly, and the
search page gets confused because filters have been applied but no
search has been initiated. Filters should keep their own copy of the
filter when they're opened, and the commit + search on apply.

The previous filter and pre-filter concepts were also cleaned up. They
added complexity, and `search()` now owns the full life cycle of the
filter.

This now also reverts the changes from #27155, as this solution should
be cleaner.

* refactor & color tweak

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-04 22:53:03 +00:00
Thomas
123119ca0d chore(mobile): reduce buffering timer duration (#27111)
3 seconds is too long for some people, and it can be confusing to see
the video not playing and no indication that it's buffering. Reducing
the duration of the timer should show the spinner faster and prevent
confusion.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-04 21:25:40 +00:00
Thomas
1772f720bf fix(mobile): reset video controls hide timer when showing controls ch… (#26985)
fix(mobile): reset video controls hide timer when showing controls changes

The hide timer currently only resets when the status of the video
changes, but does not account for when the controls change. This means
that two things happen:

 1. The hide timer does not reset when the controls become visible
    again, the controls will stay visible forever or until the playback
    status changes.
 2. The hide timer will fire too quickly, and will hide the controls
    much sooner than 5 seconds if the controls are hidden and then shown
    again before the hide timer fires.
2026-04-04 16:12:56 -05:00
Thomas
bcc29903de chore(mobile): persist video controls visibility when swiping (#26986)
At current, the controls for videos are always hidden when opening an
asset from the timeline, and when swiping between assets. The latter is
actually quite annoying, so it would be better UX if video controls were
hidden when opening from the timeline like before, but visibility of the
controls was retained when swiping between assets.
2026-04-04 16:11:59 -05:00
Thomas
767caf9bfe fix(mobile): ignore pointer events on toasts (#26990)
These toasts can sometimes cover UI elements and make them impossible to
interact with until they are dismissed. Specifically, deleting an asset
will show a toast over the video controls and prevent seeking.
2026-04-04 10:39:13 -05:00
Min Idzelis
649d14822a refactor(web): rename MonthGroup to TimelineMonth (#27447)
Rename MonthGroup class to TimelineMonth to better convey that it represents a single month within the timeline. Updates the file, class, and all references across 16 files.

Change-Id: Id50fd6d4b7d0e431571b67c0f81c0e316a6a6964
2026-04-03 13:27:12 -04:00
Jason Rasmussen
207672c481 fix: user-agent format (#27488)
* fix: user-agent format

* ci: fix static analysis

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-04-03 12:26:50 -04:00
John Maguire
4fcd9c2e0d feat: add preview button when selecting images (#27305)
* Add preview button when selecting images

* Fix test helper

* prettier

* styling

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2026-04-03 16:21:43 +00:00
renovate[bot]
a2687d674e chore(deps): update dependency lodash-es to v4.18.1 [security] (#27448)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-03 18:16:05 +02:00
renovate[bot]
fb1bc7f9e2 chore(deps): update dependency lodash to v4.18.1 [security] (#27461)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-03 18:15:31 +02:00
Mert
18e8d30b1c fix(server): double exif join (#27485) 2026-04-03 18:14:46 +02:00
Vogeluff
95ef60628c fix(web): always show search type button (#27043)
* fix(web): always show search type button

* fix(web): formatting fixes

* fix(web): search-type-button inactive styling outline/secondary

* chore: styling

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2026-04-03 15:37:03 +00:00
Mees Frensel
a19b7148e5 feat(web): use ui meter component for storage (#27459) 2026-04-03 09:36:22 -05:00
Weblate (bot)
8e414e42f3 chore(web): update translations (#27029)
Co-authored-by: -J- <heyj0e@tuta.io>
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: André Nøbbe Christiansen <andre@nobbe.dk>
Co-authored-by: Arif Budiman <arifpedia@gmail.com>
Co-authored-by: Chao En, Kuo <daniel970275@gmail.com>
Co-authored-by: Cornelius Christiansen <christiansen.cornelius@gmail.com>
Co-authored-by: David Kurniawan <kurniawandavid17@gmail.com>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Deyan Stamboliev <deyan.stamboliev@gmail.com>
Co-authored-by: Fatah Rokbi <fatahrokbi@gmail.com>
Co-authored-by: Felipe Gomes <seutiaoemporio@gmail.com>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Francesco Miccoli <fra03mi@gmail.com>
Co-authored-by: Frank Paul Silye <frankps@gmail.com>
Co-authored-by: Gideon Wentink <gjwentink@gmail.com>
Co-authored-by: HackingAll <hacking.all.YT@gmail.com>
Co-authored-by: Hakan <gucsav@yahoo.com>
Co-authored-by: Hans Cats <hanscats@gmail.com>
Co-authored-by: Happy <59247878+happy2452354@users.noreply.github.com>
Co-authored-by: Hosted Weblate user 85894 <reo7s@users.noreply.hosted.weblate.org>
Co-authored-by: Hurricane_32 <rodrigorimo@hotmail.com>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: Indrek Haav <indrekhaav@users.noreply.hosted.weblate.org>
Co-authored-by: Ivan Dimitrov <idimitrov08@gmail.com>
Co-authored-by: Jarek Iwanus <jiwanus@proton.me>
Co-authored-by: Jeppe Nellemann <jepnel@proton.me>
Co-authored-by: JiZPaper <JiZPaper@gmail.com>
Co-authored-by: Joseph <josephlegrand33+hosted.weblate.org@gmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Link Notig <TestMailProtonWhyNot@protonmail.com>
Co-authored-by: Marin Čorkalo <mcorkalo@gmail.com>
Co-authored-by: Marwan Jalaleddine <marwanjalaleddine@gmail.com>
Co-authored-by: Matjaž T. <matjaz@moj-svet.si>
Co-authored-by: Matthias Hirsch <ma.hirsch.hh@gmail.com>
Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
Co-authored-by: Niklas Trautwein <jniklast@gmail.com>
Co-authored-by: Olaf Nielsen <solluh@mail.de>
Co-authored-by: Oleksandr Yurov <oyurov@icloud.com>
Co-authored-by: Oscar Guillén <osguima3@gmail.com>
Co-authored-by: PigeonPeak <pigeonpeak@proton.me>
Co-authored-by: PontusÖsterlindh <pontus@osterlindh.com>
Co-authored-by: Psycho <unhomen@gmail.com>
Co-authored-by: Roi Gabay <roigby@gmail.com>
Co-authored-by: Runskrift <anders@rimfrost.nu>
Co-authored-by: Sepehr Behroozi <sep.behroozi@gmail.com>
Co-authored-by: Shimul Roy <stenasaha@gmail.com>
Co-authored-by: Skanda <skillwiz94@gmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: Szymon Kucharski <szymon.kucharski5@gmail.com>
Co-authored-by: TA <tobi@warsnich.de>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Tage Lauritsen <tage@tunenet.dk>
Co-authored-by: Tim Morley <weblate.3919org@timsk.org>
Co-authored-by: UDP <udp@users.noreply.hosted.weblate.org>
Co-authored-by: Vegard Fladby <vegard@fladby.org>
Co-authored-by: Xo <xocodokie@users.noreply.hosted.weblate.org>
Co-authored-by: Yllelder <yllelder@gmail.com>
Co-authored-by: anton garcias <isaga.percompartir@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: bural <bural@mailbox.org>
Co-authored-by: chamdim <chamdim@protonmail.com>
Co-authored-by: dacx910 <dacx910@users.noreply.hosted.weblate.org>
Co-authored-by: dark fury <nodo05nodo05@gmail.com>
Co-authored-by: dark&white <darkwhite@users.noreply.hosted.weblate.org>
Co-authored-by: dvbthien <dvbthien@users.noreply.hosted.weblate.org>
Co-authored-by: fabiosequeira <fabio.sequeira.t0126448@edu.atec.pt>
Co-authored-by: josu. <josugarralda@gmail.com>
Co-authored-by: kylo32 <kylo32@gmail.com>
Co-authored-by: millallo <millallo@tiscali.it>
Co-authored-by: muziqaz <muziqaz@users.noreply.hosted.weblate.org>
Co-authored-by: pcnc <paul@cioanca.eu>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: pythoncontroller <zinovlaun@gmail.com>
Co-authored-by: stefano trubian <trubianstefano@gmail.com>
Co-authored-by: waclaw66 <waclaw66@seznam.cz>
Co-authored-by: walpeDEV <walpe.aw@proton.me>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
Co-authored-by: 藍焰-0Blue_Yan0 <jim080825@gmail.com>
2026-04-03 00:04:17 +01:00
Zack Pollard
db0f86c749 feat: move version checks to our own infrastructure (#27450) 2026-04-02 23:32:26 +01:00
Jason Rasmussen
adb6b39eec fix: migrations (#27477) 2026-04-02 17:49:26 -04:00
Jason Rasmussen
c8ae99e7d7 fix: escape html (#27469) 2026-04-02 15:19:24 -04:00
Alex
37823bcd51 feat: create new person in face editor (#27364)
* feat: create new person in face editor

* add delay

* fix: test

* i18n

* fix: unit test

* pr feedback
2026-04-02 15:28:40 +00:00
Mees Frensel
b465f2b58f fix: scrollbar ui theme colors (#27455) 2026-04-02 10:18:47 -05:00
Min Idzelis
2166f07b1f refactor(web): rename DayGroup to TimelineDay (#27446)
Rename DayGroup class to TimelineDay to better convey that it represents
a single day within the timeline. Updates the file, class, and all
references across 13 files.

Change-Id: I9faef9bad73cd5b11f40daaf5eb140dd6a6a6964
2026-04-01 19:30:54 -04:00
Min Idzelis
c9e251c78c feat(web): highlight active person thumbnail in detail panel and edit faces panel (#27401)
- Dim non-hovered person thumbnails to 40% opacity when any face is active
- Add ring highlight on the active person's thumbnail
- Add focus-visible outline styling for keyboard navigation
- Apply same treatment to both detail panel people section and edit faces side panel

Change-Id: I4ac10fe4568b95f3e0e8d9104133180f6a6a6964

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-04-01 10:49:09 -05:00
Mees Frensel
da4b88fc14 fix(web): transition bg and border-radius (#27438)
* fix(web): transition bg and border-radius

* also transition thumb
2026-04-01 09:34:49 -05:00
okxint
d1e2e8ab4e fix(server): use substring matching for person name search (#26903) 2026-04-01 13:31:54 +00:00
Timon
2a619d3c10 fix(web): Enable stack selector in shared album view (#24641) 2026-04-01 15:19:14 +02:00
Brandon Wees
c29493e3a0 fix: withFilePath select edited or unedited file (#27328)
* fix: withFilePath select edited or unedited file

* chore: test
2026-04-01 08:19:38 -04:00
renovate[bot]
4ef777d145 chore(deps): update dependency handlebars to v4.7.9 [security] (#27334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-01 08:17:58 -04:00
renovate[bot]
0b40f4fd76 chore(deps): update dependency happy-dom to v20.8.9 [security] (#27350)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-01 08:16:45 -04:00
bo0tzz
ecba4e2a62 chore: tee GITHUB_OUTPUT for debugging (#27378) 2026-04-01 08:15:43 -04:00
Michel Heusschen
4eb531197e fix(web): prevent AssetUpdate from adding unrelated timeline assets (#27369) 2026-04-01 08:14:28 -04:00
Alex
505a07a825 feat: add move to lock folder in folder view (#27384) 2026-04-01 08:10:39 -04:00
Robin Meese
548dbe8ad6 feat(docs): add keycloack example to oauth docs (#27425) 2026-04-01 13:39:36 +02:00
renovate[bot]
0c184940f4 chore(deps): update github-actions (#27416)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-31 15:58:36 +00:00
Channing Bellamy
be180fd9da fix: detection of WebM container (#24182) 2026-03-31 11:44:51 -04:00
renovate[bot]
859f58174e chore(deps): update node.js to v24.14.1 (#27412)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-31 12:46:38 +02:00
renovate[bot]
a6c7e76008 chore(deps): update grafana/grafana docker tag to v12.4.2 (#27411)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-31 12:39:33 +02:00
renovate[bot]
0ff94213e6 chore(deps): update dependency exiftool-vendored to v35.15.1 (#27415)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-31 12:39:11 +02:00
Yaros
6b1dd6f680 fix(mobile): favorite button not updating state (#27271) 2026-03-30 21:24:56 -05:00
Min Idzelis
7d4286bbc5 fix(web): add drop shadow to asset viewer nav bar and prevent button shrinking (#27404)
- Add subtle drop shadow to the asset viewer nav bar for better visual
  separation from the image behind it
- Add drop shadow to the OCR text recognition button in the lower right
- Prevent nav bar action buttons from shrinking to nothing by adding
  *:shrink-0 to the flex container, with p-1/-m-1 to avoid clipping
  focus outlines

Change-Id: I61cdc0ec66a65cde1c95b40c2c5428006a6a6964
2026-03-30 19:22:10 -05:00
Min Idzelis
18201a26d9 feat(web): OCR overlay interactivity during zoom (#27039)
Change-Id: Id62e1a0264df2de0f3177a59b24bc5176a6a6964
2026-03-30 19:19:53 -05:00
Daniel Dietzler
a2e3635ac9 chore: use esm global import (#27408) 2026-03-31 00:22:07 +02:00
Min Idzelis
ce346bf956 feat(web): dim photo outside hovered face bounding box (#27402)
When hovering over a detected face in the photo viewer, an SVG mask overlay
dims the rest of the image (40% black) while leaving the hovered face fully
visible. The overlay fades in/out smoothly via CSS opacity transition by
freezing the last highlighted box positions in state, preventing the overlay
from popping off instantly when the mouse leaves.

Change-Id: I07e2eb2b297820ec89812785fe7943846a6a6964
2026-03-30 16:16:38 -05:00
Mert
a1a2939868 fix(mobile): low upload timeout on android (#27399)
fix timeout
2026-03-30 16:05:21 -05:00
renovate[bot]
e8309585d6 fix(deps): update dependency nodemailer to v8 [security] (#27351)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-30 19:44:39 +02:00
Jason Rasmussen
17d4941089 refactor: asset select manager (#27330) 2026-03-30 15:45:57 +01:00
Tony Fung
b09ebb11e9 perf(server): optimize people page query (#27346)
Optimize People page query: modified SQL to use index for faster performance
2026-03-28 21:33:05 +00:00
Nicolas-micuda-becker
181b028b09 fix(web): keep upload totals stable when dismissing items (#27247) (#27354)
* fix(web): keep upload totals stable when dismissing items (#27247)

* chore: remove package-lock.json

---------

Co-authored-by: bwees <brandonwees@gmail.com>
2026-03-28 16:25:44 -05:00
bo0tzz
eb20b715e4 fix: don't auto-close manually reopened PRs (#27347) 2026-03-28 12:50:09 +00:00
Luis Nachtigall
a277c6311f fix(mobile): streamline error handling for live photo saving (#27337) 2026-03-27 19:07:38 -05:00
Jason Rasmussen
5889c42eb6 refactor: asset select manager (#27329) 2026-03-27 14:23:33 -04:00
Jason Rasmussen
14cce0cba3 refactor: asset select manager (#27327) 2026-03-27 13:48:51 -04:00
Jason Rasmussen
9b80ffd9c6 refactor: selection mananger (#27325) 2026-03-27 12:41:52 -04:00
Luis Nachtigall
306a3b8c7f fix(mobile): images loads sometimes cancel too early (#27067)
* refactor listener tracking for image stream completers and fix early cancel call

* fix: improve cache listener identification in image stream tracking

* add documentation and test cases for listener tracking in ImageStreamCompleter

* fix: remove unnecessary image provision flag from listener tracking

* fix: override setImage method in cache aware listener tracker mixin

* fix: rename test file
2026-03-27 10:35:50 -04:00
Putu Prema
be0fc403d8 fix(mobile): mismatch between system and app color when using low-chroma system color scheme (#27282)
use DynamicSchemeVariant.fidelity to preserve low-chroma system color scheme as the app color
2026-03-27 09:21:43 -05:00
Yaros
c13fd9e4b5 fix(mobile): video icon not showing on memories (#27311) 2026-03-27 09:11:02 -05:00
Thomas
8724848fce chore(mobile): reduce spacing on video controls (#27313)
The spacing was required for the old slider, but the new one has its own
spacing and makes it redundant. There is too much now, and we've
received feedback that it should be less sparse. The default track
height of 16px is an improvement over the old track height, but it is
very thick. A middleground of 12px might be better.
2026-03-27 09:10:19 -05:00
Min Idzelis
2d950db940 refactor(web): replace intersection booleans with enum (#27306)
Change-Id: I0c9703d5960031142ae47fef23805e0a6a6a6964
2026-03-27 08:37:12 -04:00
Min Idzelis
4b9ebc2cff refactor(web): migrate isFaceEditMode from standalone store to assetViewerManager (#27307) 2026-03-27 13:20:15 +01:00
Saurav Sharma
e2d26ebdea fix(web): prevent Safari from overwriting live photo image with video (#26898)
When downloading a live photo, Safari overwrites the image file with
the motion video because both share the same base filename. Append
'-motion' suffix to the video filename to prevent collision.

For example, IMG_1234.heic and IMG_1234.mov become IMG_1234.heic
and IMG_1234-motion.mov.

Fixes #23055
2026-03-26 14:37:05 -04:00
Phlogi
8c6adf7157 feat(server): resolve duplicates (#25316)
* feat(web): Synchronize information from deduplicated images

* Added new settings menu to the the deduplication tab.
* The toggable options in the settings are synchronization of: albums, favorites, ratings, description, visibility and location.
* When synchronizing the albums, the resolved images will be added to all albums of the duplicates.
* When synchronizing the favorite status, the resolved images will be marked as favorite, if at least one selectable image is marked as favorite.
* When synchronizing the ratings, the highest rating from the selectable images will be applied to the resolved image.
* When synchronizing the description, all descriptions from the selectable images will be merged into one description for the resolved image.
* When synchronizing the visibility, the most restrictive visibility setting from the selectable images will be applied to the resolved image.
* When synchronizing the location, if exactly one unique location exists among the selectable images, this location will be applied to the resolved image.
* There is no additional UI for these settings to keep the visual clutter minimal. The settings are applied automatically based on the user's preferences.

* Replace addAssetToAlbums with copyAsset

* fix linter

* feat(web): add duplicate sync fields and fix typo

* feat(web): add tag sync and enhance duplicate resolution

This update introduces tag synchronization for duplicate resolution,
ensuring all unique tag IDs from duplicates are applied to kept assets.
The visibility sync logic is updated to use a simplified ordering, as the hidden status items will never show up in a duplicate set.
Album synchronization now merges albums directly via addAssetsToAlbums; as the approach with copyAsset API endpoint was ineffiecient.
Description, rating, and location sync logic is improved for correctness.
and deduplication. i18n strings were added / updated.

* feat(server): move duplicate resolution to backend with sync and stacking

Moves duplicate metadata synchronization from frontend to backend, enabling robust
batch operations and proper validation. This is an improved refactor of PR #13851.

New endpoints:
- POST /duplicates/resolve - batch resolve with configurable metadata sync
- POST /duplicates/stack - create stacks from duplicate groups
- GET /duplicates - now includes suggestedKeepAssetIds based on file size and EXIF

Key changes:
- Move sync logic (albums, tags, favorites, ratings, descriptions, location, visibility) to server
- Add server-side metadata merge policies with proper conflict resolution
- Replace client-side resolution logic with new backend endpoints
- Add comprehensive E2E tests (70+ test cases) and unit tests
- Update OpenAPI specs and TypeScript SDK

No breaking changes - only additions to existing API.

* feat(preferences): enable all duplicate sync settings by default

* chore: clean up

* chore: clean up

* refactor: rename & clean up

* fix: preference upgrade

* chore: linting

* refactor(e2e): use updateAssets API for setAssetDuplicateId

* fix: visibility sync logic in duplicate resolution

* fix(duplicate): write description to exifUpdate

Previously the duplicate resolution populated assetUpdate.description even
though description belongs to exif info.

* fix(duplicate): remove redundant updateLockedColumns wrapper

updateAllExif already computes lockedProperties via distinctLocked
using Object.keys(options). The wrapper added a lockedProperties key
to the options object, causing the spurious string 'lockedProperties'
to be stored in the lockedProperties array.

* fix(duplicate): write merged tags to asset_exif to survive metadata re-extraction

During duplicate resolution, replaceAssetTags correctly wrote merged tag
IDs to the tag_asset table, but never updated asset_exif.tags or locked
the tags property. The subsequent SidecarWrite → AssetExtractMetadata
chain calls applyTagList, which destructively replaces tag_asset rows
with whatever is in asset_exif.tags — still the original per-asset tags,
not the merged set.

Write merged tag values to asset_exif.tags via updateAllExif (which also
locks the property via distinctLocked), and queue SidecarWrite when tags
change so they persist to the sidecar file.

* docs(duplicates): clarify location and tag sync behavior

* refactor(duplicate): remove sync settings, always sync all metadata on resolve

Remove DuplicateSyncSettingsDto and the per-field sync toggles
(albums, favorites, rating, description, visibility, location, tags).
Duplicate resolution now unconditionally syncs all metadata from
trashed assets to kept assets.

- Remove DuplicateSyncSettingsDto and settings field from DuplicateResolveDto
- Update DuplicateService to always run all sync logic without conditionals
- Delete DuplicateSettingsModal.svelte and settings gear button from UI
- Remove DuplicateSettings type and duplicateSettings persisted store
- Update unit and e2e tests to remove settings from resolve requests

* docs: update duplicates utility to reflect automatic metadata sync

* docs(web): replace duplicates info modal with link to documentation

* chore: clean up

* fix: add missing type cast to jsonAgg in duplicate repository getAll

* fix: skip persisting rating=0 in duplicate merge to avoid unnecessary sidecar write

---------

Co-authored-by: Toni <51962051+EinToni@users.noreply.github.com>
Co-authored-by: Jason Rasmussen <jason@rasm.me>
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2026-03-26 18:33:55 +00:00
Mees Frensel
48fdd39d30 feat(web): use ui pin input element (#27200) 2026-03-26 18:24:46 +00:00
Jonathan Jogenfors
22bf7c2005 feat(server): add checksum algorithm field (#26573)
* feat: add checksum algorithm field

* fix comments

* chore: rename migration

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-03-26 18:20:25 +00:00
Mees Frensel
47b45453c8 chore(web): refactor activity status (#26956)
* chore(web): refactor activity status

* fix: size change

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-03-26 18:15:42 +00:00
Robin Wohlers-Reichel
448c069fb6 feat(web): add shortcuts to rotate images (#26927) 2026-03-26 19:13:01 +01:00
Diogo Tavares Sendim Fernandes
958f270f0d fix(web): keep map view open after closing asset viewer (#26980) 2026-03-26 18:11:05 +00:00
renovate[bot]
9f699fdfc3 chore(deps): update typescript-projects (#26973)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-03-26 19:02:27 +01:00
renovate[bot]
00da7b88a1 chore(deps): update dependency @types/node to ^24.12.0 (#26966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 13:52:45 -04:00
Michel Heusschen
144a57ddff refactor(server): use helpers for shared link queries (#27088)
* fix(server): prevent album shared link from breaking after uploads

* update test

* add withSharedAssets helper

* remove options

* add more helpers

* update selects
2026-03-26 13:51:00 -04:00
Daniel Dietzler
1bd2d474d7 fix: various comamnd palette usages (#27304) 2026-03-26 17:45:14 +00:00
Jason Rasmussen
b33874ef12 feat: add support for helmet configuration (#27058) 2026-03-26 17:41:23 +00:00
bo0tzz
dbaf4b548b fix: pin success-check-action to correct tag (#27230) 2026-03-26 17:37:23 +00:00
Jason Rasmussen
7d58d5be12 refactor: memory manager (#27206) 2026-03-26 17:36:25 +00:00
renovate[bot]
42fe86d24c chore(deps): update grafana/grafana docker tag to v12.4.1 (#26969)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 18:31:08 +01:00
Michael Maycock
eeb55c279b fix(web): preserve timezone when changing timestamp (Closes #25354) (#27095) 2026-03-26 17:30:47 +00:00
renovate[bot]
5c159d70a7 chore(deps): update node.js to v24.14.0 (#26972)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 18:30:38 +01:00
Paul Makles
44ae0fa7ed fix(database restores): don't assume onboarding has completed (#27052) 2026-03-26 18:30:14 +01:00
renovate[bot]
f782782662 fix(deps): update dependency kysely to v0.28.14 [security] (#27068)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 18:29:53 +01:00
renovate[bot]
4436cab827 chore(deps): update dependency yaml to v2.8.3 [security] (#27293)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 18:28:46 +01:00
renovate[bot]
74789ad1c4 chore(deps): update dependency picomatch to v4.0.4 [security] (#27281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 18:28:05 +01:00
Jason Rasmussen
7877097b3f refactor: asset viewing store (#27204) 2026-03-26 13:22:40 -04:00
Jason Rasmussen
fb84c1cf61 chore: remove unused file (#27202) 2026-03-26 13:22:31 -04:00
Jason Rasmussen
940a1d4ab8 refactor: change location (#27201) 2026-03-26 13:22:14 -04:00
Jason Rasmussen
fae25dbe65 chore: whitelist server deploy files (#27056) 2026-03-26 13:22:03 -04:00
Mert
8dd0d7f34c fix(server): memory fragmentation (#27027) 2026-03-26 18:21:52 +01:00
Jason Rasmussen
9b78f2c0ba chore: remove unused resources (#27055) 2026-03-26 13:21:36 -04:00
Timon
67cedfef17 feat(web): add RemoveFromAlbumAction to asset viewer nav bar (#27000) 2026-03-26 18:20:28 +01:00
Andreas Heinz
c9c2322b9d feat(web): focus on face-editor search input (#27136) 2026-03-26 18:18:23 +01:00
Daniel Dietzler
389356149a refactor: actionable toasts (#27203) 2026-03-26 18:18:06 +01:00
Michel Heusschen
4812a2e2d8 fix(server): refresh unedited asset dimensions on metadata extraction (#27220) 2026-03-26 18:17:32 +01:00
Vogeluff
8f01d06927 feat(web): add a seperate tooltip for switching from dark to light mode (#27297) 2026-03-26 18:15:16 +01:00
github-actions
a2ff075e9a chore: version v2.6.3 2026-03-26 16:23:35 +00:00
Brandon Wees
d8b39906f9 fix: incorrect asset face sync (#27243)
* fix: incorrect asset face sync

* chore: sync sql
2026-03-26 09:39:02 -05:00
Michel Heusschen
b36911a16b fix(server): filter out empty search suggestions (#27292)
* fix(server): filter out empty search suggestions

* make sql
2026-03-26 09:36:04 -05:00
Alex
b074ee202e chore: move slideshow control button group to the left (#27287) 2026-03-26 14:31:11 +00:00
bo0tzz
78bb6cf926 chore: log id of existing asset on duplicate upload (#27266) 2026-03-26 11:50:53 +01:00
Yaros
c980f5fc19 chore(docs): withPeople parameter description (#27262)
* fix(server): withPeople inconsistent

* fix: query failing in some occasions

* test: add medium tests for withPeople option

* Revert "test: add medium tests for withPeople option"

This reverts commit 6c1505ba6b.

* Revert "fix: query failing in some occasions"

This reverts commit 221feeca45.

* Revert "fix(server): withPeople inconsistent"

This reverts commit 4289a9f23d.

* chore: change endpoint description

* chore: generate open-api
2026-03-26 11:50:29 +01:00
Yaros
a26d9e05ba fix(web): shifting motion image button (#27275) 2026-03-26 11:49:21 +01:00
bo0tzz
c862163204 fix: explicitly specify repo in auto-close job (#27291) 2026-03-26 10:43:51 +00:00
Michel Heusschen
5fb8f9bf1a fix(web): prevent horizontal scroll bar in asset viewer side panel (#27270)
* fix(web): prevent horizontal scroll bar in asset viewer side panel

* simplify
2026-03-25 21:02:31 -05:00
Mees Frensel
b9b5dba037 fix(web): crop square ratio i18n (#27257) 2026-03-25 14:05:43 -05:00
renovate[bot]
8bfa75087c chore(deps): update base-image to v202603251709 (major) (#27273)
chore(deps): update base-image to v202603251709

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-25 14:04:26 -05:00
bo0tzz
95280edd6c fix: let renovate update base images (#27272) 2026-03-25 18:00:40 +00:00
Mert
a9666d2cef fix(mobile): remove upload timeout (#27237)
remove timeout
2026-03-24 14:40:48 -04:00
renovate[bot]
4af9edc20b chore(deps): update github-actions (#27215)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-24 14:31:00 +01:00
renovate[bot]
c975fe5bc7 chore(deps): update github-actions (major) (#27225)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-24 12:40:10 +00:00
renovate[bot]
12a4d8e2ee chore(deps): update ghcr.io/jdx/mise docker tag to v2026.3.12 (#27224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-24 12:06:19 +00:00
github-actions
ce9b32a61a chore: version v2.6.2 2026-03-24 02:51:55 +00:00
Yaros
4ddc288cd1 fix(mobile/web): album cover buttons consistency (#27213)
* fix(mobile/web): album cover buttons consistency

* test: adjust test
2026-03-23 21:40:17 -05:00
Yaros
94b15b8678 fix(server): album permissions for editors (#27214)
* fix(server): album permissions for editors

* test: adjust e2e test

* test: fix test
2026-03-23 21:39:30 -05:00
Daniel Dietzler
ff9ae24219 fix: album picker show all albums (#27211) 2026-03-23 19:08:57 -05:00
Matthew Momjian
b456f78771 fix(docs): clarify ML CPU architecture (#27187)
* ML architecture

* format

* clarify amd/arm
2026-03-23 18:29:58 -04:00
Mert
1506776891 fix(mobile): add cookie for auxiliary url (#27209)
add cookie before validating
2026-03-23 16:22:46 -05:00
Yaros
0e93aa74cf fix(mobile): add keys to people list (#27112)
mobile(fix): add keys to people list
2026-03-23 10:50:56 -05:00
Yaros
e95ad9d2eb fix(mobile): option padding on search dropdowns (#27154)
* fix(mobile): option padding on search dropdowns

* chore: prevent height fill up screen and block the bottom menu entry

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-23 15:03:07 +00:00
Nicolas-micuda-becker
b98a227bbd fix(web): update upload summary when removing items (#27035) (#27139) 2026-03-23 10:02:09 -05:00
Michel Heusschen
2dd785e3e2 fix(web): restore duplicate viewer arrow key navigation (#27176) 2026-03-23 10:01:15 -05:00
Daniel Dietzler
7e754125cd fix: download original stale cache when edited (#27195) 2026-03-23 10:00:32 -05:00
Yaros
e2eb03d3a4 fix(mobile): star rating always defaults to 0 (#27157) 2026-03-23 09:56:27 -05:00
Yaros
bf065a834f fix(mobile): no results before applying filter (#27155) 2026-03-23 09:41:13 -05:00
Daniel Dietzler
db79173b5b chore: vite 8 (#26913) 2026-03-23 15:39:46 +01:00
Yaros
33666ccd21 fix(mobile): view similar photos from search (#27149)
* fix(mobile): view similar photos from search

* clean up

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2026-03-23 09:36:42 -05:00
bo0tzz
be93b9040c feat: consolidate auto-close workflows (#27172) 2026-03-23 11:22:44 +01:00
Luis Nachtigall
00dae6ac38 fix(mobile): cronet image cache clearing on android (#27054) 2026-03-20 18:28:24 -04:00
Daniel Dietzler
5a8fd40dc5 fix: svelte reactivity issues (#27109) 2026-03-20 15:56:16 -04:00
Jason Rasmussen
813d684aaa fix: shared link add to album (#27063) 2026-03-20 13:14:07 -05:00
Luis Nachtigall
644f705be1 fix(mobile): correct maximumSizeBytes setter to use maximumSizeBytes property (#27098)
fix: correct maximumSizeBytes setter to use maximumSizeBytes property
2026-03-20 12:22:59 -04:00
Thomas
f3e4bcc733 fix(server): queue version check job when config changed (#27094)
Nothing happens when the version checks are enabled, which means the
server may return very stale versions until the next job runs.

Refs: #26935
2026-03-20 10:07:40 -05:00
Michel Heusschen
9a0c17fdb8 fix(web): preserve album scroll when adding to other albums (#27078) 2026-03-20 09:42:19 -05:00
Michel Heusschen
b7c4497dfd fix(web): allow showing combobox items outside modals (#27075)
fix(web): allow showing combobox items outside of modals
2026-03-20 09:41:34 -05:00
Yaros
9c227aeaf5 fix(mobile): simplified chinese not available (#27066) 2026-03-20 00:27:50 -05:00
github-actions
e939fde6f1 chore: version v2.6.1 2026-03-19 17:56:42 +00:00
Mert
019beaed0b fix(mobile): server url migration (#27050)
decode json
2026-03-19 12:52:51 -05:00
Jason Rasmussen
0e4d6d4eac fix(web): disable send button (#27051)
fix(web): disable button while sending
2026-03-19 17:39:13 +00:00
Daniel Dietzler
79f978ddeb fix: writing empty exif tags (#27025) 2026-03-19 17:17:56 +00:00
Jason Rasmussen
f2445ecab1 fix(web): stop in-progress uploads on logout (#27021) 2026-03-19 13:05:11 -04:00
Jason Rasmussen
86311e3cfe fix(web): wrap long album title (#27012) 2026-03-19 13:04:56 -04:00
Jason Rasmussen
29000461c2 fix: ignore errors deleting untitled album (#27020) 2026-03-19 12:55:43 -04:00
Mees Frensel
b30373b24f fix: release rootles compose file (#27048) 2026-03-19 16:45:11 +00:00
bo0tzz
bc2439883a fix: track PR template auto-close with label (#27042)
* fix: track PR template auto-close with label

* fix: format
2026-03-19 12:42:29 -04:00
Jason Rasmussen
044257531e fix(server): fallback to email when name is empty (#27016) 2026-03-19 12:41:20 -04:00
github-actions
f413f5c692 chore: version v2.6.0 2026-03-18 20:37:39 +00:00
Weblate (bot)
52307ed09f chore(web): update translations (#26192)
Translate-URL: https://hosted.weblate.org/projects/immich/immich/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/af/
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/bg/
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/de_CH/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/el/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/eo/
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/eu/
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/ga/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/gl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/gsw/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/gu/
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/hu/
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/km/
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/lt/
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/ms/
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/sr_Cyrl/
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/te/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/th/
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/vi/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/yue_Hant/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hant/
Translation: Immich/immich

Co-authored-by: -J- <heyj0e@tuta.io>
Co-authored-by: Abdullah Atsa <abdullahatsauban@gmail.com>
Co-authored-by: Adam Havránek <adamhavra@seznam.cz>
Co-authored-by: Ahmed Hamdy <ahmedhamdy19375@gmail.com>
Co-authored-by: Ahmed Khaleel Shihab <ahmed91shihab@gmail.com>
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Aleksa Milošević <akimaki15@gmail.com>
Co-authored-by: Anton Palmqvist <apq@users.noreply.hosted.weblate.org>
Co-authored-by: Antón Gómez López <antongomez03@gmail.com>
Co-authored-by: Arif Budiman <arifpedia@gmail.com>
Co-authored-by: Balázs R <nvi9@outlook.hu>
Co-authored-by: Baptiste Mille-Mathias <baptiste.millemathias@alcmeon.com>
Co-authored-by: Berkay Görgülü <berthanas@gmail.com>
Co-authored-by: Bora Atıcı <boratici.acc@gmail.com>
Co-authored-by: CHUNG, Jin-ho <doctorjinho@gmail.com>
Co-authored-by: Cesare Capo <cescapo@icloud.com>
Co-authored-by: Chintan Prajapati <chintan9@student.fdu.edu>
Co-authored-by: Denis Pacquier <denis.pacquier@gmail.com>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Dusan Hlavaty <dhlavaty@gmail.com>
Co-authored-by: Dymitr <zasvab@gmail.com>
Co-authored-by: Dániel Gál <galdaniel.school@gmail.com>
Co-authored-by: Emanuel Santos Martins <santosmartinsemanuel@gmail.com>
Co-authored-by: Emanuele La Mura <acedit.wcry@gmail.com>
Co-authored-by: Farkas Rábai <farkasrabai@gmail.com>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Floruit <1964993179@qq.com>
Co-authored-by: Frank Paul Silye <frankps@gmail.com>
Co-authored-by: Gabriel <jellyfin.sensitize624@passmail.net>
Co-authored-by: Gideon Wentink <gjwentink@gmail.com>
Co-authored-by: Goran Aničin <anicin.goran@gmail.com>
Co-authored-by: HackingAll <hacking.all.YT@gmail.com>
Co-authored-by: Happy <59247878+happy2452354@users.noreply.github.com>
Co-authored-by: Haru Ijima <haruijimakun@gmail.com>
Co-authored-by: Hosted Weblate user 85894 <reo7s@users.noreply.hosted.weblate.org>
Co-authored-by: Hurricane_32 <rodrigorimo@hotmail.com>
Co-authored-by: IMPERA <rickbrouwer2005premium@gmail.com>
Co-authored-by: Ian.Su <stu92116stu92116@hotmail.com.tw>
Co-authored-by: Immich <weblate@immich.app>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: IsakBH <isak@brunhenriksen.net>
Co-authored-by: Ismail <binhelati@gmail.com>
Co-authored-by: Ivan Dimitrov <idimitrov08@gmail.com>
Co-authored-by: Jannik Norden <development.jnorden@outlook.com>
Co-authored-by: Jeppe Nellemann <jepnel@proton.me>
Co-authored-by: Joseph <josephlegrand33+hosted.weblate.org@gmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Junghyuk Kwon <kwon@junghy.uk>
Co-authored-by: KW Lam <kwlam281@gmail.com>
Co-authored-by: Kaushal Patel <kaushalpatel1982@gmail.com>
Co-authored-by: Kim Hokyeong <manmen.mi1375@gmail.com>
Co-authored-by: Krzysztof <doom.zg@gmail.com>
Co-authored-by: Leo Bottaro <github@leobottaro.com>
Co-authored-by: Leon S <leonnis16@gmail.com>
Co-authored-by: Liviu Roman <contact@liviuroman.com>
Co-authored-by: Londoneye02 <jcdelcaz@gmail.com>
Co-authored-by: Luca Beyer <trickobert@gmail.com>
Co-authored-by: Lukas Konsin <lukaskonsin@proton.me>
Co-authored-by: Madipodo <madipo@posteo.de>
Co-authored-by: Marc Casillas <mcasillassu@gmail.com>
Co-authored-by: MarcSerraPeralta <marcserraperalta@gmail.com>
Co-authored-by: Marian Wolf <marian.wolf2008@gmail.com>
Co-authored-by: Matjaž T. <matjaz@moj-svet.si>
Co-authored-by: Mayukh Roy <mayukhroy2020@gmail.com>
Co-authored-by: Mees Frensel <meesfrensel@gmail.com>
Co-authored-by: Meet Bhingradiya <meetbhingradiya36@gmail.com>
Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
Co-authored-by: MrZapp-LM <paul.lutz@lichtermacher.at>
Co-authored-by: Muhammad Ivan Aldorino <ivanaldorino@gmail.com>
Co-authored-by: Mykola Vaskevych <22372199@studentmail.ul.ie>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Olaf Nielsen <solluh@mail.de>
Co-authored-by: PPNplus <ppnplus@protonmail.com>
Co-authored-by: PaCoalt <pschwarzwaelder@yahoo.com>
Co-authored-by: Petri Hämäläinen <petri.hamalainen@mailbox.org>
Co-authored-by: Philip Goto <philip.goto@gmail.com>
Co-authored-by: Philipp Frauenfelder <philipp.frauenfelder@gmail.com>
Co-authored-by: PhillyMay <mein.alias@outlook.com>
Co-authored-by: Phyruos <phyruos@gmail.com>
Co-authored-by: Predrag Milićević <predragmilicevic@gmail.com>
Co-authored-by: Psycho <unhomen@gmail.com>
Co-authored-by: Ramon \"9Tails\" Canales <emaildoramon@gmail.com>
Co-authored-by: Rebelder <bas+github@bbelder.eu>
Co-authored-by: Reo7s <biz@reo-ang.net>
Co-authored-by: Roger Veciana Rovira <rveciana@gmail.com>
Co-authored-by: Roman Fedorchuk <roma.fedorchuk@gmail.com>
Co-authored-by: Ruben Silva <ruben02b@gmail.com>
Co-authored-by: Shadi Alrashoodi <shadifaisal91@gmail.com>
Co-authored-by: Shashibhushan Singh <shashibhushansingh11@gmail.com>
Co-authored-by: Shawn <xiaxinx@gmail.com>
Co-authored-by: Shihfu Juan <xlion@xlion.tw>
Co-authored-by: Skanda <skillwiz94@gmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: TA <tobi@warsnich.de>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Thomas Grimstad <solmester@gmail.com>
Co-authored-by: Thomas Vente <thomas.vente@gmail.com>
Co-authored-by: Tim Morley <weblate.3919org@timsk.org>
Co-authored-by: TobnacTobi <Tobias.Schrull@gmx.de>
Co-authored-by: Toivo Schmale <thetoicraft@gmail.com>
Co-authored-by: Tomo Tomov <tomotomov92@gmail.com>
Co-authored-by: Ugnius <kaugnius@gmail.com>
Co-authored-by: Ulices <hasecilu@tuta.io>
Co-authored-by: Ulpi Antor <weblate.residual441@passmail.net>
Co-authored-by: Unn Krigul <unn@arter.studio>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Vallabh Sharma <srivallabhsharma@gmail.com>
Co-authored-by: Vegard Fladby <vegard@fladby.org>
Co-authored-by: Viesturs <viesturs.sprogis@outlook.com>
Co-authored-by: Vivek M <vivekmalhotra004@gmail.com>
Co-authored-by: Vixepti <contact@vixepti.fr>
Co-authored-by: Xo <xocodokie@users.noreply.hosted.weblate.org>
Co-authored-by: Zawaer <zawarudo123pro@gmail.com>
Co-authored-by: anton garcias <isaga.percompartir@gmail.com>
Co-authored-by: ashraf0484 <t.sashraf@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: chamdim <chamdim@protonmail.com>
Co-authored-by: dimitrijeforesta <dimitrije.foresta@gmail.com>
Co-authored-by: eav5jhl0 <eav5jhl0@users.noreply.hosted.weblate.org>
Co-authored-by: jellebuitenhuis <github@jellebuitenhuis.nl>
Co-authored-by: kylo32 <kylo32@gmail.com>
Co-authored-by: l m <virtuamoo@gmail.com>
Co-authored-by: millallo <millallo@tiscali.it>
Co-authored-by: muziqaz <muziqaz@users.noreply.hosted.weblate.org>
Co-authored-by: mycodeco <victorschiellerup@gmail.com>
Co-authored-by: nachtpfoetchen <nachtpfoetchen@posteo.de>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: stesoma <soma.steltzer@gmail.com>
Co-authored-by: techmoocher 🦊 <ndvphuc276@gmail.com>
Co-authored-by: timmy61109 <qazzxcasdqwewsxedc@gmail.com>
Co-authored-by: waclaw66 <waclaw66@seznam.cz>
Co-authored-by: 안세훈 <on9686@gmail.com>
2026-03-18 20:27:17 +00:00
Jason Rasmussen
77020e742a fix: validate accept header before returning html (#27019) 2026-03-18 14:15:48 -04:00
Jason Rasmussen
38b135ff36 fix: bounding box return type (#27014) 2026-03-18 11:58:40 -04:00
Jason Rasmussen
cda4a2a5fc fix: filter after searching by asset id (#26994)
* fix: filter after searching by asset id

* Update web/src/lib/modals/SearchFilterModal.svelte

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

---------

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
2026-03-18 13:32:54 +00:00
Min Idzelis
88002cf7fe fix(web): allow images to be downloaded again(long-press or right click) (#26992) 2026-03-18 12:40:36 +01:00
Andreas Heinz
694ea151f5 fix(web): escape handling for tagging and adding a face in asset viewer (#26870) 2026-03-18 12:39:25 +01:00
Jason Rasmussen
b092c8b601 fix: healthcheck (#26989) 2026-03-17 17:54:39 -04:00
Jason Rasmussen
48e6e17829 feat: primary notifications (#26988) 2026-03-17 17:54:11 -04:00
Jason Rasmussen
0519833d75 refactor: prefer tv (#26993) 2026-03-17 17:53:48 -04:00
Thomas
34caed3b2b fix(server): flaky metadata tests (#26964) 2026-03-17 18:06:22 +01:00
Thomas
677cb660f5 fix(mobile): reflect asset deletions instantly (#26835)
Sometimes the current asset won't update when deleted, or it won't
refresh until an event (like showing details) happens.
2026-03-17 06:43:14 -05:00
Michel Heusschen
9b0b2bfcf2 fix(web): jump to primary stacked asset from memory (#26978) 2026-03-17 06:39:39 -05:00
Preslav Penchev
ac6938a629 fix(web): allow pasting PIN code from clipboard or password manager (#26944)
* fix(web): allow pasting PIN code from clipboard or password manager

The keydown handler was blocking Ctrl+V/Cmd+V since it called
preventDefault() on all non-numeric keys. Also adds an onpaste
handler to distribute pasted digits across the individual inputs.

* refactor: handle paste in handleInput, remove maxlength

* cleanup + fix digit focus

---------

Co-authored-by: Preslav Penchev <preslav.penchev@acronis.com>
Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
2026-03-17 06:38:06 -05:00
Thomas
16749ff8ba fix(server): sync files to disk (#26881)
Ensure that all files are flushed after they've been written.

At current, files are not explicitly flushed to disk, which can cause
data corruption. In extreme circumstances, it's possible that uploaded
files may not ever be persisted at all.
2026-03-17 06:33:43 -05:00
renovate[bot]
bba4a00eb1 chore(deps): update github-actions (#26967)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-17 10:40:22 +01:00
Mees Frensel
9dafc8e8e9 fix(web): make link fit album card (#26958) 2026-03-16 19:17:55 +01:00
Michel Heusschen
4e44fb9cf7 fix(web): prevent search page error on missing album filter (#26948) 2026-03-16 19:15:20 +01:00
Yaros
82db581cc5 feat(mobile): open in browser (#26369)
* feat(mobile): open in browser

* chore: open in browser instead of webview

* chore: allow archived asset

* fix: moved openinbrowser above unstack

* feat: deeplink into favorites, trash & archived

* fix: use remoteId (for tests to succeed)

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-16 18:06:51 +00:00
Mert
b66c97b785 fix(mobile): use shared auth for background_downloader (#26911)
shared client for background_downloader on ios
2026-03-13 22:23:07 -05:00
Mert
ff936f901d fix(mobile): duplicate server urls returned (#26864)
remove server url

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-13 22:09:42 -05:00
Min Idzelis
48fe111daa feat(web): improve OCR overlay text fitting, reactivity, and accessibility (#26678)
- Precise font sizing using canvas measureText instead of character-count heuristic
- Fix overlay repositioning on viewport resize by computing metrics from reactive state instead of DOM reads
- Fix animation delay on resize by using transition-colors instead of transition-all
- Add keyboard accessibility: OCR boxes are focusable via Tab with reading-order sort
- Show text on focus (same styling as hover) with proper ARIA attributes
2026-03-13 22:04:55 -05:00
bo0tzz
0581b49750 fix: ignore optional headers in pr template check (#26910) 2026-03-13 22:55:00 +00:00
rthrth-svg
2c6d4f3fe1 fix(web): copy yearMonth in MonthGroup to avoid shared object reference with asset (#26890)
Co-authored-by: Min Idzelis <min123@gmail.com>
2026-03-13 22:27:08 +01:00
Belnadifia
55513cd59f feat(server): support IDPs that only send the userinfo in the ID token (#26717)
Co-authored-by: irouply <irouply@secom.fr>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-03-13 22:14:45 +01:00
bo0tzz
10fa928abe feat: require pull requests to follow template (#26902)
* feat: require pull requests to follow template

* fix: persist-credentials: false
2026-03-13 09:43:00 -05:00
Nathaniel Hourt
e322d44f95 fix: SMTP over TLS (#26893)
Final step on #22833

PReq #22833 is about adding support for SMTP-over-TLS rather than just STARTTLS when sending emails. That PReq adds almost everything; it just forgot to actually pass the flag to Nodemailer at the end.

This adds that last line of code and makes it work correctly (for me, anyways!).

Co-authored-by: Nathaniel <I@nathaniel.land>
2026-03-13 09:41:50 -05:00
Michel Heusschen
c2a279e49e fix(web): keep header fixed on individual shared links (#26892) 2026-03-13 09:40:04 -05:00
Mert
226b9390db fix(mobile): video auth (#26887)
* fix video auth

* update commit
2026-03-13 09:38:21 -05:00
Michel Heusschen
754f072ef9 fix(web): disable drag and drop for internal items (#26897) 2026-03-13 09:37:51 -05:00
luis15pt
c91d8745b4 fix: use correct original URL for 360 video panorama playback (#26831)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:27:44 +01:00
Brandon Wees
f3b7cd6198 refactor: move encoded video to asset files table (#26863)
* refactor: move encoded video to asset files table

* chore: update
2026-03-12 16:15:21 -04:00
Jason Rasmussen
990aff441b fix: add to shared link (#26886) 2026-03-12 16:10:55 -04:00
Daniel Dietzler
001d7d083f refactor: small test factories (#26862) 2026-03-12 14:48:49 -04:00
Michel Heusschen
3fd24e2083 fix(server): restrict individual shared link asset removal to owners (#26868)
* fix(server): restrict individual shared link asset removal to owners

* make open-api
2026-03-12 14:48:00 -04:00
Jason Rasmussen
6bb8f4fcc4 refactor: clean class (#26885) 2026-03-12 14:47:35 -04:00
Jason Rasmussen
d4605b21d9 refactor: external links (#26880) 2026-03-12 14:55:33 +00:00
Jason Rasmussen
3bd37ebbfb refactor: clean class (#26879) 2026-03-12 09:53:46 -05:00
Min Idzelis
5c3777ab46 fix(web): fix zoom touch event handling (#26866)
fix(web): fix zoom touch event handling and add clarifying comments

- Suppress Safari's synthetic dblclick on double-tap which conflicts with zoom-image's touchstart-based zoom
- Add comment explaining pointer-events-none on zoom transform wrapper
- Add comments for touchAction and overflow style overrides
2026-03-12 09:37:29 -05:00
Alex
6c531e0a5a chore: add shadow to video play/pause icon shadow (#26836) 2026-03-11 14:15:31 -05:00
Thomas
471c27cd33 chore(mobile): remove background from asset viewer back button (#26851)
We recently changed the asset viewer to use a gradient. The circle
button looks out of place now.
2026-03-11 14:15:18 -05:00
bo0tzz
4773788a88 chore: more unused release workflow cleanup (#26817) 2026-03-11 20:04:26 +01:00
renovate[bot]
d49d995611 chore(deps): update dependency exiftool-vendored to v35.13.1 (#26813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-11 20:03:19 +01:00
Snowknight26
0ac3d6a83a fix(web): face selection box position resetting on browser resize (#26766) 2026-03-11 19:38:08 +01:00
Mees Frensel
9996ee12d0 refactor(web): crop area tool (#26843) 2026-03-11 18:58:26 +01:00
Brendan Ngo
0a79dd1228 fix(server): extract make/model from sony video files (#26833)
Co-authored-by: Your Name <brendan.ngo@okendo.io>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-03-11 17:29:37 +00:00
Mees Frensel
e45308b949 fix(web): exclude emoji from translation string (#26852) 2026-03-11 13:22:59 -04:00
Mert
c403e03a42 fix(mobile): logout on upgrade (#26827)
* use cookiejar

* cookie duping hook

* remove old pref

* handle network switching on logout

* remove bootstrapCookies

* dead code

* fix cast

* use constants

* use new event name

* update api
2026-03-11 12:07:27 -05:00
Luis Nachtigall
e7db3b220d feat(mobile): show animated images in asset viewer (#26614)
* Add support for showing animated images in AssetViewer with AnimatedImageStreamCompleter

* Add GIF overlay to thumbnail tile for animated assets

* formatting

* require isAnimated parameter in image providers for better asset handling

* feat: refactor AnimatedImageStreamCompleter to use streams for codec loading and initial image handling

* formatting

* add isAnimatedImage property to BaseAsset

* remove ApiService.getRequestHeaders() usage
2026-03-11 12:07:06 -05:00
bo0tzz
28d5c169c0 chore: use pokedex-large runner for rocm (#26823) 2026-03-11 11:15:48 -05:00
renovate[bot]
0f2fe656db fix(deps): update typescript-projects (#26812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-11 16:40:41 +01:00
Daniel Dietzler
34ce68095d chore: upgrade to kysely 0.28.11 (#26744) 2026-03-11 16:17:31 +01:00
Min Idzelis
8764a1894b feat: adaptive progressive image loading for photo viewer (#26636)
* feat(web): adaptive progressive image loading for photo viewer

Replace ImageManager with a new AdaptiveImageLoader that progressively
loads images through quality tiers (thumbnail → preview → original).

New components and utilities:
- AdaptiveImage: layered image renderer with thumbhash, thumbnail,
  preview, and original layers with visibility managed by load state
- AdaptiveImageLoader: state machine driving the quality progression
  with per-quality callbacks and error handling
- ImageLayer/Image: low-level image elements with load/error lifecycle
- PreloadManager: preloads adjacent assets for instant navigation
- AlphaBackground/DelayedLoadingSpinner: loading state UI

Zoom is handled via a derived CSS transform applied to the content
wrapper in AdaptiveImage, with the zoom library (zoomTarget: null)
only tracking state without manipulating the DOM directly.

Also adds scaleToCover to container-utils and getAssetUrls to utils.

* fix: don't partially render images in firefox

* add passive loading indicator to asset-viewer

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-11 09:48:46 -05:00
Michel Heusschen
27f69b39b2 fix(server): use correct day ordering in timeline buckets (#26821)
* fix(web): sort timeline day groups received from server

* fix(server): use correct day ordering in timeline buckets
2026-03-11 08:49:35 -04:00
Michel Heusschen
9fc6fbc373 fix(web): restore asset update events in asset viewer (#26845) 2026-03-11 08:46:29 -04:00
Thomas
9fc32b6f7a feat(mobile): use material design 3 slider (#26829)
* feat(mobile): use material design 3 slider

The new slider is easier to use, and looks more modern.

* chore: add shadow to button and text for better visibility

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-11 01:58:01 +00:00
Thomas
4571940a4e fix(mobile): wrap backup error message text (#26834)
Refs: #25022
2026-03-10 20:40:01 -05:00
Thomas
1ceb6d2e21 fix(mobile): use tabular figures in backup page (#26830)
The numbers in the backup page are not monospace, and so changes cause
the layout to shift. Using tabular figures (monospace) will prevent
that.

Refs: #25021
2026-03-10 20:12:33 -05:00
Andreas Heinz
1a4c5d73ac feat(web): add shortcut "p" to open/close the face tag box (#26826) 2026-03-10 23:53:38 +01:00
renovate[bot]
22b43bf4d9 chore(deps): update dependency @types/node to ^24.11.0 (#26808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-10 16:46:21 +00:00
Michel Heusschen
45eff1c663 fix(web): prevent unrelated assets from appearing in tag view (#26816) 2026-03-10 17:43:30 +01:00
renovate[bot]
56b8e1b8a9 chore(deps): update docker.io/valkey/valkey:9 docker digest to 3eeb097 (#26807)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-10 17:34:20 +01:00
Thomas
f79c8cf1c1 feat(mobile): consolidate video controls (#26673)
Videos have recently been changed to support zooming, but this can make
the controls in the centre of the screen unergonomic as they will either
stay in the centre when dismissing, or stick to the video when zooming.
Neither is great. We should align the behaviour with other apps which
has the play/pause toggle at the bottom of the screen with the seeker
bar instead.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-10 10:55:31 -05:00
Min Idzelis
8e50d25f45 feat(web): animate zoom toggle with cubicOut easing (#26731) 2026-03-10 10:42:02 -05:00
Michel Heusschen
8222781d1f fix(web): correct tag rounding in search options (#26814) 2026-03-10 15:25:13 +00:00
bo0tzz
08c4594cde chore: remove release-pr workflow (#26742)
It's not being used right now; can always add it back :P
2026-03-09 16:53:13 -04:00
Daniel Dietzler
d325231df2 chore: refactor test factories (#26804) 2026-03-09 20:47:03 +00:00
Daniil Suvorov
f2726606e0 fix(web): context menu overflow (#26760) 2026-03-09 19:47:54 +01:00
Andreas Heinz
0edbca24e4 fix(web): recalculate face bounding boxes (#26737)
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-03-09 18:44:47 +00:00
Snowknight26
4791d9c0c3 fix(web): show the correct cursor at crop bounds when editing an asset (#26748) 2026-03-09 19:39:01 +01:00
Michel Heusschen
a47b232235 fix(web): refresh recent albums sidebar after album changes (#26757) 2026-03-09 17:11:32 +01:00
Luis Nachtigall
df0c86920d fix(mobile): restrict trashed asset migration to Android platform (#26726)
* fix(migration): restrict trashed asset migration to Android platform

* playbackStyle migration add different log depending on platform
2026-03-09 09:56:27 -05:00
Min Idzelis
422111d26e test(e2e): fix flakiness: optimize resetDatabase with TRUNCATE and fix selectDay selector scoping (#26776)
fix(e2e): optimize resetDatabase with TRUNCATE and fix selectDay selector scoping
2026-03-08 01:38:15 -06:00
Min Idzelis
7a83baaf27 feat: responsive video duration in thumbnail (#26770) 2026-03-08 01:37:41 -06:00
Aleksander Pejcic
aaf34fa7d4 feat(ml): enable openvino for cpu (#22948)
* Enable OpenVINO CPU acceleration in Immich

* Remove unnecessary debug log

* Removing checking for device_ids for openvino since cpu will always be available

* Find OpenVINOExecutionProvider index instead of assuming index 0

* Fix openvino tests

* Fix failing test mock. OpenVINO expects provider options, but cuda provide doesn't so use that for mocked tests.

* Support empty provider options in OrtSessions in which case ONNXRuntime will use its own defaults

* Use OpenVINOExecutionProvider for test_sets_provider_options_kwarg

* fix mock

* simplify

* unused variable

---------

Co-authored-by: Aleksander <pejcic@adobe.com>
Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
2026-03-07 18:40:43 +00:00
Sergey Katsubo
4a384bca86 fix(server): opus handling as accepted audio codec in transcode policy (#26736)
* Fix opus handling as accepted audio codec in transcode policy

Fix the issue when opus is among accepted audio codecs in transcode policy
(which is default) but it still triggers transcoding because the codec name
from ffprobe (opus) does not match `libopus` literal in Immich.

Make a distinction between a codec name and encoder:
- codec name: switch to `opus` as the audio codec name. This matches what ffprobe
returns for a media file with opus audio.
- encoder: continue using the `libopus` encoder in ffmpeg.

* Add unit tests for accepted audio codecs and for libopus encoder

* Add db migration for ffmpeg.targetAudioCodec opus

* backward compatibility

* tweak

* noisy logs

* full mapping

* make check happy

* mark deprecated

* update api

* indexOf

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
2026-03-07 13:08:42 -05:00
Thomas
dd72ec2621 fix(mobile): correct local asset dimensions (#26677)
* fix(mobile): correct local asset dimensions

We are constraining the size of videos so that they play nicely with
hero animations, and don't stretch in weird ways. This however caused a
regression as we are not account for local assets on Android which have
un-oriented dimensions.

* post-orientation width and height in local sync

* migration

* no need to handle it in asset viewer

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-07 13:07:34 -05:00
Luis Nachtigall
e73686bd76 feat(android): enhance playback style detection using MIME type, reducing glide exposure (#26747)
* feat(android): enhance playback style detection using MIME type

* feat(android): improve playback style detection for GIF and WebP formats

* fix(android): make playback style detection faster

* refactor(android): simplify XMP reading logic for API 29 and below

* update playback style detection documentation

* use DefaultImageHeaderParser instead of all available ones for webp playbackStyle type detection
2026-03-07 10:41:26 -05:00
Snowknight26
6e9a425592 fix(web): asset viewer showing wrong viewer type when hovering on stack thumbnails (#26741) 2026-03-06 21:17:11 +01:00
Thomas
6012d22d98 fix(mobile): incorrect asset dimensions in search (#26725)
Search results use a different provider than the main timeline, and they
appear appear to have diverged a bit. This means that assets can
sometimes look wrong or different in search compared to the main
timeline or albums.
2026-03-05 21:58:58 -06:00
Min Idzelis
abfcffb423 feat(web): toggle zoom on double-click in photo viewer (#26732) 2026-03-05 21:58:09 -06:00
Min Idzelis
ec7246b86f refactor(web): add --font-sans CSS variable for primary font (#26730) 2026-03-06 03:00:37 +00:00
Brandon Wees
9597f8c37f feat(mobile): SyncAssetEditV1 (#26518)
* feat(mobile): SyncAssetEditV1

* fix: websocket handling

* fix: server version requirement

* fix: revert pubspec changes
2026-03-05 13:03:59 -06:00
Alex
7b0deb1fd3 fix: playback style migration (#26718) 2026-03-05 12:51:58 -06:00
Thomas
5ab05e57fa fix(mobile): inconsistent asset details background (#26634)
The background of the photo view does not extend below the height of the
viewport, and so the asset details fade in over black with the photo
view, and the standard surface colour scheme of the scaffold for the
rest. This leads to a janky animation. We can't change the background of
the scaffold to black, as it in turn makes the iOS bouncing scroll
physics cut off incorrectly. The best fix is to remove background
decoration from the photo view, and defer to the parent to colour the
background.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-05 17:45:21 +00:00
Marvin M
ba3f114625 chore: always use Package Imports (#26630)
* chore: always_use_package_imports

* fix: lint

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-05 11:18:01 -06:00
Mees Frensel
9b642633c1 fix(server): clarify transcoding bitrate policy (#26711) 2026-03-05 12:13:29 -05:00
Mert
a05c8c6087 feat(mobile): use shared native client (#25942)
* use shared client in dart

fix android

* websocket integration

platform-side headers

update comment

consistent platform check

tweak websocket handling

support streaming

* redundant logging

* fix proguard

* formatting

* handle onProgress

* support videos on ios

* inline return

* improved ios impl

* cleanup

* sync stopForegroundBackup

* voidify

* future already completed

* stream request on android

* outdated ios ws code

* use `choosePrivateKeyAlias`

* return result

* formatting

* update tests

* redundant check

* handle custom headers

* move completer outside of state

* persist auth

* dispose old socket

* use group id for cookies

* redundant headers

* cache global ref

* handle network switching

* handle basic auth

* apply custom headers immediately

* video player update

* fix

* persist url

* potential logout fix

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-05 11:04:45 -06:00
Mert
35a521c6ec fix(ml): batch size setting (#26524) 2026-03-05 12:01:47 -05:00
Snowknight26
09fabb36b6 fix(web): video stealing focus when it plays again when looping (#26704) 2026-03-05 15:41:27 +00:00
Daniel Dietzler
c259fee309 chore: cleanup vscode settings (#26709) 2026-03-05 08:12:59 -05:00
renovate[bot]
78ba9cbc63 chore(deps): update dependency multer to v2.1.1 [security] (#26705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-05 10:59:51 +01:00
Michel Heusschen
33d75462c9 fix(web): combobox dropdown positioning in modals (#26707) 2026-03-05 10:58:26 +01:00
Mees Frensel
e9451f10d6 chore(web): small cleanup of timeline month (#26708) 2026-03-05 09:53:20 +00:00
Min Idzelis
480b7e8d65 chore: configure ESLint flat config and auto-fix on save in VSCode settings (#26679) 2026-03-04 14:55:02 -05:00
Thomas
228ac63ab9 feat(mobile): keep search results visible (#26498)
Search results are replaced with a spinner when loading the next page,
which is quite jarring. Search results now remain visible when loading
the next page with a spinner at the bottom. The next page also loads
sooner, which makes it feel a lot smoother.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-04 11:27:11 -06:00
Thomas
7e9da945f6 chore(mobile): simplify asset page scroll (#26635)
In order to scroll smoothly without interfering with the gesture
detector on the photo view, we have an offstate scroll view which we
defer all drags to, and then forward scroll offsets to the real scroll
controller. This works well, but it can be simpler. Instead, we can
create a custom scroll controller on a scroll view with never scrollable
physics, and then forward drag events to that, bypassing the need for a
proxy scroll controller.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-04 10:28:55 -06:00
Thomas
dd03c9c0a9 fix(mobile): add safe area for asset details (#26675) 2026-03-04 09:47:51 -06:00
Mees Frensel
16e4a2b92a fix(docs): we usually don't assign issues (#26691)
Update CONTRIBUTING.md
2026-03-04 09:43:19 -06:00
Andreas Heinz
5caa7e1902 feat(web): bounding box for faces when hovering over the face in photo view (#26667)
* feat(web): when hovering over a face already deteced, display the bounding box also shown when hovering over the person in the details-pane.

* prevent lint error

* fix unused var
2026-03-04 15:27:26 +00:00
Snowknight26
8279e1078a fix(web): download toast showing wrong filename for motion assets (#26689) 2026-03-04 16:22:48 +01:00
Timon
011ecbb43d refactor(web): remove replaceAsset action (#26444) 2026-03-04 09:05:44 -06:00
Min Idzelis
2725c96cb1 chore: add recommended VSCode workspace extensions (#26682) 2026-03-04 09:29:15 -05:00
Daniel Dietzler
3c476b1987 chore: vitest 4 for web, cli, and e2e (#26668) 2026-03-04 14:19:13 +00:00
Snowknight26
5989c9b4aa fix(web): inconsistent asset nav bar state after visiting shared link (#26674) 2026-03-04 08:25:29 -05:00
Min Idzelis
13c4260a1f fix: resolve medium test asset paths relative to file location (#26683) 2026-03-04 08:23:58 -05:00
Min Idzelis
54bc9ddd69 chore: add vitest project names and fix server config root paths (#26684)
Add `name` to all vitest configs matching CI job buckets (server:unit,
server:medium, cli:unit, web:unit, e2e:server, e2e:maintenance) so they
appear as filterable @tags in the Vitest VSCode extension.

Fix `root` in server vitest configs to use an absolute path derived from
`import.meta.url` instead of `'./'`, which resolved relative to the config
file directory (`server/test/`) rather than `server/`, causing test
discovery to fail in the Vitest VSCode extension.
2026-03-04 08:20:43 -05:00
Paul Makles
f94e0fbc39 fix(maintenance mode): wait for valid server config on restart (#26456)
Signed-off-by: izzy <me@insrt.uk>
2026-03-04 11:16:21 +00:00
Nicolò Maria Semprini
5532f669eb feat: improve HEIC, HEIF and JPEG XL browser support detection (#26122)
feat: improve heic, heif and jxl browser support detection
2026-03-03 22:41:51 -05:00
Min Idzelis
e4c24bdec8 chore: enable prettier caching and quiet output (#26681) 2026-03-04 03:34:48 +00:00
Savely Krasovsky
56f14162f6 chore: bump base images manually (#26670) 2026-03-04 00:54:55 +00:00
renovate[bot]
8abbbc49cf chore(deps): update dependency opentofu to v1.11.5 (#26655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 16:53:01 +00:00
Thomas
4eb08eee18 fix(mobile): video state (#26574)
Consolidate video state into a single asset-scoped provider, and reduce
dependency on global state generally. Overall this should fix a few
timing issues and race conditions with videos specifically, and make
future changes in this area easier.
2026-03-03 10:28:07 -06:00
Mees Frensel
0560f98c2d chore(web): clarify locale settings description (#25562) 2026-03-03 12:52:17 +01:00
Brandon Annin
49ad411d50 fix(docs): add ocr to job flow diagram (#26505) 2026-03-03 12:43:59 +01:00
renovate[bot]
2478cc40f4 chore(deps): update dependency terragrunt to v0.99.4 (#26658)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 11:42:11 +00:00
Joe Babbitt
44eeb1e088 fix: implement existing withStacked on searchAssetBuilder (#26607)
Co-authored-by: Joe <code@joebabbitt.com>
2026-03-03 11:41:29 +00:00
Min Idzelis
a868ae3ad0 perf: move album fetching into detail panel (#26632) 2026-03-03 12:25:03 +01:00
renovate[bot]
acac0d4f37 chore(deps): update github-actions (#26656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: bo0tzz <git@bo0tzz.me>
2026-03-03 11:14:12 +00:00
Michel Heusschen
8c40a28fef fix(server): clean up edited thumbnail when deleting asset (#26664) 2026-03-03 12:08:07 +01:00
renovate[bot]
b2081eda1e fix(deps): update typescript-projects (#26657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-03-03 12:06:22 +01:00
renovate[bot]
9670c853c6 chore(deps): update docker.io/valkey/valkey:9 docker digest to 2bce660 (#26652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 12:02:04 +01:00
renovate[bot]
cc2dacb308 chore(deps): update prom/prometheus docker digest to 4a61322 (#26653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 11:23:13 +01:00
renovate[bot]
15fc6b18f3 chore(deps): update dependency @types/node to ^24.10.14 (#26654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 09:22:58 +00:00
Mees Frensel
a284e38890 fix(web): timeline and asset viewer RTL support (#26513) 2026-03-03 09:01:54 +01:00
Thomas
05010c3a84 fix(mobile): asset viewer hero animation (#26545)
The image in the photo view has no height, and is therefore entirely
unconstrained. This causes the image to take up the full height of the
viewport during the hero animation, which can make look out of sync. In
some other cases, it can stretch or resize the image to fill the entire
viewport.
2026-03-02 22:26:53 -06:00
Min Idzelis
4da3d68a67 refactor: use keyed each for face bounding boxes (#26648) 2026-03-02 22:16:13 -06:00
Min Idzelis
20c639e52a refactor: extract shared ContentMetrics for overlay position calculations (#26310) 2026-03-02 21:49:56 -06:00
Luis Nachtigall
6deb97d5bc fix(mobile): android detect supported version for special format column (#26633)
* fix(android): detect supported version for special format column

* fix(android): remove unnecessary suppression for new API in special format check

* fix(android): change visibility of hasSpecialFormatColumn method to private
2026-03-02 17:06:35 -05:00
Snowknight26
b282d83e95 fix(web): show shared link download button when logged in (#26629) 2026-03-02 22:00:23 +01:00
Jason Rasmussen
5bc08f8654 refactor: queue names (#26650) 2026-03-02 15:46:26 -05:00
shenlong
f54924d46a refactor: simplify video zooming (#26527)
fix: simplify video zooming

# Conflicts:
#	mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart
#	mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-03-02 17:53:49 +00:00
Michel Heusschen
dffe4d1d5c refactor(web): remove resize observer action (#26647) 2026-03-02 14:45:34 +00:00
Min Idzelis
7f47cdd645 feat: enhance face-editor positioning (#26303)
feat: enhance face-editor positioning - less overlap

test: timeline with actual video
2026-03-02 09:44:59 -05:00
Min Idzelis
625b30c50a test: stack editor e2e tests (#26526)
* feat: add responsive layout to broken asset

* test: stack editor e2e tests
2026-03-02 09:43:56 -05:00
Min Idzelis
8619d14eca feat: add responsive layout to broken asset (#26384) 2026-03-02 09:27:40 -05:00
Min Idzelis
062546c168 refactor: rename image cancel method (#26381) 2026-03-02 09:23:20 -05:00
Michel Heusschen
ea668d6b22 refactor(web): convert memory observer to an attachment (#26646) 2026-03-02 09:20:13 -05:00
Michel Heusschen
f06af2c600 refactor(web): dedupe isAllUserOwned logic (#26645) 2026-03-02 09:18:32 -05:00
Snowknight26
9dd2633e0c chore(web): deduplicate storage template examples (#26462) 2026-03-02 12:52:02 +01:00
Mees Frensel
13a514c189 fix(web): small thumbnail issues (#26643) 2026-03-02 12:50:33 +01:00
Mees Frensel
b0c9120bb6 chore: update PWA support (#26491) 2026-03-02 11:35:53 +00:00
Yaros
bc4265416d fix(web): top bar z index on search page (#26582) 2026-03-02 11:33:00 +01:00
shenlong
d4434f2276 fix: reset db from splash screen (#26617)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-03-01 15:23:26 -06:00
Luis Nachtigall
f4e156494f feat(mobile): add playbackStyle to local asset entity and related database schema (#26596)
* feat: add playbackStyle to local asset entity and related database schema

* implement conversion function for playbackStyle in local sync service

* implement conversion function for playbackStyle in local sync service

* refactor: remove deducedPlaybackStyle from TrashedLocalAssetEntityData

* add playbackStyle column to trashed local asset entity

* make playbackStyle non-nullable across the mobile codebase

* Streamline playbackStyle backfill:
- only backfill local assets playbackStyle in flutter/dart code
- only update trashed local assets in db migration

* bump target database version to 23 and update migration logic for playbackStyle

* set playback_style to 0 in merged_asset.drift as its a getter in base asset

* run make pigeon

* Populate playbackStyle for trashed assets during native migration
2026-03-01 14:50:21 -05:00
Min Idzelis
84abad564e fix(server): deduplicate shared links in getAll query (#26395) 2026-03-01 14:41:15 -05:00
renovate[bot]
02d356f5dd chore(deps): update dependency multer to v2.1.0 [security] (#26613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 11:34:11 +01:00
renovate[bot]
e963eedd26 chore(deps): update dependency @sveltejs/kit to v2.53.3 [security] (#26612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 11:33:51 +01:00
renovate[bot]
3da4acfe67 chore(deps): update dependency svelte to v5.53.5 [security] (#26611)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 11:22:54 +01:00
Yaros
e06cedb626 fix: hide download action for local/merged assets (#26461)
* fix: hide download action for local/merged assets

* chore: use onlyRemote

* chore: rename hasLocal to onlyLocal
2026-03-01 11:16:45 +05:30
Luis Nachtigall
ac5ef6a56d feat(mobile): add support for encoded image requests in local/remote image APIs (#26584)
* feat(mobile): add support for encoded image requests in local and remote image APIs

* fix(mobile): handle memory cleanup for cancelled image requests

* refactor(mobile): simplify memory management and response handling for encoded image requests

* fix(mobile): correct formatting in cancellation check for image requests

* Apply suggestion from @mertalev

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

* refactor(mobile): rename 'encoded' parameter to 'preferEncoded' for clarity in image request APIs

* fix(mobile): ensure proper resource cleanup for cancelled image requests

* refactor(mobile): streamline codec handling by removing unnecessary descriptor disposal in loadCodec request

---------

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
2026-02-28 11:43:58 -05:00
Luis Nachtigall
d6c724b13b feat(mobile): add playbackStyle to native sync API (#26541)
* feat(mobile): add playbackStyle to native sync API

Adds a `playbackStyle` field to `PlatformAsset` in the pigeon sync API so
native platforms can communicate the asset's playback style (image, video,
animated, livePhoto) to Flutter during sync.

- Add `playbackStyleValue` computed property to `PHAsset` extension (iOS)
- Populate `playbackStyle` in `toPlatformAsset()` and the full-sync path
- Update generated Dart/Kotlin/Swift files

* fix(tests): add playbackStyle to local asset test cases

* fix(tests): update playbackStyle to use integer values in local sync tests

* feat(mobile): extend playbackStyle enum to include videoLooping

* Update PHAssetExtensions.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix(playback): simplify playbackStyleValue implementation by removing iOS version check

* feat(android): implement proper playbackStyle detection

* add PlatformAssetPlaybackStyle enum

* linting

---------

Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-28 03:08:51 +00:00
Hao Xi
aa87d1b9a3 fix: tune up the performance of the getByDayOfYear query. (#26495) 2026-02-27 16:51:19 -05:00
Savely Krasovsky
dc4da4b3d6 feat: update onnxruntime-openvino to 1.24.1 and intel drivers (#26565)
feat: update onnxruntime-openvino to 1.24.1 and intel drivers to the latest version
2026-02-27 16:35:29 -05:00
Marius
7dbd08a747 feat(mobile): add confirmation dialog to permanent delete action (#26442) 2026-02-27 15:49:57 +00:00
Thomas
1d89190f96 fix(mobile): don't cut off top corners of app bar (#26550)
It's not visible normally, but in screenshots and when casting, the top
corners of the app bar are cut off. This should fix that.
2026-02-27 17:39:58 +05:30
Thomas
c2d8400899 fix(mobile): prevent video player from being recreated unnecessarily (#26553)
The changes in #25952 inadvertently removed an optimisation which
prevents the video player from being recreated when the tree changed.
This happens surprisingly often, namely when the hero animation
finishes. The widget is particularly expensive, so recreating it 2-3 in
a short period not only feels sluggish, but also causes the video to
hitch and restart.

The solution is to bring the global key back for the native video
player. Unlike before, we are using a custom global key which compares
the values of hero tags directly. This means we don't need to maintain a
map of hero tags to global keys in the state, and also means we don't
have to pass the global key down multiple layers.

This also fixes #25981.
2026-02-27 17:39:38 +05:30
Mees Frensel
a100a4025e fix(web): handle delete shortcut on shared link page as remove (#26552) 2026-02-27 12:50:06 +01:00
Nikhil Alapati
334fc250d3 fix(server): Live Photo migration bug when album is in template (#25329)
Co-authored-by: Nikhil Alapati <nikhilalapati@meta.com>
2026-02-27 12:46:55 +01:00
Michel Heusschen
28ca5f59fe fix(web): map timeline asset count (#26564) 2026-02-27 12:28:53 +01:00
Thomas
789d82632a fix(mobile): race condition showing details (#26559)
Asset details are prematurely hidden when a drag ends if the simulation
shows that it will close given its current velocity. It makes for a much
more responsible feeling UI. However, this behaviour conflicts with the
logic which determines whether details are showing based on the current
offset. The result is that the details are hidden, then immediately
shown again, and then hidden once it passes the min snap distance
threshold.

This can be fixed by only evaluating the position based logic when a
drag is active, and then inferring upcoming state with a simulation.
2026-02-27 12:12:24 +05:30
Daniel Dietzler
9f9569c152 fix: schema check (#26543) 2026-02-26 13:27:50 -05:00
Jason Rasmussen
fae05270a3 feat: doc links (#26519) 2026-02-26 12:14:17 -05:00
Michel Heusschen
771816f601 feat(web): map timeline sidepanel (#26532)
* feat(web): map timeline panel

* update openapi

* remove #key

* add index on lat/lng
2026-02-26 12:03:23 -05:00
Daniel Dietzler
e25ec4ec17 chore: migrate migration scripts to sql tools (#26537) 2026-02-26 17:59:52 +01:00
Kishor Prins
dd9046508d feat: ROCm 7.2 and MIGraphX support (#26178) 2026-02-26 16:52:26 +00:00
shenlong
177d1c9a30 feat: splash screen error page (#26460)
* feat: splash screen error page

* Update mobile/lib/pages/common/splash_screen.page.dart

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

* add clear data action

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-02-26 16:47:28 +00:00
Noel S
ded8d4e2b4 fix(mobile): set correct initial system-ui mode in asset viewer (#26500)
* fix: set correct initial system-ui mode on asset open

* move to function and add details visibility to initial state logic

* switch to ref.read
2026-02-26 10:10:46 -06:00
Mees Frensel
e454c3566b refactor: star rating (#26357)
* refactor: star rating

* transform rating 0 to null in controller dto

* migrate rating 0 to null

* deprecate rating -1

* rating type annotation

* update Rating type
2026-02-26 14:54:20 +01:00
Luis Nachtigall
4c79c3c902 feat(mobile): Prevent premature image cache eviction when higher image loading is enabled (#26208)
* feat(mobile): enhance image loading with error handling and eviction options

* pull request suggestions: remove log and remove useless local implementation
2026-02-25 17:31:37 -05:00
Alex
3bed1b6131 fix: consider DAR when extracting video dimension (#25293)
* feat: consider DAR when extracting video dimension

* move to probe

* comment

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
2026-02-25 21:58:53 +00:00
Brandon Wees
3c9fb651d0 feat(server): SyncAssetEditV1 (#26446)
* feat: SyncAssetEditV1

* fix: audit table import

* fix: sql tools table fetch

* fix: medium tests (wip)

* fix: circ dependency

* chore: finalize tests

* chore: codegen/lint

* fix: code review
2026-02-25 18:12:41 +00:00
Mees Frensel
55e625a2ac fix(web): error page i18n (#26517) 2026-02-25 18:35:25 +01:00
Daniel Dietzler
ca6c486a80 refactor: person stubs (#26512) 2026-02-25 08:56:00 -05:00
socksprox
d94d9600a7 fix(mobile): birthday picker shows limited months when no date exists (#26407)
* ScrollDatePicker defaults maximumDate to DateTime.now(). When no birthday exists, the picker starts at today (Feb 2026) with max also Feb 2026 — so only Jan–Feb are available for the current year.

Fix applied: Added maximumDate: DateTime(DateTime.now().year, 12, 31) at person_edit_birthday_modal.widget.dart:93, allowing all 12 months to be selected while still preventing future-year birthdays.

* fix(mobile): initialize birthday picker to past date to prevent future birthdays

When no birthday exists, initialize to 30 years ago instead of today.
This allows all 12 months to be selectable while keeping maximumDate
as DateTime.now() to prevent future birthday selection.

Fixes issue where only current months were available due to maxDate constraint.

---------

Co-authored-by: socksprox <info@shadowfly.net>
2026-02-25 07:58:02 +05:30
Mees Frensel
11e5c42bc9 fix(web): toast warning when trying to upload unsupported file type (#26492) 2026-02-24 15:58:40 -05:00
shenlong
33c6cf8325 test: backup repository (#26494)
test: backup repository tests

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-24 15:50:02 -05:00
renovate[bot]
dd97395f3a chore(deps): update dependency gunicorn to v25 (#26486)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 16:14:04 +00:00
renovate[bot]
7ae268e287 fix(deps): update dependency exiftool-vendored to v35 (#26488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-02-24 14:40:57 +01:00
Jason Rasmussen
f07e2b58f0 refactor: prefer buffer (#26469)
* refactor: prefer buffer

* Update server/src/schema/tables/session.table.ts

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>

---------

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2026-02-24 13:26:36 +00:00
shenlong
4b8f90aa55 refactor: remote album repository test to use context (#26481)
* refactor: remote album repository test to use context

* refactor: medium repo context (#26482)

* refactor: medium repo context

* store userId in closure

---------

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

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-24 13:25:07 +00:00
Daniel Dietzler
55ee9f76da chore: eslint 10 (#26490) 2026-02-24 08:24:18 -05:00
Michel Heusschen
30f6d4439e fix(web): prevent null folder tree on concurrent load (#26489) 2026-02-24 08:23:07 -05:00
renovate[bot]
f62d98a0d1 chore(deps): update dependency eslint-plugin-unicorn to v63 (#26484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 12:34:12 +01:00
renovate[bot]
db3d580761 chore(deps): update dependency globals to v17 (#26485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 12:18:01 +01:00
renovate[bot]
0bc38fefe6 fix(deps): update typescript-projects (#26483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-02-24 11:15:26 +00:00
renovate[bot]
acc4219849 chore(deps): update actions/checkout action to v6.0.2 (#26477)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 10:09:34 +01:00
shenlong
5234e21241 fix: retain asset when either asset is a favorite (#26473)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-23 16:52:34 -05:00
shenlong
17b327bfcd refactor: medium repository context (#26472)
refactor: repository test context

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-23 21:34:21 +00:00
Min Idzelis
d14d0a9b9b feat: add isTransparent to db (#26413) 2026-02-23 21:33:52 +00:00
Mees Frensel
bf47147fbb fix(server): accept showAt and hideAt for creating memories (#26429)
* fix(server): accept showAt and hideAt for creating memories

* fix history
2026-02-23 21:26:34 +00:00
aviv926
9ea0a69a72 feat(docs): Adding information about parameter c= (#26430)
* Adding information about parameter c=

* Apply suggestions from code review

Co-authored-by: bo0tzz <git@bo0tzz.me>

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
Co-authored-by: bo0tzz <git@bo0tzz.me>
2026-02-23 21:21:06 +00:00
shenlong
00f43ffc25 chore: add Option type (#26467)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-23 16:20:25 -05:00
Jonathan Jogenfors
96dc4a77a0 fix: always show library scan button (#26428)
* fix: always show library scan button

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-23 21:18:23 +00:00
shenlong
db7158b967 refactor: ImmichHtmlText to ImmichFormattedText (#26466)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-23 16:05:00 -05:00
Brandon Wees
e5722c525b feat: getAssetEdits respond with edit IDs (#26445)
* feat: getAssetEdits respond with edit IDs

* chore: cleanup typings for edit API

* chore: cleanup types with jason

* fix: openapi sync

* fix: factory
2026-02-23 20:57:57 +00:00
shenlong
f616de5af8 chore(mobile): nudge users to switch to the new timeline (#26458)
* nudge users to switch to the new timeline

* remove timeline switch setting from new timeline

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-23 13:42:32 -06:00
shenlong
4f39663d27 fix: simplify timeline rebuild on orientation (#26408)
* revert: current fix

# Conflicts:
#	mobile/lib/presentation/widgets/timeline/timeline.widget.dart

* fix: simpler fix

* rebase

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-23 13:30:09 -05:00
Thomas
367025a3a8 chore(mobile): simplify showing details toggle (#26403)
Keeping track of the last scroll offset and guarding on scroll direction
is not necessary. The dead zone with kTouchSlop is more than sufficient,
and much simpler.

Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
2026-02-23 23:49:35 +05:30
Min Idzelis
60dafecdc9 refactor: thumbnail components (#26379) 2026-02-23 11:56:20 -05:00
Yaros
16c1c3c780 fix(mobile): join local on archived timeline (#26387) 2026-02-23 20:21:32 +05:30
Brandon Wees
e633bc3f24 fix: missing deletedAt and isVisible columns on mobile (#26414)
* feat: SyncAssetV2

* feat: mobile sync handling

* feat: request correct sync object based on server version

* fix: mobile queries

* chore: sync sql

* fix: test

* chore: switch to mapper

* fix: sql sync
2026-02-23 09:50:54 -05:00
Daniel Dietzler
a07d7b0c82 chore: migrate to sql-tools library (#26400)
Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-23 09:50:16 -05:00
Yaros
a469d350be feat(mobile): prompt when deleting from trash (#26392)
* feat(mobile): prompt when deleting from trash

* refactor: use existing strings

* chore: use type-safe translations

* chore: remove old translation function
2026-02-23 14:45:05 +00:00
Yaros
ccab4c88bb perf(mobile): optimized album sorting (#25179)
* perf(mobile): optimized album sorting

* refactor: add index & sql query

* fix: migration

* refactor: enum, ordering & list

* test: update album service tests

* chore: fix enums

broken during merging main

* chore: remove unnecessary tests

* test: add tests for getSortedAlbumIds

* test: added back stubs in service test
2026-02-23 20:13:45 +05:30
Min Idzelis
430638e129 feat: warn when losing transparency during thumbnail generation (#26243)
* feat: preserve alpha

* refactor: use isTransparent naming and separate getImageMetadata

* warn instead of preserve
2026-02-23 08:16:28 -05:00
Thomas
caebe5166a chore(mobile): remove redundant assignment (#26404)
The view controller is already assigned during page build. Reassigning
it for every drag doesn't really make any sense.
2026-02-23 12:48:25 +00:00
Michel Heusschen
1bd28c3e78 fix(web): prevent state_unsafe_mutation error on people page (#26438) 2026-02-23 13:24:51 +01:00
Matthew Momjian
31a55aaa73 fix(web): storage template example (#26424) 2026-02-23 10:34:56 +00:00
Thomas
8b2e1509ff chore(mobile): simplify pop logic (#26410)
We have all the information we need to decide on whether we should pop
or not at the end of a drag. There's no need to track that separately,
and update the value constantly.
2026-02-23 14:49:15 +05:30
Lauritz Tieste
d0cb97f994 feat(mobile): Add slug support for shared links (#26441)
* feat(mobile): add slug support for shared links

* fix(mobile): ensure slug retains existing value when unchanged
2026-02-23 14:31:42 +05:30
Timon
f0cf3311d5 feat(mobile): Allow users to set profile picture from asset viewer (#25517)
* init

* fix

* styling

* temporary workaround for 500 error

**Root cause:**
The autogenerated Dart OpenAPI client (`UsersApi.createProfileImage()`) had two issues:
1. It set `Content-Type: multipart/form-data` without a boundary, which overrode the correct header that Dart's `MultipartRequest` would set (`multipart/form-data; boundary=...`).
2. It added the file to both `mp.fields` and `mp.files`, creating a duplicate text field.

**Result:**
Multer on the server failed to parse the multipart body, so `@UploadedFile()` was `undefined` → accessing `file.path` in `UserService.createProfileImage()` threw → **500 Internal Server Error**.

**Workaround:**
Bypass the autogenerated method in `UserApiRepository.createProfileImage()` and send the multipart request directly using the same `ApiClient` (basePath + auth), ensuring:
- No manual `Content-Type` header (let `MultipartRequest` set it with boundary)
- File only in `mp.files`, not `mp.fields`
- Proper filename fallback

* Revert "temporary workaround for 500 error"

This reverts commit 8436cd402632ca7be9272a1c72fdaf0763dcefb6.

* generate route for ProfilePictureCropPage

* add route import

* simplify

* try this

* Revert "try this"

This reverts commit fcf37d2801055c49010ddb4fd271feb900ee645a.

* try patching

* Reapply "temporary workaround for 500 error"

This reverts commit faeed810c21e4c9f0839dfff1f34aa6183469e56.

* Revert "Reapply "temporary workaround for 500 error""

This reverts commit a14a0b76d14975af98ef91748576a79cef959635.

* fix upload

* Refactor image conversion logic by introducing a new utility function. Replace inline image-to-Uint8List conversion with the new utility in EditImagePage, DriftEditImagePage, and ProfilePictureCropPage.

* use toast over snack

* format

* Revert "try patching"

This reverts commit 68a616522a1eee88c4a9755a314c0017e6450c0f.

* Enhance toast notification in ProfilePictureCropPage to include success type for better user feedback.

* Revert "simplify"

This reverts commit 8e85057a40.

* format

* add tests

* refactor to use statefulwidget

* format

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-02-22 06:02:33 +00:00
Timon
3ce0654cab feat(mobile): Allow users to set album cover from mobile app (#25515)
* set album cover from asset

* add to correct kebab group

* add to album selection

* add to legacy control bottom bar

* add tests

* format

* analyze

* Revert "add to legacy control bottom bar"

This reverts commit 9d68e12a08.

* remove unnecessary event emission

* lint

* fix tests

* fix: button order and remove unncessary check

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-02-22 05:53:39 +00:00
Noel S
f0e2fced57 feat(mobile): video zooming in asset viewer (#22036)
* wip

* Functional implementation, still need to bug test.

* Fixed flickering bugs

* Fixed bug with drag actions interfering with zoom panning. Fixed video being zoomable when bottom sheet is shown. Code cleanup.

* Add comments and simplify video controls

* Clearer variable name

* Fix bug where the redundant onTapDown would interfere with zooming gestures

* Fix zoom not working the second time when viewing a video.

* fix video of live photo retaining pan from photo portion

* code cleanup and simplified widget stack

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-02-21 23:37:36 -06:00
Alex Balgavy
8ba20cbd44 feat: tap to see next/previous image (#20286)
* feat(mobile): tap behavior for next/previous image

This change enables switching to the next/previous photo in the photo
viewer by tapping the left/right quarter of the screen.

* Avoid animation on first/last image

* Add changes to asset_viewer.page

* Add setting for tap navigation, disable by default

Not everyone wants to have tapping for next/previous image enabled, so
this commit adds a settings toggle. Since it might be confusing behavior
for new users, it is disabled by default.

* chore: refactor

* fix: lint

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2026-02-22 05:28:17 +00:00
Mert
1d25267f22 fix(mobile): buffer width/height referenced after recycling (#26415)
recycle after getters
2026-02-21 09:41:44 -06:00
Michel Heusschen
a4d95b7aba fix(web): prevent side panel overlap during transition (#26398) 2026-02-21 09:14:53 -06:00
Min Idzelis
25d0bdc9f5 chore: replace remaining usages of npm with pnpm (#26411) 2026-02-21 08:44:33 -05:00
Michel Heusschen
905b9bd560 fix(web): album description auto height (#26420) 2026-02-21 08:43:23 -05:00
Michel Heusschen
672743f543 fix(web): escape handling on album page (#26419) 2026-02-21 08:42:31 -05:00
Michel Heusschen
27c45b5ddb fix(web): restore close action for asset viewer (#26418) 2026-02-21 10:31:30 +00:00
Peter Ombodi
82c6302549 feat(mobile): timeline - add persistentBottomBar flag (#25634)
* feat(mobile): timeline - add selectable all-assets control

* feature(mobile): introduce bottomWidgetBuilder in Timeline
remove redundant code

* fix(mobile): remove redundant code

* refactor(mobile): refactor new code in Timeline

* fix(mobile): fix format

* refactor(mobile): replace unsupported Dart syntax for analyzer compatibility

* refactor(mobile): remove Timeline.bottomSheet and migrate to bottomWidgetBuilder

* refactor(mobile): restore Timeline.bottomSheet and remove bottomWidgetBuilder
add withPersistentBottomBar param to Timeline class

* refactor(mobile): refactor var name

---------

Co-authored-by: Peter Ombodi <peter.ombodi@gmail.com>
2026-02-20 23:51:26 +05:30
Min Idzelis
aae64b5e2f test: thumbnail selector (#26383)
* test: face ordering issue/flakiness

* test: thumbnail selector
2026-02-20 15:04:17 +00:00
Benjamin Nguyen
18bf96b4b2 fix(mobile): handle userPreferencesProvider error state during sync (#26332)
fix drift_search_page render bug
2026-02-20 08:57:28 -06:00
Timon
84f2956941 fix(cli): delete sidecar files after upload if requested (#26353)
* fix(cli): delete sidecar files after upload if requested

Introduced a new function, findSidecar, to locate XMP sidecar files based on specified naming conventions. Updated the deleteFiles function to delete associated sidecar files when the main asset file is deleted. Added unit tests for findSidecar to ensure correct functionality.

* lint and format

* fix test

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-20 14:54:08 +00:00
Min Idzelis
6044b41648 fix: align devcontainers with standard development containers (#26321) 2026-02-20 09:37:07 -05:00
Min Idzelis
b4e16efdf4 test: face ordering issue/flakiness (#26382) 2026-02-20 09:23:40 -05:00
Min Idzelis
19da655390 fix: exiftool-vendored.exe (#26393) 2026-02-20 09:16:42 -05:00
Benjamin Nguyen
a1839b3676 fix(mobile): Reset "People" search filter chip if no selections are made (#26267)
* filter by tags

* reset people search filter chip if no selections
2026-02-20 16:37:26 +05:30
dotlambda
7461479f60 chore(ml): remove unused dependency ftfy (#25529) 2026-02-19 22:58:25 +00:00
Jason Rasmussen
01050a3d54 fix: pin code reset modal (#26370) 2026-02-19 21:50:39 +00:00
renovate[bot]
e8bedfdb7a chore(deps): update dependency @sveltejs/kit to v2.52.2 [security] (#26371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 16:19:19 -05:00
Timon
7b4cabc2c6 chore: update task commands in web/mise.toml to use pnpm (#26345)
* chore: update task commands in mise.toml to use pnpm

* Replaced direct commands with pnpm run equivalents for consistency.
* Added new tasks for type checking and Svelte checks.
* Removed deprecated svelte-kit-sync task and adjusted dependencies accordingly.

* mroe

* chore: update mise.toml to add demo server task

* Removed the direct IMMICH_SERVER_URL setting from the environment section.
* Added a new task for starting the demo server with the IMMICH_SERVER_URL environment variable.
* Ensured consistency in task definitions.
2026-02-19 16:10:55 -05:00
David Baxter
5c7c07a09f perf: add indexes to improve People API response times (#26337)
Add SQL indexes for people search endpoints
2026-02-19 16:09:05 -05:00
Jason Rasmussen
e6ac48f4b5 refactor: app download modal (#26368) 2026-02-19 16:03:46 -05:00
Jason Rasmussen
3d4dec0cca refactor: asset actions (#26367) 2026-02-19 20:42:37 +00:00
Jason Rasmussen
1d11106dd0 refactor: add to album (#26366) 2026-02-19 15:27:30 -05:00
Timon
8eec3c810e fix(web): single select scroll behavior (#26358)
refactor(timeline): remove single select scroll behavior
2026-02-19 15:21:03 -05:00
Thomas
a43680c8b1 chore(mobile): simplify drag logic (#26291)
We were manually tracking whether gestures should be blocked, which was
a remnant of how the old code worked. This is no longer needed as we
have better heuristics for knowing whether we should skip drag updates
now.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-02-19 14:18:44 -06:00
Jason Rasmussen
b2a510efee refactor: remove unused actions (#26363) 2026-02-19 12:52:21 -06:00
shenlong
a0077a0f51 feat(mobile): html text (#25739)
* feat: html text

* feat: mobile ui showcase (#25827)

* feat: mobile ui showcase

* remove showcase from main app

* update fonts

* update code to be loaded from asset

* fix ci

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
# Conflicts:
#	mobile/lib/widgets/common/immich_sliver_app_bar.dart

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-19 12:27:16 -06:00
Thomas
aa02310d63 chore(mobile): cleanup asset viewer state (#26300)
initState was quite noisy, so I've moved some things around and made the
intention a bit clearer.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-02-19 12:26:21 -06:00
renovate[bot]
7394fa1491 chore(deps): update dependency svelte to v5.51.5 [security] (#26352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 17:11:56 +00:00
Mees Frensel
99f7eb4ce6 chore(server): remove redundant nullish checks (#26354) 2026-02-19 17:09:12 +00:00
Timon
ffd54d0431 fix(i18n): add translation key for partner's photos (#26348)
* fix(i18n): add translation key for partner's photos

* reuse existing key
2026-02-19 10:53:19 -06:00
Michel Heusschen
7005e9fc50 fix(web): update @immich/ui to v0.64.0 (#26351) 2026-02-19 16:33:06 +00:00
Michel Heusschen
4f2e6e3f15 fix(web): favoriting assets opened via GalleryViewer (#26350)
fix(web): favoriting assets through GalleryViewer
2026-02-19 10:32:25 -06:00
Michel Heusschen
8b5fc3d8bc fix(web): prevent panorama image reload during asset updates (#26349) 2026-02-19 10:31:30 -06:00
Thomas
0fa385c465 fix(mobile): infer drag intent early (#26344)
The drag intent was not set until it reached the kTouchSlop threshold.
This is not necessary as flutter already has its own heuristics for
preventing unintended drags.

The result of using kTouchSlop is that dismissing or scroll can feel a
little delayed, and will jump from 0 to kTouchSlop (18px) rather than
moving smoothly.
2026-02-19 09:56:51 -06:00
Daniel Dietzler
db4e7abf6d chore: refactor more queries (#25572)
* refactor: asset service queries

* chore: refactor more queries
2026-02-19 10:48:30 -05:00
Thomas
dadd20acfc chore(mobile): reduce the asset details snap target (#26343)
We were snapping to 75%, but 66.6% may be more natural.

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-02-19 15:48:21 +00:00
Jason Rasmussen
f04efbb714 fix: safari address bar color (#26346) 2026-02-19 09:40:13 -06:00
Timon
208c07af1f chore(web): merge "Add to album" and "Add to shared album" actions into a single action (#24669)
* refactor: simplify album selection actions by removing shared option

* Removed the shared option from AddToAlbumAction and related components.
* Updated AlbumPickerModal and other components to reflect this change.
* Cleaned up related tests and documentation for consistency.

* fix lint
2026-02-19 16:15:26 +01:00
Jason Rasmussen
72a5ccaa53 feat: editing descriminator (#26336) 2026-02-19 09:15:56 -05:00
Daniel Dietzler
fd0338f89c refactor: asset service queries (#25535) 2026-02-19 08:54:28 -05:00
Daniel Dietzler
d0ed76dc37 refactor: small face tests (#26340) 2026-02-19 08:51:18 -05:00
renovate[bot]
e0bb5f70ec fix(deps): update dependency fabric to v7 [security] (#26342)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 12:28:12 +00:00
Timon
f965daa8d2 chore: remove push trigger for check-openapi workflow (#26341) 2026-02-19 13:14:26 +01:00
Timon
316f86d25e feat: add .mxf file support (#24644)
* feat: add support for MXF format in media handling

* Updated supported formats documentation to include MXF.
* Added MXF to valid video extensions in tests.
* Registered MXF MIME type in mime-types utility.

* fix: enhance MXF handling in mime-types utility

* Updated video mime type validation to include 'application/mxf'.
* Adjusted asset type determination to recognize MXF as a video container.

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-19 06:39:41 +00:00
Hao Xi
e520fc3b63 fix: include DROP INDEX in transaction to prevent missing index on rollback (#25399)
* fix: ERR_PNPM_ENOENT error while `make dev` on macOS.

* fix: include `DROP INDEX` in transaction to prevent missing index on rollback.

* chore: clean up this PR.
2026-02-19 06:20:36 +00:00
Jonathan Jogenfors
b3b9834c00 feat(web): loop chromecast video (#24410) 2026-02-18 20:29:13 -05:00
Mees Frensel
84f7fb63ee feat(web): show ocr text boxes in panoramas (#25727) 2026-02-18 20:04:18 -05:00
Jorge Montejo
1f8359ead4 fix: Download the edited version when downloading multiple photos (#26259)
* fix: download the edited version when downloading multiple photos

* test: update tests

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-18 21:47:45 +00:00
Fabio Garavini
ea30c9d2ba fix(server): db restore failure when DB_URL is set to unix-domain socket connection (#26252)
* fix db restore fails to get postgres user

* Apply suggestion from @danieldietzler

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>

* fix fallback to reasonable default test

---------

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2026-02-18 16:37:40 -05:00
Dusan Hlavaty
d1abdea420 chore(docs): add quick-start guide for DevPod with docker (#26213) 2026-02-18 16:26:09 -05:00
Benjamin Nguyen
ae8dad68fc feat(mobile): filter by tags (#26196)
filter by tags
2026-02-18 21:16:26 +00:00
renovate[bot]
227ff70b6e chore(deps): update dependency ajv to v8.18.0 [security] (#26297)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 16:06:43 -05:00
bo0tzz
ee7ac09450 fix: bad field paste in metadata.service.spec (#26329)
Caused https://github.com/immich-app/immich/actions/runs/22153269773/job/64050176244 to flake
2026-02-18 16:04:32 -05:00
Devansh H Jani
2e59dbdc12 fix: prevent server crash when extraction of metadata fails if the assets are corrupted (#26042)
* Fix-25968 Extraction of metadata fails if the assets are corrupted

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-18 15:53:23 -05:00
Timon
c4c7f94317 chore: add OpenAPI check workflow (#26223) 2026-02-18 15:16:01 -05:00
Jason Rasmussen
d004d7e21b fix: metadata crash (#26327) 2026-02-18 15:09:35 -05:00
Timon
5f95aab437 chore: align mobile mise tasks (#26237)
* chore: align mobile mise tasks

* fix
2026-02-18 14:11:01 -05:00
Michel Heusschen
dd632f38de fix(web): unblock escape after opening context menu (#26325) 2026-02-18 14:10:15 -05:00
renovate[bot]
6f7fc94710 chore(deps): update github-actions (#25388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 19:53:41 +01:00
renovate[bot]
85cb515cae chore(deps): update dependency github:cqlabs/homebrew-dcm to v1.35.1 (#26278)
* chore(deps): update dependency github:cqlabs/homebrew-dcm to v1.35.1

* fix static analysis

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-18 23:47:54 +05:30
bo0tzz
65e1bb83b7 fix: use wrangler cli directly (#26326) 2026-02-18 18:06:41 +00:00
bo0tzz
d9b1b69827 fix: switch docs-deploy to use wrangler-action (#26323) 2026-02-18 18:45:43 +01:00
Min Idzelis
b2050583f5 chore: run maintenance test (e2e) in isolation too, share containers (#26246) 2026-02-18 09:39:13 -05:00
Keunes
1bdc24c730 feat(docs): Explain configuration file location for Docker Compose (#24989)
* Explain configuration file location for Docker Compose

* Update config-file.md

* Update config-file.md

* Update config-file.md

---------

Co-authored-by: Mees Frensel <33722705+meesfrensel@users.noreply.github.com>
2026-02-18 14:52:28 +01:00
renovate[bot]
5adb75c272 fix(deps): update dependency @mapbox/mapbox-gl-rtl-text to v0.3.0 (#23353)
* fix(deps): update dependency @mapbox/mapbox-gl-rtl-text to v0.3.0

* fix: maplibre rtl import

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-02-18 12:05:41 +01:00
Daniel Dietzler
8f9ea6a171 fix: utc time zone upserts (#26258)
fix: utc timezone upserts
2026-02-17 18:59:52 +01:00
Thomas
3f41916ad7 chore(mobile): fix asset marker icon file name (#26290) 2026-02-17 11:53:44 -05:00
Thomas
5c6433b4ca feat(mobile): inline asset details (#25952)
The existing implementation for showing asset details uses a bottom
sheet, and is not in sync with the preview or scroll intent. Other apps
use inline details, which is much cleaner and feels better to use.
2026-02-17 09:24:34 -06:00
Damien Nozay
06d487782e fix(release): add docker-compose.rootless.yml to released assets (#26261)
* fix(release): add docker-compose files to released assets

Since there is a warning:
"Make sure to use the docker-compose.yml of the current release"

This should apply to other docker-compose files, so it would make sense to release them.

It also makes it slightly easier to get the asset for rootless (e.g., PR 2750).

* release docker-compose.rootless.yml
2026-02-17 12:55:34 +01:00
Min Idzelis
455afbb119 ci: fix formatting task (#26274) 2026-02-17 12:51:15 +01:00
ewinnd
0767ae0c8a fix(docs): remove truenas link from synology community guide (#26277)
* Update synology.md to remove Truenas link

Removed link to Truenas github community repo.

* remove blank line

---------

Co-authored-by: Mees Frensel <33722705+meesfrensel@users.noreply.github.com>
2026-02-17 12:50:11 +01:00
renovate[bot]
a16a00ebd4 fix(deps): update typescript-projects (#26276)
* fix(deps): update typescript-projects

* chore: downgrade kysely

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-02-17 11:50:02 +00:00
renovate[bot]
398b750ef7 chore(deps): update dependency github:extism/js-pdk to v1.6.0 (#26279)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-17 12:49:14 +01:00
renovate[bot]
18bbb5b4db chore(deps): update node.js to v24.13.1 (#26275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-17 11:45:57 +01:00
renovate[bot]
b3c37905f7 chore(deps): update dependency @types/node to ^24.10.13 (#26273)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-17 11:44:38 +01:00
renovate[bot]
90ef6c4e28 chore(deps): update docker.io/valkey/valkey:9 docker digest to 930b414 (#26272)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-17 11:44:21 +01:00
Michel Heusschen
ceef65154d fix(web): clear cache when asset changes (#26257)
* fix(web): clear cache when asset changes

* formatting
2026-02-17 11:43:08 +01:00
Joren Guillaume
de7b42eb23 chore(docs): Update help channel for developers (#26284)
Update help channel for developers
2026-02-17 11:39:43 +01:00
Min Idzelis
75bdd6a644 fix: development containers init race conditions (#25876) 2026-02-16 18:34:42 -05:00
Michel Heusschen
0da74569f2 fix(web): clear unsaved asset description when changing asset (#26255)
* fix(web): clear unsaved asset description when changing asset

* remove unneeded $derived
2026-02-16 18:25:13 +01:00
Michel Heusschen
cc9c261fd0 fix(web): clear face boxes when switching assets (#26249) 2026-02-16 15:52:34 +01:00
Michel Heusschen
4dccc2082b fix(web): focus tag input when modal opens (#26256) 2026-02-16 14:30:41 +00:00
shenlong
9211013996 fix: bring back timeline args auto-scoping (#26219)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-16 08:20:28 -06:00
Alex
156e3479fa chore: styling tweak profile panel (#26248) 2026-02-16 08:20:01 -06:00
Min Idzelis
19ef196150 chore: quiet down dotenv (#26245) 2026-02-15 22:25:18 -06:00
Thomas
d2682f160e fix(mobile): inherit toolbar opacity (#25694)
Some widgets, like Icon widgets, automatically inherit opacity from the
icon theme in the context. Many other widgets however, do not. The
Immich logo, profile picture, and backup badge are examples of widgets
of this.

All unsupported toolbar widgets have been updated to support inheriting
the opacity from the icon theme.

IconButtons internally animate properties like opacity, which is kind of
nice, but means we have to do more work to replicate that behaviour for
other widgets. In most cases, we can simply use an IconButton widget and
forward the correct opacity. The Immich logo however is not a button,
and therefore we need to use a custom TweenAnimationBuilder.

All widgets are using efficient, native opacity rather than the heavy
Opacity widget.
2026-02-16 09:54:57 +05:30
Nicolas
c9dd8e0a79 feat(mobile): hide search by context/OCR if disabled on server (#25472) (#26063)
* feat(mobile): hide search by context/OCR if disabled on server (#25472)

* revert(mobile): remove changes to old search page

---------

Co-authored-by: Nicolas <nicolasroy@MacBookPro>
2026-02-16 08:11:56 +05:30
Dusan Hlavaty
f6e10afe2b chore(docs): fix discord channel in docs (#26238) 2026-02-15 21:34:02 +01:00
Thomas
5f87047490 feat(mobile): dynamic multi-line album name (#26040)
* feat(mobile): dynamic multi-line album name

Album names are currently limited to a single line, and scroll on
overflow. It would be better if album names were multi-line, and even
better if the font size was dynamic depending on how many lines there
are. The album name should then overflow with an ellipsis.

This is actually quite similar to how Google Photos handles album names.

* lint

---------

Co-authored-by: timonrieger <mail@timonrieger.de>
2026-02-15 22:23:45 +05:30
Daniel Dietzler
75e3b0467a chore: hyperlink contributing file in llm message (#26234) 2026-02-15 10:51:47 +00:00
bo0tzz
df4c25e567 fix: use pull_request_target in close-llm-pr.yml (#26232)
So that it actually has write permissions; this should be safe as it doesn't use any external input.
2026-02-15 11:47:01 +01:00
Michel Heusschen
ff7dca35f5 perf(web): speed up asset selection (#26216) 2026-02-14 15:31:04 -05:00
Alex
49ba833e4c fix(web): Revert "add checkerboard background for transparent images (#26091)" (#26220)
Revert "fix(web): add checkerboard background for transparent images (#26091)"

This reverts commit bc7a1c838c.
2026-02-14 20:25:14 +00:00
Michel Heusschen
9ab887d5d2 perf(web): speed up multi asset operations (#26217) 2026-02-14 15:24:47 -05:00
Min Idzelis
d264e78d3f chore: pnpm workspace protocol for sibling packagages (#26218) 2026-02-14 15:03:08 -05:00
Min Idzelis
2c9d69865c fix: e2e exit 135 (#26214) 2026-02-14 12:51:54 -05:00
bo0tzz
72cefcabaf chore: discourage LLM-generated PRs (#26211)
* chore: discourage LLM-generated PRs

* chore: add reading CONTRIBUTING.md to PR checklist

* chore: workflow to close LLM-generated PRs
2026-02-14 10:40:27 -06:00
Xantin
2fb9f84b56 refactor(i18n): Follow IETF standard (#26171)
* refactor(18n):  Follow IETF standard

Rename zh_SIMPLIFIED to zh_Hans

Makes it easier to merge #21337

* fix(web): zh_SIMPLIFIED -> zh_Hans
2026-02-13 18:47:41 +01:00
Weblate (bot)
434ded92f5 chore(web): update translations (#26167)
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/es/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/et/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/fr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ga/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/it/
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/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/th/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/yue_Hant/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hant/
Translation: Immich/immich

Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Happy <59247878+happy2452354@users.noreply.github.com>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Kuba <kubaant@gmail.com>
Co-authored-by: Matjaž T. <matjaz@moj-svet.si>
Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
Co-authored-by: PPNplus <ppnplus@protonmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: Ulices <hasecilu@tuta.io>
Co-authored-by: Yu-Hsuan Liao <EMC521@outlook.com>
Co-authored-by: albanobattistella <albano_battistella@hotmail.com>
Co-authored-by: jcreusand <joan.creusandreu@gmail.com>
Co-authored-by: waclaw66 <waclaw66@seznam.cz>
Co-authored-by: Óscar Fernández Díaz <42654671+oscfdezdz@users.noreply.github.com>
2026-02-13 17:45:35 +00:00
agent-steven
bc7a1c838c fix(web): add checkerboard background for transparent images (#26091)
Co-authored-by: steven94kr <rlgns98kr@gmail.com>
Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2026-02-13 17:18:44 +00:00
Daniel Dietzler
7cb355279e chore: remove asset stubs (#26187) 2026-02-13 11:00:31 -05:00
Nykri
ecb09501a5 feat(cli): change progress bar to display file size (#23328)
* Change progress bar to display file size

* Solved lint errors
2026-02-13 10:22:00 -05:00
Michel Heusschen
34eb2e1410 fix(web): timeline multi select group state (#26180) 2026-02-13 08:34:15 -05:00
shenlong
2d6580acd8 feat(mobile): dynamic layout in new timeline (#23837)
* feat(mobile): dynamic layout in new timeline

* simplify _buildAssetRow

* auto dynamic mode on smaller column count

* auto layout on smaller tiles

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-13 09:15:42 +05:30
Yaros
9aa3fe82c1 fix(mobile): inconsistent query for people (#24437)
* fix(mobile): inconsistent query for people

* refactor: implement suggestions

* refactor: refactored query impl suggestions
2026-02-13 09:13:21 +05:30
Arne Schwarck
66733eb4c0 feat: add people deeplink (#25686)
* Change path prefix from '/memories/' to '/people/'

Updated the AndroidManifest.xml to change the path prefix from '/memories/' to '/people/'.
Memories is anyway wrong and was replaced by /memory
and now the people path completes the known deeplinks.

* Add regex for people deep link handling

Add regex for people deep link handling

* Add deep link handling for 'people' route

* fix: missing person route builder method

---------

Co-authored-by: bwees <brandonwees@gmail.com>
2026-02-13 09:13:04 +05:30
Thomas
e5156df4f1 fix(mobile): hide latest version warnings (#26036)
The latest version is already hidden in the server info widget if
disabled (https://github.com/immich-app/immich/pull/25691), however I
did not realise there are more places where this warning is shown. This
hides the warning everywhere, and cleans up the code a bit.
2026-02-13 08:15:25 +05:30
Jason Rasmussen
8ef4e4d452 feat: schema-check (#25904) 2026-02-12 17:59:00 -05:00
Christos Longros
7413356a2f fix: clarify external domain translation (#26009)
* fix: clarify external domain setting is used for emails too (#24950)

* Update i18n/en.json

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

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-12 14:01:51 -05:00
Jason Rasmussen
5bf4e9595c refactor: purchase store (#25734) 2026-02-12 13:32:17 -05:00
renovate[bot]
6c0c4b3dda chore(deps): update docker.io/valkey/valkey:9 docker digest to 930b414 (#26087)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-12 12:36:46 -05:00
Yaros
206a208410 feat(web): change link expiration logic & presets (#26064)
* feat(web): link expiration presets

* refactor: implement suggestions

* chore: remove createdAt prop

* fix: tests

* fix: button keys
2026-02-12 18:27:49 +01:00
Jason Rasmussen
72cef8b94b feat: shared link login (#25678) 2026-02-12 12:08:38 -05:00
Jason Rasmussen
81c93101a0 feat: verify permissions (#25647) 2026-02-12 12:08:20 -05:00
Christos Longros
b06c21325e fix(mobile): prevent nav bar label text wrapping (#26011)
* fix(mobile): prevent nav bar label text wrapping (#25921)

* chore: fix format

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-12 16:57:03 +00:00
Bastian Köcher
730b770e67 fix(mobile): timeline orientation & foldable phones handling (#25088)
* [mobile]: Fix timeline handling on foldable phones + ensuring that images are not cut off

This fixes the handling of unfolding the phone while having the application opened. So,
the timeline is correctly rescaled and the current position is kept.
Besides that it fixes a bug with the ordering which lead to images being "cut off" at the right side
of the screen.

* refactor + cleanup

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-12 22:20:00 +05:30
Michel Heusschen
b85f6f3fce fix(web): add missing @immich/ui translations (#26143) 2026-02-12 16:42:35 +00:00
Min Idzelis
81f592ca52 feat: remove Cache API, rework preload(), cancel() and fetch() (#25289)
* feat: remove Cache API, rework preload(), cancel() and fetch()

perf - replace broadcast channel with direct postMessage

* remove sw response handling

* review comments
2026-02-12 11:25:20 -05:00
renovate[bot]
a62e8ed179 fix(deps): update typescript-projects (#25549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-02-12 16:24:04 +00:00
Michel Heusschen
1cf3a80840 fix(web): show correct assets in memory gallery (#26157) 2026-02-12 11:17:09 -05:00
Klenner Martins Barros
9f6dbf710c fix(web): improve api key modal responsiveness (#26151) 2026-02-12 17:13:09 +01:00
Michel Heusschen
f207f99e86 fix(web): prevent event manager from throwing error (#26156) 2026-02-12 11:09:15 -05:00
Thomas
0d35231dfd chore(mobile): cleanup server storage info (#26038)
The server storage info has a lot of whitespace due to the ListTile.
Converting it to be inline makes the styling appear more intentional.

There are also a few semantically relevant list items in the app bar
dialog which have been grouped together.
2026-02-12 21:09:55 +05:30
shenlong
675bbf3ac3 chore: remove unused key and fix casing for recent_albums (#24691)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-12 16:06:22 +01:00
Weblate (bot)
c45450b6ac chore(web): update translations (#26118)
Translate-URL: https://hosted.weblate.org/projects/immich/immich/de/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/hi/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lt/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ro/
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/th/
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: Anton Palmqvist <apq@users.noreply.hosted.weblate.org>
Co-authored-by: PPNplus <ppnplus@protonmail.com>
Co-authored-by: Rishi <rishikesh.200@gmail.com>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Vivek M <vivekmalhotra004@gmail.com>
Co-authored-by: WellsTsai <dan50907@gmail.com>
Co-authored-by: czlevi7 <czlevi7@gmail.com>
Co-authored-by: jässin <aouanijassin@gmail.com>
Co-authored-by: muziqaz <muziqaz@users.noreply.hosted.weblate.org>
Co-authored-by: pyccl <changcongliang@163.com>
2026-02-12 15:02:07 +00:00
renovate[bot]
fea6e8d9f3 chore(deps): update dependency python-multipart to v0.0.22 [security] (#25559) 2026-02-12 09:32:59 -05:00
renovate[bot]
27ebbab1d9 fix(deps): update dependency pillow to v12 [security] (#26142) 2026-02-12 09:32:17 -05:00
renovate[bot]
4647ecf2ea chore(deps): update machine-learning (#25067) 2026-02-12 09:31:13 -05:00
shenlong
78c8f1d5a9 chore: add indexes for foreign keys (#25925)
* chore: add indexes for foreign keys

* update idx_asset_face_person_id index

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-12 19:58:31 +05:30
shenlong
ec4de54ea2 fix: null local date time in timeline queries (#26133)
* fix: null local date time in timeline queries

* refactor effectiveCreatedAt

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-12 19:58:00 +05:30
Peter Ombodi
420cd5193b fix(mobile): Login routing on Splash screen (#26128)
* fix(mobile): fix Login routing on Splash screen

* fix(mobile): remove _duplicateGuard from the LoginRoute
revert changes in splash_screen

---------

Co-authored-by: Peter Ombodi <peter.ombodi@gmail.com>
2026-02-12 19:57:43 +05:30
Daniel Dietzler
7e0356e227 refactor: more small tests (#26159) 2026-02-12 08:34:32 -05:00
Michel Heusschen
913904f418 fix(web): escape shortcut handling (#26096) 2026-02-11 18:55:28 +01:00
Daniel Dietzler
e54678e0d6 refactor: small tests (#26141) 2026-02-11 11:49:00 -05:00
Daniel Dietzler
222c90b7b7 chore: remove album stubs (#26130) 2026-02-11 09:12:46 -05:00
Michel Heusschen
1c1a000c78 fix(web): use locale for date picker (#26125) 2026-02-11 11:39:30 +01:00
Jason Rasmussen
458d5f0f8f fix: ignore checksum constraint error when logging (#26113) 2026-02-10 20:43:55 +00:00
Jason Rasmussen
4c948647fc chore: asset-job medium tests (#26111) 2026-02-10 13:41:40 -05:00
github-actions
3be8e265cd chore: version v2.5.6 2026-02-10 18:24:40 +00:00
Weblate (bot)
e3c4e0197a chore(web): update translations (#25947)
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ar/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/bg/
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/de_CH/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/el/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/eo/
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/fr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ga/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/gl/
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/kn/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lt/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lv/
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/nn/
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/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/sq/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sv/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/th/
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/vi/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/yue_Hant/
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: Adam Havránek <adamhavra@seznam.cz>
Co-authored-by: Ahmed Khaleel Shihab <ahmed91shihab@gmail.com>
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Arif Budiman <arifpedia@gmail.com>
Co-authored-by: Arnie97 <arnie97@gmail.com>
Co-authored-by: David Krizak <lavidk@icloud.com>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Gautam Pai <gautam.gp4207@gmail.com>
Co-authored-by: Gilgalard Elendil <Gilgalard11@gmail.com>
Co-authored-by: Gisle-Andre Larsen <gisleandre@hotmail.com>
Co-authored-by: Guillaume Petit <gpetit@edelzone.fr>
Co-authored-by: Guillermo Ramos Santos <guillermo.ramosantos@gmail.com>
Co-authored-by: HackingAll <hacking.all.YT@gmail.com>
Co-authored-by: Hans Cats <hanscats@gmail.com>
Co-authored-by: Happy <59247878+happy2452354@users.noreply.github.com>
Co-authored-by: Haru Ijima <haruijimakun@gmail.com>
Co-authored-by: Hurricane_32 <rodrigorimo@hotmail.com>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: Ivan Dimitrov <idimitrov08@gmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Julius Lehmann <julius.lehmann.privat@gmail.com>
Co-authored-by: Luuk Heijnen <luukheijnen1@gmail.com>
Co-authored-by: MarcSerraPeralta <marcserraperalta@gmail.com>
Co-authored-by: Masani Amin Yasin <langugetranslategay.taps663@silomails.com>
Co-authored-by: Matjaž T. <matjaz@moj-svet.si>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Olaf Nielsen <solluh@mail.de>
Co-authored-by: Oleksandr Yurov <oyurov@icloud.com>
Co-authored-by: Philipp Frauenfelder <philipp.frauenfelder@gmail.com>
Co-authored-by: Reetryyy <kaxa.kikalishvili.kk@gmail.com>
Co-authored-by: Romo <romo@romo.al>
Co-authored-by: Shawn <xiaxinx@gmail.com>
Co-authored-by: Skanda <skillwiz94@gmail.com>
Co-authored-by: Sylvain Pichon <service@spichon.fr>
Co-authored-by: TV Box <realceday.tvbox@gmail.com>
Co-authored-by: Tage Lauritsen <tage@tunenet.dk>
Co-authored-by: Tim Morley <weblate.3919org@timsk.org>
Co-authored-by: Ulices <hasecilu@tuta.io>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Veerasak Kritsanapraphan <veerasak.kritsanapraphan@gmail.com>
Co-authored-by: albanobattistella <albano_battistella@hotmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: chamdim <chamdim@protonmail.com>
Co-authored-by: dvbthien <dvbthien@users.noreply.hosted.weblate.org>
Co-authored-by: ea stanley <hang4ea@gmail.com>
Co-authored-by: muziqaz <muziqaz@users.noreply.hosted.weblate.org>
Co-authored-by: nobo73 <noboru.tanaka@gmail.com>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: waclaw66 <waclaw66@seznam.cz>
Co-authored-by: Óscar Fernández Díaz <42654671+oscfdezdz@users.noreply.github.com>
2026-02-10 18:22:41 +00:00
Nicolò Maria Semprini
1ddb8f0667 feat: jxl browser support detection (#25599) 2026-02-10 12:49:09 -05:00
Alex
adfb003d03 fix: local date time group fall back (#26110)
* fix: local date time group fall back

* remove else clause
2026-02-10 11:47:13 -06:00
Brandon Wees
8c8b11f80c fix: person thumbnail generation on edited assets (#26112)
* fix: person thumbnail generation on edited assets

* chore: sql sync
2026-02-10 11:38:43 -06:00
Claudio
90d554947f docs: update ml-hardware-acceleration.md (#25755)
Update ml-hardware-acceleration.md

Invert the lines about editing the docker-compose.yml file to have users add the tag to the image first, then uncomment the extends section. This should help users follow the instructions as they flow through the YAML file.
2026-02-10 12:28:21 -05:00
Paul Makles
caeba5063b refactor(server): move database restores code into a service (#25918)
* fix(server): use provided database name/username for restore & ensure name is not mangled

fixes #25633

Signed-off-by: izzy <me@insrt.uk>

* chore: add db switch back but with comments

Signed-off-by: izzy <me@insrt.uk>

* refactor: no need to restore database since it's not technically possible
chore: late fallback for username in parameter builder

Signed-off-by: izzy <me@insrt.uk>

* chore: type fix

Signed-off-by: izzy <me@insrt.uk>

* refactor: move db backup code into service

* test: check SQL sent to psql

* chore: remove todo

Signed-off-by: izzy <me@insrt.uk>

---------

Signed-off-by: izzy <me@insrt.uk>
2026-02-10 12:12:27 -05:00
Matthew Momjian
280174026f feat(deployment): rootless compose file (#25931)
rootless deployment
2026-02-10 12:11:44 -05:00
Michel Heusschen
a9e0fa43fa fix: correctly cancel select all assets (#26067) 2026-02-10 11:47:23 -05:00
Jason Rasmussen
e6e56d75e2 fix(web): refresh text (#26071) 2026-02-10 16:42:03 +00:00
Brandon Wees
0886281dd8 fix: create face exif orientation handling (#26108)
* fix: handle exif orientation when creating face

* chore: tests
2026-02-10 11:30:34 -05:00
renovate[bot]
32dea76a92 chore(deps): update dependency @types/node to ^24.10.11 (#26088)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-10 11:24:33 -05:00
Min Idzelis
6af534fe4c feat: run maintenance tests in isolation, share containers between all … (#25856)
* feat: run maintance tests in isolation, share containers between all serial test suites

* refactor: organize files

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-10 11:05:06 -05:00
Daniel Dietzler
71fe9192fd chore: metadata extraction date test (#26102) 2026-02-10 10:05:05 -05:00
Mert
7fa6f617f5 fix(server): thumbnail queueing (#26077)
* fix thumbnail queueing

* add bmp

* other isEdited column
2026-02-10 09:04:03 -06:00
Thomas
c3730c8eab chore(mobile): enable high refresh rate in debug builds (#26085)
I'm testing changes to animations and app performance, and noticed it
felt quite sluggish on a 120hz display. It turns out that high refresh
is disabled in debug builds. It's probably a good idea to enable it so
that it more closely mirrors the production build.
2026-02-10 03:51:33 +00:00
Luis Nachtigall
3462fc434e fix: evict image from cache on error during image loading (#26078) 2026-02-09 17:01:10 -05:00
Luis Nachtigall
561469b826 fix(mobile): handle image stream completion when no image is emitted (#25984)
* Fix image cancellation to be stream-scoped instead of widget-scoped

* fix(OneFramePlaceholderImageStreamCompleter): make onLastListenerRemoved callback synchronous with removing the last listener

* fix(OneFrameMultiImageStreamCompleter): remove unnecessary blank line in code

* fix(OneFramePlaceholderImageStreamCompleter): cancel pending requests when only cache listener remains

* fix(OneFrameMultiImageStreamCompleter): ensure onLastListenerRemoved callback is invoked only once
2026-02-09 16:59:29 -05:00
bo0tzz
937bef9a4d fix: run rocm builds on pokedex (#26062)
* fix: run rocm builds on pokedex

* fix: --parallel 48 (#26065)
2026-02-09 15:06:05 -05:00
dolfje
5f18110e97 fix(web): removing a person in an asset, doesn't remove the asset in … (#26068)
* fix(web): removing a person in an asset, doesn't remove the asset in the persons view (without refresh)

* prettier

---------

Co-authored-by: Nikos Verschore <nikos@uwsoftware.be>
2026-02-09 11:51:02 -06:00
Alex
57485023ae fix: free up space using small batch size to reliably work on Android (#26047)
* fix: free up space delete in small batch

* fix: free up space delete in small batch
2026-02-09 11:48:55 -06:00
Michel Heusschen
8a9b541dd0 fix: slideshow setting dropdown overflow (#26066) 2026-02-09 09:28:47 -06:00
Justin Xiao
25be5fc22d fix(web): prevent context menu from overflowing viewport (#26041)
* fix(web): prevent context menu from overflowing viewport

The context menu used `max-h-dvh` (100% viewport height) as its max height,
but did not account for the menu's top position. When the menu opens at
y > 0, its bottom extends beyond the viewport.

Compute `maxHeight` dynamically based on the menu's top position and apply
it as an inline style, so the menu always fits within the viewport and
scrolls when content exceeds the available space.

* fix: linting

* fix: overflow

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-09 09:26:25 -06:00
Alex
906c38273f fix: profile dialog auto dismiss after opening on iPad (#26046) 2026-02-08 17:30:57 -06:00
Alex
10b2bf7970 fix: iOS slow start (#26043) 2026-02-08 16:28:51 -06:00
Kolin
7cf8a9936a fix(web): display storage unit next to value instead of absolute positioning in admin user page (#25985)
* fix(web): display storage unit next to value instead of absolute positioning in admin user page

* chore: styling

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2026-02-08 17:22:42 +00:00
Michel Heusschen
59c4a49ffd fix: scroll jump when opening show & hide people (#25932) 2026-02-08 11:19:35 -06:00
Yaros
00486cbcc8 fix(web): toast fixed location (#25966)
fix: toast fixed location
2026-02-08 11:15:09 -06:00
Michel Heusschen
b524d7b6fd fix: reduce queue graph jitter and include paused count (#26023)
fix: reduce queue graph jitter and show paused count
2026-02-08 11:03:59 -06:00
Michel Heusschen
5b705cb723 fix: improve albums page load time on firefox (#26025) 2026-02-08 11:03:17 -06:00
Luis Nachtigall
354dd3cc3c feat(mobile): enhance album sorting functionality with order handling (#24816)
* feat: enhance album sorting functionality with effective order handling

* mobile: formatting

* test: align album sorting order in unit tests with defaultSortOrder

* test(mobile): add reverse order validation for album sorting

* chore(PR): remove OppositeSortOrder Extension and move it directly into SortOrder enum

* refactor: return sorted list directly in album sorting function

* refactor: remove sort_order_extensions.dart
2026-02-07 10:11:37 +05:30
shenlong
57483a1e7f fix: user profile refetched each time on opening app dialog (#25992)
fix: user profile on opening app dialog

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-07 10:10:17 +05:30
Romo
bcea64875f fix: image and video download complete notification shows "file_name" (#25975)
* fix: image and video download complete notification shows "file_name"

* fix lint

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-07 02:56:14 +00:00
Matthew Momjian
84e30abe5d feat(docs): version policy (#25979)
version policy
2026-02-06 20:31:57 -05:00
Jason Rasmussen
e3e243fa2b refactor: tests (#25987) 2026-02-07 00:47:54 +01:00
Jason Rasmussen
b3820c259e refactor: test factories (#25977) 2026-02-06 16:32:50 -05:00
Daniel Dietzler
a356497d96 refactor: album service small tests (#25640) 2026-02-06 08:56:46 -05:00
Michel Heusschen
16fe828913 fix: revert "fix(web): Ensure profile picture is cropped to 1:1 ratio (#25892)" (#25956)
Revert "fix(web): Ensure profile picture is cropped to 1:1 ratio (#25892)"

This reverts commit 3c77c724c5.
2026-02-06 08:21:56 -05:00
Michel Heusschen
211dc3c056 fix: add missing translations for image editor (#25957) 2026-02-06 13:08:24 +01:00
github-actions
ff9052f7f5 chore: version v2.5.5 2026-02-06 03:49:02 +00:00
Aditya Gaurav
999ce34251 fix(web): avoid transparent pixels from rounded-full during profile picture capture (#25950)
The cropContainer has rounded-full class which creates transparent
corners when captured by domtoimage.toBlob(). Override border-radius
and border to 0/none during capture to produce a clean square image.

Co-authored-by: Aditya Gaurav <aditya-ai-architect@users.noreply.github.com>
2026-02-06 03:21:27 +00:00
Vahant Sharma
491ed3d927 fix(cli): suppress startup messages for immich-admin (#25928)
fix(server): suppress startup messages for immich-admin CLI Introduce a log_message() helper function with QUIET flag to suppress informational startup output when running immich-admin. This prevents both shell messages and Node.js WASI experimental warnings from interfering with CLI help output. Fixes: #25909
2026-02-05 18:34:15 -05:00
Jason Rasmussen
94e86c6e76 fix: dedupe version announcement modal (#25946)
fix: version announcement modal
2026-02-05 16:24:10 -05:00
github-actions
8581b4f350 chore: version v2.5.4 2026-02-05 21:16:52 +00:00
Weblate (bot)
4835d5f97f chore(web): update translations (#25585)
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/bg/
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/de/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/eo/
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/fr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ga/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/hu/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/lv/
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/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/th/
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/yue_Hant/
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: Ahmed Khaleel Shihab <ahmed91shihab@gmail.com>
Co-authored-by: Aindriú Mac Giolla Eoin <aindriu80@gmail.com>
Co-authored-by: Alan Lai <alanlai22@outlook.com>
Co-authored-by: Aldi Maulana <aldim222@gmail.com>
Co-authored-by: Andrei P <darkandrei197@gmail.com>
Co-authored-by: Ari Selseng <ari@selseng.net>
Co-authored-by: Beans <leey0818@gmail.com>
Co-authored-by: Bryan Tank <perso@bryantank.fr>
Co-authored-by: Cheng Chien <jamesqian1999@gmail.com>
Co-authored-by: Chun-Hei Lam <chun.lam18@imperial.ac.uk>
Co-authored-by: Ciprriann <cipriannebeja@gmail.com>
Co-authored-by: Damien Doumax <damien@orfaon.net>
Co-authored-by: Fran Sánchez <mrurkaz@gmail.com>
Co-authored-by: Hans Cats <hanscats@gmail.com>
Co-authored-by: Happy <59247878+happy2452354@users.noreply.github.com>
Co-authored-by: Hurricane_32 <rodrigorimo@hotmail.com>
Co-authored-by: Indrek Haav <indrek.haav@hotmail.com>
Co-authored-by: Ivan Dimitrov <idimitrov08@gmail.com>
Co-authored-by: Jordy H <jordy@hoebergen.net>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Junghyuk Kwon <kwon@junghy.uk>
Co-authored-by: KecskeTech <teonyitas@gmail.com>
Co-authored-by: Kim Hokyeong <manmen.mi1375@gmail.com>
Co-authored-by: Marc Casillas <mcasillassu@gmail.com>
Co-authored-by: Masani Amin Yasin <langugetranslategay.taps663@silomails.com>
Co-authored-by: Masani Amin Yasin <masaniamin@proton.me>
Co-authored-by: Matjaž T. <matjaz@moj-svet.si>
Co-authored-by: Melih Ozkan <malihozkan156@gmail.com>
Co-authored-by: Mārtiņš Bruņenieks <martinsb@gmail.com>
Co-authored-by: Nicolò <nicosemp@gmail.com>
Co-authored-by: Nik Grebovšek <nikigre@gmail.com>
Co-authored-by: Oleksandr Yurov <oyurov@icloud.com>
Co-authored-by: PPNplus <ppnplus@protonmail.com>
Co-authored-by: Phantom0174 <darrenhsiou@gmail.com>
Co-authored-by: Pieter Lexis <pieter@plexis.eu>
Co-authored-by: Rey <x46puy43k@mozmail.com>
Co-authored-by: Roman Fedorchuk <roma.fedorchuk@gmail.com>
Co-authored-by: Samuel Medeiros <steixeiramedeiros@gmail.com>
Co-authored-by: Shawn <xiaxinx@gmail.com>
Co-authored-by: Stan P <g97d6liib@mozmail.com>
Co-authored-by: Taiki M. <vexingly-many-mace@duck.com>
Co-authored-by: Temuri Doghonadze <temuri.doghonadze@gmail.com>
Co-authored-by: Tim Morley <weblate.3919org@timsk.org>
Co-authored-by: Tim Müller <info@timxllr.de>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: balc11 <balc11@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: john family <tyutee09@gmail.com>
Co-authored-by: kellogcheung <kellogcheung@gmail.com>
Co-authored-by: liimee <git.taaa@fedora.email>
Co-authored-by: millallo <millallo@tiscali.it>
Co-authored-by: pyccl <changcongliang@163.com>
Co-authored-by: shiuh67 <shiuh.cheng@gmail.com>
Co-authored-by: stesoma <soma.steltzer@gmail.com>
Co-authored-by: Óscar Fernández Díaz <42654671+oscfdezdz@users.noreply.github.com>
Co-authored-by: გიორგი კუცია <giorgi.kucia@gmail.com>
2026-02-05 21:14:41 +00:00
Aram Akhavan
435565be1b docs: remove writeTimeout on traefik example (#25837)
Don't recommend writeTimeout: 600s for traefik example
2026-02-05 20:58:10 +00:00
Jason Rasmussen
94d3039606 fix: prettier not found (#25941) 2026-02-05 20:44:09 +00:00
Min Idzelis
092ebe01a5 fix: queue assets missing fullsize files for thumbnail regeneration (#25794)
* fix: queue assets missing fullsize files for thumbnail regeneration

* refactor: query

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
2026-02-05 19:31:13 +00:00
Brandon Wees
37e5968a7a fix: face and edit handling (#25738)
* fix: handle edits when creating face
2026-02-05 19:29:46 +00:00
Dion de Koning
cfc5ed5997 fix: timezone issue in tests (#25937)
* Fixed an issue where time tests fail in some timezones

* Revert previous fix and add TZ env variable to fix the issue

* Revert other changes and align TZ fix between server and web

* Revert package lock file

---------

Co-authored-by: Dion de Koning <dion@DionK01.local>
2026-02-05 19:24:23 +00:00
Romo
1b3c0e4f65 fix: image download complete notification shows an extra {file_name} template tag (#25936)
* [fix] Image download complete notification shows an extra {file_name} template tag

fixes https://github.com/immich-app/immich/issues/25690
added

```dart
final FileName = 'file_name'.t( args: {'file_name': '{filename}', }, );
```

* chore: fix formatting

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-05 19:19:33 +00:00
Mert
fd49d7d566 chore(mobile): skip macro validation (#25744)
* use podfile

* update deps
2026-02-05 14:16:21 -05:00
cmdPromptCritical
ad9f3cfa05 fix(mobile): cancel share download when dialog is dismissed (#25466)
* fix(mobile): cancel share download when dialog is dismissed

* refactor: centralize temporary file cleanup logic

* refactor: replace `CancellationToken` with `Completer<void>` for asset sharing cancellation

---------

Co-authored-by: cmdpromptcritical <cmdpromptcritical@github.com>
2026-02-05 19:08:35 +00:00
Dane
9d8efe2685 fix(docs): add missing --json-output arg to CLI example (#25870) 2026-02-05 14:00:27 -05:00
Paul Makles
ed4d9abdae fix(server): use provided database username for restore & ensure name is not mangled (#25679)
* fix(server): use provided database name/username for restore & ensure name is not mangled

fixes #25633

Signed-off-by: izzy <me@insrt.uk>

* chore: add db switch back but with comments

Signed-off-by: izzy <me@insrt.uk>

* refactor: no need to restore database since it's not technically possible
chore: late fallback for username in parameter builder

Signed-off-by: izzy <me@insrt.uk>

* chore: type fix

Signed-off-by: izzy <me@insrt.uk>

* chore: re-use the username we just pulled out

---------

Signed-off-by: izzy <me@insrt.uk>
2026-02-05 13:59:05 -05:00
Vahant Sharma
ac9f6921cc fix(server): add missing history metadata to getAuthStatus endpoint (#25927)
* fix(server): add missing history metadata to getAuthStatus endpoint

* chore: regenerate openapi specs
2026-02-05 18:56:44 +00:00
shenlong
f0da875e37 fix: allow clear text traffic on android (#25933)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-06 00:15:33 +05:30
Peter Ombodi
b0e1a425b3 fix(mobile): jump to previous asset when last asset is deleted (#25563)
* fix(mobile): fix wrong index, update pageController

* fix(mobile): refactor code

---------

Co-authored-by: Peter Ombodi <peter.ombodi@gmail.com>
Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
2026-02-05 18:19:40 +00:00
Yaros
7211d80e5f docs: update mobile setup to use mise (#25847)
docs: update mobile setup to mise
2026-02-05 12:55:38 -05:00
shenlong
92c79a7122 chore: increase cache_size and use memory temp store (#25930)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-05 11:55:07 -06:00
renovate[bot]
7580521a76 chore(deps): update grafana/grafana docker tag to v12.3.2 (#25840)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-05 12:52:07 -05:00
shenlong
2dd3a764ae fix: timezone in timeline bucketing (#25894)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-05 11:47:16 -06:00
Thomas
a42c08ed84 fix(mobile): reset asset index on timeline refresh (#25729)
The current asset changes when the timeline refreshes, which can be
quite jarring. Assets are tracked by their index, and that index becomes
stale when the timeline refreshes. This can be resolved by updating the
index of asset based on a unique identifier (like the hero tag).
2026-02-05 11:46:38 -06:00
Aditya Gaurav
3c77c724c5 fix(web): Ensure profile picture is cropped to 1:1 ratio (#25892)
* fix(web): Ensure profile picture is cropped to 1:1 ratio

Fixes #20097

The profile picture was being captured from the PhotoViewer element
which could have non-square dimensions based on the original image.

Changed to capture from the crop container element which has the
aspect-square class, ensuring the output is always 1:1 ratio.

* fix: remove trailing whitespace to pass prettier check

---------

Co-authored-by: Aditya Gaurav <aditya-ai-architect@users.noreply.github.com>
2026-02-05 11:45:06 -06:00
Yaros
84b2979485 docs: non-code contributing (datasets) (#25850)
docs: datasets
2026-02-05 11:44:35 -06:00
Paul Makles
e9c2ca008a docs: update manual backup/restore to match the automatic process (#25924)
* docs: update manual backup/restore to match automatic process

closes #25772

* docs: update DB_NAME to DB_DATABASE_NAME
2026-02-05 12:43:33 -05:00
Daniel Dietzler
9c098109e0 fix: time zone upserts (#25889) 2026-02-05 12:43:03 -05:00
Mert
27a2808470 fix(mobile): mtls on native clients (#25802)
* handle mtls on ios

* update android impl

* ui improvements

* dead code

* no need to store data separately

* improve concurrency

* dead code

* add migration

* remove unused dependency

* trust user-installed certs

* removed print statement

* fix ios

* improve android styling

* outdated comments

* update lock file

* handle translation

* fix prompt cancellation

* fix video playback

* Apply suggestion from @shenlong-tanwen

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

* Apply suggestion from @shenlong-tanwen

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

* formatting

---------

Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
2026-02-05 17:42:53 +00:00
Alex
0a8a65a45e fix: file name search label (#25916) 2026-02-05 17:24:58 +00:00
shenlong
2b6055e830 chore: do not process cloud ids during sync & hash (#25914)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-02-05 11:23:50 -06:00
Mert
ba2dfa7df6 refactor(mobile): consolidate image requests (#25743)
remote url image provider

remove cached_network_image

formatting

linting

remove thumb provider

formatting
2026-02-05 12:16:42 -05:00
Jason Rasmussen
237ea3aedd chore: update oauth documentation (#25907)
* chore: prefer lowercase for non i18n labels

* chore: update documentation
2026-02-05 09:00:00 -05:00
Michel Heusschen
810e9254f3 fix: preserve hidden people state across pagination (#25886)
* fix: preserve hidden people state across pagination

* track overrides instead

* use event instead of bind:people

* update test
2026-02-05 08:51:30 -05:00
Michel Heusschen
57e0835b46 fix: ensure theme stays in sync with @immich/ui (#25922) 2026-02-05 08:36:20 -05:00
Michel Heusschen
e97030a7ae fix: make switch labels properly clickable (#25898) 2026-02-05 12:09:27 +01:00
Michel Heusschen
fdf06a91cc fix: improve asset editor exit handling (#25917) 2026-02-05 12:01:54 +01:00
Michel Heusschen
732303661b fix: allow null tagIds in search dto (#25920) 2026-02-05 11:52:35 +01:00
Alex
e9f8521a50 fix: date time picker text color in dark mode (#25883) 2026-02-04 18:45:56 +00:00
Michel Heusschen
6bd60270b4 fix: correctly sync shared link download with metadata toggle (#25885) 2026-02-04 12:38:42 -05:00
Michel Heusschen
5a6fd7af06 fix: close tag modal after tagging assets (#25884) 2026-02-04 12:35:00 -05:00
Jason Rasmussen
6cdebdd3b3 fix(server): deleting stacked assets (#25874)
* fix(server): deleting stacked assets

* fix: log a warning when removing an empty directory fails
2026-02-04 17:33:37 +00:00
Jason Rasmussen
9dddccd831 fix: null validation (#25891) 2026-02-04 12:27:52 -05:00
Min Idzelis
440b3b4c6f chore: move devcontainer specific tasks to devcontainer.json (#25881)
refactor: move devcontainer specific tasks to devcontainer.json
2026-02-03 23:04:09 -05:00
Jason Rasmussen
3ea65f8d27 fix: album dto docs (#25873) 2026-02-03 21:05:18 +00:00
github-actions
38c1f0b1fd chore: version v2.5.3 2026-02-03 18:14:21 +00:00
Michel Heusschen
5212bca3d0 fix: reset zoom when navigating between assets (#25863) 2026-02-03 11:07:06 -06:00
Daniel Dietzler
2990bde0bb fix: metadata extraction race condition (#25866) 2026-02-03 11:03:27 -06:00
Michel Heusschen
af1ecaf5cc fix: prevent backspace from accidentally triggering delete modals (#25858)
* fix: prevent backspace from accidentally triggering delete modals

* ignore input fields instead of removing shortcut
2026-02-03 16:42:46 +00:00
Alex
3870ebc3c6 fix: prevent album page get rebuilt when resuming app (#25862) 2026-02-03 16:35:53 +00:00
Michel Heusschen
0a9d969b47 fix: prevent stale values in edit user form after save (#25859) 2026-02-03 17:29:01 +01:00
Daniel Dietzler
94965f6d66 chore: rework tags sidebar (#25855) 2026-02-03 16:06:26 +00:00
Alex
8872d2c7ae chore: remove swift logs (#25857) 2026-02-03 16:00:17 +00:00
Alex
23445fdcc1 fix: upload progress bar flickering (#25829)
* fix: upload progress bar flickering

* pr feedback and more logs
2026-02-03 09:28:29 -06:00
renovate[bot]
25f2273e24 chore(deps): update redis:6.2-alpine docker digest to 46884be (#25839)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-03 12:56:56 +01:00
Min Idzelis
95e8e474b8 fix(web): enable asset viewer navigation across memory boundaries (#25741) 2026-02-02 10:12:08 -06:00
Timon
9f52d864cf chore(ml): replace black with ruff format (#25578) 2026-02-02 09:02:06 -05:00
Mees Frensel
0273dcb0cf fix(web): user settings styling (#25775) 2026-02-02 08:47:28 -05:00
2525 changed files with 146678 additions and 123806 deletions

View File

@@ -2,6 +2,7 @@
"name": "Immich - Backend, Frontend and ML",
"service": "immich-server",
"runServices": [
"immich-init",
"immich-server",
"redis",
"database",
@@ -26,7 +27,59 @@
"vitest.explorer",
"ms-playwright.playwright",
"ms-azuretools.vscode-docker"
]
],
"settings": {
"tasks": {
"version": "2.0.0",
"tasks": [
{
"label": "Immich API Server (Nest)",
"type": "shell",
"command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0",
"isBackground": true,
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": true,
"clear": false,
"group": "Devcontainer tasks",
"close": true
},
"runOptions": {
"runOn": "folderOpen"
},
"problemMatcher": []
},
{
"label": "Immich Web Server (Vite)",
"type": "shell",
"command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0",
"isBackground": true,
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": true,
"clear": false,
"group": "Devcontainer tasks",
"close": true
},
"runOptions": {
"runOn": "folderOpen"
},
"problemMatcher": []
},
{
"label": "Build Immich CLI",
"type": "shell",
"command": "pnpm --filter cli build:dev"
}
]
}
}
}
},
"features": {
@@ -56,8 +109,8 @@
}
},
"overrideCommand": true,
"workspaceFolder": "/workspaces/immich",
"remoteUser": "node",
"workspaceFolder": "/usr/src/app",
"remoteUser": "root",
"userEnvProbe": "loginInteractiveShell",
"remoteEnv": {
// The location where your uploaded files are stored

View File

@@ -1,23 +1,17 @@
services:
immich-app-base:
image: busybox
immich-server:
extends:
service: immich-app-base
profiles: !reset []
image: immich-server-dev:latest
build:
target: dev-container-mobile
environment:
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
volumes: !override # bind mount host to /workspaces/immich
- ..:/workspaces/immich
volumes:
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
- /etc/localtime:/etc/localtime:ro
immich-web:
env_file: !reset []

View File

@@ -2,6 +2,7 @@
"name": "Immich - Mobile",
"service": "immich-server",
"runServices": [
"immich-init",
"immich-server",
"redis",
"database",
@@ -35,7 +36,7 @@
},
"forwardPorts": [],
"overrideCommand": true,
"workspaceFolder": "/workspaces/immich",
"workspaceFolder": "/usr/src/app",
"remoteUser": "node",
"userEnvProbe": "loginInteractiveShell",
"remoteEnv": {

View File

@@ -2,11 +2,6 @@
export IMMICH_PORT="${DEV_SERVER_PORT:-2283}"
export DEV_PORT="${DEV_PORT:-3000}"
# search for immich directory inside workspace.
# /workspaces/immich is the bind mount, but other directories can be mounted if runing
# Devcontainer: Clone [repository|pull request] in container volumne
WORKSPACES_DIR="/workspaces"
IMMICH_DIR="$WORKSPACES_DIR/immich"
IMMICH_DEVCONTAINER_LOG="$HOME/immich-devcontainer.log"
log() {
@@ -30,52 +25,8 @@ run_cmd() {
return "${PIPESTATUS[0]}"
}
# Find directories excluding /workspaces/immich
mapfile -t other_dirs < <(find "$WORKSPACES_DIR" -mindepth 1 -maxdepth 1 -type d ! -path "$IMMICH_DIR" ! -name ".*")
if [ ${#other_dirs[@]} -gt 1 ]; then
log "Error: More than one directory found in $WORKSPACES_DIR other than $IMMICH_DIR."
exit 1
elif [ ${#other_dirs[@]} -eq 1 ]; then
export IMMICH_WORKSPACE="${other_dirs[0]}"
else
export IMMICH_WORKSPACE="$IMMICH_DIR"
fi
export IMMICH_WORKSPACE="/usr/src/app"
log "Found immich workspace in $IMMICH_WORKSPACE"
log ""
fix_permissions() {
log "Fixing permissions for ${IMMICH_WORKSPACE}"
# Change ownership for directories that exist
for dir in "${IMMICH_WORKSPACE}/.vscode" \
"${IMMICH_WORKSPACE}/server/upload" \
"${IMMICH_WORKSPACE}/.pnpm-store" \
"${IMMICH_WORKSPACE}/.github/node_modules" \
"${IMMICH_WORKSPACE}/cli/node_modules" \
"${IMMICH_WORKSPACE}/e2e/node_modules" \
"${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \
"${IMMICH_WORKSPACE}/server/node_modules" \
"${IMMICH_WORKSPACE}/server/dist" \
"${IMMICH_WORKSPACE}/web/node_modules" \
"${IMMICH_WORKSPACE}/web/dist"; do
if [ -d "$dir" ]; then
run_cmd sudo chown node -R "$dir"
fi
done
log ""
}
install_dependencies() {
log "Installing dependencies"
(
cd "${IMMICH_WORKSPACE}" || exit 1
export CI=1 FROZEN=1 OFFLINE=1
run_cmd make setup-web-dev setup-server-dev
)
log ""
}

View File

@@ -1,26 +1,21 @@
services:
immich-app-base:
image: busybox
immich-server:
extends:
service: immich-app-base
profiles: !reset []
image: immich-server-dev:latest
build:
target: dev-container-server
env_file: !reset []
hostname: immich-dev
environment:
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
volumes: !override
- ..:/workspaces/immich
volumes:
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
- /etc/localtime:/etc/localtime:ro
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
- pnpm_store_server:/buildcache/pnpm-store
- ../plugins:/build/corePlugin
immich-web:
env_file: !reset []

View File

@@ -1,17 +0,0 @@
#!/bin/bash
# shellcheck source=common.sh
# shellcheck disable=SC1091
source /immich-devcontainer/container-common.sh
log "Setting up Immich dev container..."
fix_permissions
log "Setup complete, please wait while backend and frontend services automatically start"
log
log "If necessary, the services may be manually started using"
log
log "$ /immich-devcontainer/container-start-backend.sh"
log "$ /immich-devcontainer/container-start-frontend.sh"
log
log "From different terminal windows, as these scripts automatically restart the server"
log "on error, and will continuously run in a loop"

6
.gitattributes vendored
View File

@@ -6,6 +6,12 @@ mobile/openapi/**/*.dart linguist-generated=true
mobile/lib/**/*.g.dart -diff -merge
mobile/lib/**/*.g.dart linguist-generated=true
mobile/android/**/*.g.kt -diff -merge
mobile/android/**/*.g.kt linguist-generated=true
mobile/ios/**/*.g.swift -diff -merge
mobile/ios/**/*.g.swift linguist-generated=true
mobile/lib/**/*.drift.dart -diff -merge
mobile/lib/**/*.drift.dart linguist-generated=true

2
.github/.nvmrc vendored
View File

@@ -1 +1 @@
24.13.0
24.15.0

View File

@@ -1,7 +1,7 @@
{
"scripts": {
"format": "prettier --check .",
"format:fix": "prettier --write ."
"format": "prettier --cache --check .",
"format:fix": "prettier --cache --write --list-different ."
},
"devDependencies": {
"prettier": "^3.7.4"

View File

@@ -26,6 +26,7 @@ The `/api/something` endpoint is now `/api/something-else`
## Checklist:
- [ ] I have carefully read CONTRIBUTING.md
- [ ] I have performed a self-review of my own code
- [ ] I have made corresponding changes to the documentation if applicable
- [ ] I have no unrelated changes in the PR.

148
.github/workflows/auto-close.yml vendored Normal file
View File

@@ -0,0 +1,148 @@
name: Auto-close PRs
on:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types: [opened, edited, labeled]
permissions: {}
jobs:
parse_template:
runs-on: ubuntu-latest
if: ${{ github.event.action != 'labeled' && github.event.pull_request.head.repo.fork == true }}
permissions:
contents: read
outputs:
uses_template: ${{ steps.check.outputs.uses_template }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: .github/pull_request_template.md
sparse-checkout-cone-mode: false
persist-credentials: false
- name: Check required sections
id: check
env:
BODY: ${{ github.event.pull_request.body }}
run: |
OK=true
while IFS= read -r header; do
printf '%s\n' "$BODY" | grep -qF "$header" || OK=false
done < <(sed '/<!--/,/-->/d' .github/pull_request_template.md | grep "^## ")
echo "uses_template=$OK" | tee --append "$GITHUB_OUTPUT"
close_template:
runs-on: ubuntu-latest
needs: parse_template
if: >-
${{
needs.parse_template.outputs.uses_template == 'false'
&& github.event.pull_request.state != 'closed'
&& !contains(github.event.pull_request.labels.*.name, 'auto-closed:template')
}}
permissions:
pull-requests: write
steps:
- name: Comment and close
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f body="This PR has been automatically closed as the description doesn't follow [our template](https://github.com/immich-app/immich/blob/main/.github/pull_request_template.md). After you edit it to match the template, the PR will automatically be reopened." \
-f query='
mutation CommentAndClosePR($prId: ID!, $body: String!) {
addComment(input: {
subjectId: $prId,
body: $body
}) {
__typename
}
closePullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
}'
- name: Add label
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: gh pr edit "$PR_NUMBER" --repo "${{ github.repository }}" --add-label "auto-closed:template"
close_llm:
runs-on: ubuntu-latest
if: ${{ github.event.action == 'labeled' && github.event.label.name == 'auto-closed:llm' }}
permissions:
pull-requests: write
steps:
- name: Comment and close
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f body="Thank you for your interest in contributing to Immich! Unfortunately this PR looks like it was generated using an LLM. As noted in our [CONTRIBUTING.md](https://github.com/immich-app/immich/blob/main/CONTRIBUTING.md#use-of-generative-ai), we request that you don't use LLMs to generate PRs as those are not a good use of maintainer time." \
-f query='
mutation CommentAndClosePR($prId: ID!, $body: String!) {
addComment(input: {
subjectId: $prId,
body: $body
}) {
__typename
}
closePullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
}'
reopen:
runs-on: ubuntu-latest
needs: parse_template
if: >-
${{
needs.parse_template.outputs.uses_template == 'true'
&& github.event.pull_request.state == 'closed'
&& contains(github.event.pull_request.labels.*.name, 'auto-closed:template')
}}
permissions:
pull-requests: write
steps:
- name: Remove template label
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: gh pr edit "$PR_NUMBER" --repo "${{ github.repository }}" --remove-label "auto-closed:template" || true
- name: Check for remaining auto-closed labels
id: check_labels
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
REMAINING=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json labels \
--jq '[.labels[].name | select(startswith("auto-closed:"))] | length')
echo "remaining=$REMAINING" | tee --append "$GITHUB_OUTPUT"
- name: Reopen PR
if: ${{ steps.check_labels.outputs.remaining == '0' }}
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f query='
mutation ReopenPR($prId: ID!) {
reopenPullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
}'

View File

@@ -51,14 +51,14 @@ jobs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Check what should run
id: check
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
with:
github-token: ${{ steps.token.outputs.token }}
filters: |
@@ -79,12 +79,12 @@ jobs:
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref || github.sha }}
persist-credentials: false
@@ -96,14 +96,14 @@ jobs:
working-directory: ./mobile
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'zulu'
java-version: '17'
- name: Restore Gradle Cache
id: cache-gradle-restore
uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.gradle/caches
@@ -114,14 +114,14 @@ jobs:
key: build-mobile-gradle-${{ runner.os }}-main
- name: Setup Flutter SDK
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: 'stable'
flutter-version-file: ./mobile/pubspec.yaml
cache: true
- name: Setup Android SDK
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1
with:
packages: ''
@@ -153,14 +153,14 @@ jobs:
fi
- name: Publish Android Artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-apk-signed
path: mobile/build/app/outputs/flutter-apk/*.apk
- name: Save Gradle Cache
id: cache-gradle-save
uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
if: github.ref == 'refs/heads/main'
with:
path: |
@@ -185,13 +185,13 @@ jobs:
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref || github.sha }}
persist-credentials: false
- name: Setup Flutter SDK
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: 'stable'
flutter-version-file: ./mobile/pubspec.yaml
@@ -210,7 +210,7 @@ jobs:
working-directory: ./mobile
- name: Setup Ruby
uses: ruby/setup-ruby@v1
uses: ruby/setup-ruby@7372622e62b60b3cb750dcd2b9e32c247ffec26a # v1.302.0
with:
ruby-version: '3.3'
bundler-cache: true
@@ -291,7 +291,7 @@ jobs:
security delete-keychain build.keychain || true
- name: Upload IPA artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ios-release-ipa
path: mobile/ios/Runner.ipa

View File

@@ -19,13 +19,13 @@ jobs:
actions: write
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Check out code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}

31
.github/workflows/check-openapi.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Check OpenAPI
on:
workflow_dispatch:
pull_request:
paths:
- 'open-api/**'
- '.github/workflows/check-openapi.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
check-openapi:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Check for breaking API changes
uses: oasdiff/oasdiff-action/breaking@f8cb9308b42121e793f835bd14c0b8090420430c # v0.0.39
with:
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
revision: open-api/immich-openapi-specs.json
fail-on: ERR

View File

@@ -31,21 +31,21 @@ jobs:
working-directory: ./cli
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './cli/.nvmrc'
registry-url: 'https://registry.npmjs.org'
@@ -71,25 +71,25 @@ jobs:
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
registry: ghcr.io
@@ -104,7 +104,7 @@ jobs:
- name: Generate docker image tags
id: metadata
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
flavor: |
latest=false
@@ -115,7 +115,7 @@ jobs:
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
- name: Build and push image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
file: cli/Dockerfile
platforms: linux/amd64,linux/arm64

View File

@@ -35,7 +35,7 @@ jobs:
needs: [get_body, should_run]
if: ${{ needs.should_run.outputs.should_run == 'true' }}
container:
image: ghcr.io/immich-app/mdq:main@sha256:ab9f163cd5d5cec42704a26ca2769ecf3f10aa8e7bae847f1d527cdf075946e6
image: ghcr.io/immich-app/mdq:main@sha256:557cca601891b8b7d78b940071d35aaf7aaeb9b327d19b22cf282118edbc5272
outputs:
checked: ${{ steps.get_checkbox.outputs.checked }}
steps:

View File

@@ -44,20 +44,20 @@ jobs:
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -70,7 +70,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -83,6 +83,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
category: '/language:${{matrix.language}}'

View File

@@ -23,14 +23,14 @@ jobs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Check what should run
id: check
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
with:
github-token: ${{ steps.token.outputs.token }}
filters: |
@@ -60,7 +60,7 @@ jobs:
suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn']
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -90,7 +90,7 @@ jobs:
suffix: ['']
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -131,8 +131,8 @@ jobs:
- device: rocm
suffixes: '-rocm'
platforms: linux/amd64
runner-mapping: '{"linux/amd64": "mich"}'
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@0477486d82313fba68f7c82c034120a4b8981297 # multi-runner-build-workflow-v2.1.0
runner-mapping: '{"linux/amd64": "pokedex-large"}'
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@61a0fc2b41524edcc7c9fffb8bb178e6b0ccf21d # multi-runner-build-workflow-v2.3.0
permissions:
contents: read
actions: read
@@ -155,7 +155,7 @@ jobs:
name: Build and Push Server
needs: pre-job
if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }}
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@0477486d82313fba68f7c82c034120a4b8981297 # multi-runner-build-workflow-v2.1.0
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@61a0fc2b41524edcc7c9fffb8bb178e6b0ccf21d # multi-runner-build-workflow-v2.3.0
permissions:
contents: read
actions: read
@@ -178,7 +178,7 @@ jobs:
runs-on: ubuntu-latest
if: always()
steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
with:
needs: ${{ toJSON(needs) }}
@@ -189,6 +189,6 @@ jobs:
runs-on: ubuntu-latest
if: always()
steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
with:
needs: ${{ toJSON(needs) }}

View File

@@ -21,14 +21,14 @@ jobs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Check what should run
id: check
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
with:
github-token: ${{ steps.token.outputs.token }}
filters: |
@@ -54,23 +54,23 @@ jobs:
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './docs/.nvmrc'
cache: 'pnpm'
@@ -86,7 +86,7 @@ jobs:
run: pnpm build
- name: Upload build output
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: docs-build-output
path: docs/build/

View File

@@ -20,7 +20,7 @@ jobs:
artifact: ${{ steps.get-artifact.outputs.result }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
@@ -29,7 +29,7 @@ jobs:
run: echo 'The triggering workflow did not succeed' && exit 1
- name: Get artifact
id: get-artifact
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.token.outputs.token }}
script: |
@@ -48,7 +48,7 @@ jobs:
return { found: true, id: matchArtifact.id };
- name: Determine deploy parameters
id: parameters
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
with:
@@ -119,23 +119,23 @@ jobs:
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@cd24790a7f5f6439ac32cc94f5523cb2de8bfa8c # use-mise-action-v1.1.0
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Load parameters
id: parameters
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PARAM_JSON: ${{ needs.checks.outputs.parameters }}
with:
@@ -147,7 +147,7 @@ jobs:
core.setOutput("shouldDeploy", parameters.shouldDeploy);
- name: Download artifact
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }}
with:
@@ -192,16 +192,13 @@ jobs:
' >> $GITHUB_OUTPUT
- name: Publish to Cloudflare Pages
# TODO: Action is deprecated
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1.5.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ steps.docs-output.outputs.projectName }}
workingDirectory: 'docs'
directory: 'build'
branch: ${{ steps.parameters.outputs.name }}
wranglerVersion: '3'
working-directory: docs
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
PROJECT_NAME: ${{ steps.docs-output.outputs.projectName }}
BRANCH_NAME: ${{ steps.parameters.outputs.name }}
run: mise run //docs:deploy
- name: Deploy Docs Release Domain
if: ${{ steps.parameters.outputs.event == 'release' }}
@@ -214,7 +211,7 @@ jobs:
run: 'mise run //deployment:tf apply'
- name: Comment
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
if: ${{ steps.parameters.outputs.event == 'pr' }}
with:
token: ${{ steps.token.outputs.token }}

View File

@@ -17,19 +17,19 @@ jobs:
pull-requests: write
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup Mise
uses: immich-app/devtools/actions/use-mise@cd24790a7f5f6439ac32cc94f5523cb2de8bfa8c # use-mise-action-v1.1.0
uses: immich-app/devtools/actions/use-mise@035e80a7d4355d5f087ffb95db9e4a0944c04e56 # use-mise-action-v1.1.3
- name: Destroy Docs Subdomain
env:
@@ -42,7 +42,7 @@ jobs:
run: 'mise run //deployment:tf destroy -- -refresh=false'
- name: Comment
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
with:
token: ${{ steps.token.outputs.token }}
number: ${{ github.event.number }}

View File

@@ -16,39 +16,39 @@ jobs:
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: 'Checkout'
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.ref }}
token: ${{ steps.generate-token.outputs.token }}
persist-credentials: true
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@08c4be7e2e672a47d11bd04269e27e5f3e8529cb # v6.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Fix formatting
run: pnpm --recursive install && pnpm run --recursive --parallel fix:format
run: pnpm --recursive install && pnpm run --recursive --if-present --parallel format:fix
- name: Commit and push
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10.0.0
with:
default_author: github_actions
message: 'chore: fix formatting'
- name: Remove label
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
if: always()
with:
github-token: ${{ steps.generate-token.outputs.token }}

View File

@@ -31,7 +31,7 @@ jobs:
- name: Generate a token
id: generate_token
if: ${{ inputs.skip != true }}
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}

View File

@@ -14,13 +14,13 @@ jobs:
pull-requests: write
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Require PR to have a changelog label
uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1
uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2
with:
token: ${{ steps.token.outputs.token }}
mode: exactly

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}

View File

@@ -50,26 +50,26 @@ jobs:
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ steps.generate-token.outputs.token }}
persist-credentials: true
ref: main
- name: Install uv
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@08c4be7e2e672a47d11bd04269e27e5f3e8529cb # v6.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
@@ -86,7 +86,7 @@ jobs:
- name: Commit and tag
id: push-tag
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10.0.0
with:
default_author: github_actions
message: 'chore: version ${{ steps.output.outputs.version }}'
@@ -124,25 +124,25 @@ jobs:
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ steps.generate-token.outputs.token }}
persist-credentials: false
- name: Download APK
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-apk-signed
github-token: ${{ steps.generate-token.outputs.token }}
- name: Create draft release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
draft: true
tag_name: ${{ needs.bump_version.outputs.version }}
@@ -151,6 +151,7 @@ jobs:
body_path: misc/release/notes.tmpl
files: |
docker/docker-compose.yml
docker/docker-compose.rootless.yml
docker/example.env
docker/hwaccel.ml.yml
docker/hwaccel.transcoding.yml

View File

@@ -14,12 +14,12 @@ jobs:
pull-requests: write
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
- uses: mshick/add-pr-comment@64b8e914979889d746c99dea15a76e77ef64580a # v3.10.0
with:
github-token: ${{ steps.token.outputs.token }}
message-id: 'preview-status'
@@ -32,12 +32,12 @@ jobs:
pull-requests: write
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.token.outputs.token }}
script: |
@@ -48,14 +48,14 @@ jobs:
name: 'preview'
})
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
- uses: mshick/add-pr-comment@64b8e914979889d746c99dea15a76e77ef64580a # v3.10.0
if: ${{ github.event.pull_request.head.repo.fork }}
with:
github-token: ${{ steps.token.outputs.token }}
message-id: 'preview-status'
message: 'PRs from forks cannot have preview environments.'
- uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2
- uses: mshick/add-pr-comment@64b8e914979889d746c99dea15a76e77ef64580a # v3.10.0
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
github-token: ${{ steps.token.outputs.token }}

View File

@@ -1,170 +0,0 @@
name: Manage release PR
on:
workflow_dispatch:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
permissions: {}
jobs:
bump:
runs-on: ubuntu-latest
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
token: ${{ steps.generate-token.outputs.token }}
persist-credentials: true
ref: main
- name: Install uv
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Determine release type
id: bump-type
uses: ietf-tools/semver-action@c90370b2958652d71c06a3484129a4d423a6d8a8 # v1.11.0
with:
token: ${{ steps.generate-token.outputs.token }}
- name: Bump versions
env:
TYPE: ${{ steps.bump-type.outputs.bump }}
run: |
if [ "$TYPE" == "none" ]; then
exit 1 # TODO: Is there a cleaner way to abort the workflow?
fi
misc/release/pump-version.sh -s $TYPE -m true
- name: Manage Outline release document
id: outline
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
OUTLINE_API_KEY: ${{ secrets.OUTLINE_API_KEY }}
NEXT_VERSION: ${{ steps.bump-type.outputs.next }}
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const fs = require('fs');
const outlineKey = process.env.OUTLINE_API_KEY;
const parentDocumentId = 'da856355-0844-43df-bd71-f8edce5382d9'
const collectionId = 'e2910656-714c-4871-8721-447d9353bd73';
const baseUrl = 'https://outline.immich.cloud';
const listResponse = await fetch(`${baseUrl}/api/documents.list`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${outlineKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ parentDocumentId })
});
if (!listResponse.ok) {
throw new Error(`Outline list failed: ${listResponse.statusText}`);
}
const listData = await listResponse.json();
const allDocuments = listData.data || [];
const document = allDocuments.find(doc => doc.title === 'next');
let documentId;
let documentUrl;
let documentText;
if (!document) {
// Create new document
console.log('No existing document found. Creating new one...');
const notesTmpl = fs.readFileSync('misc/release/notes.tmpl', 'utf8');
const createResponse = await fetch(`${baseUrl}/api/documents.create`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${outlineKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'next',
text: notesTmpl,
collectionId: collectionId,
parentDocumentId: parentDocumentId,
publish: true
})
});
if (!createResponse.ok) {
throw new Error(`Failed to create document: ${createResponse.statusText}`);
}
const createData = await createResponse.json();
documentId = createData.data.id;
const urlId = createData.data.urlId;
documentUrl = `${baseUrl}/doc/next-${urlId}`;
documentText = createData.data.text || '';
console.log(`Created new document: ${documentUrl}`);
} else {
documentId = document.id;
const docPath = document.url;
documentUrl = `${baseUrl}${docPath}`;
documentText = document.text || '';
console.log(`Found existing document: ${documentUrl}`);
}
// Generate GitHub release notes
console.log('Generating GitHub release notes...');
const releaseNotesResponse = await github.rest.repos.generateReleaseNotes({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: `${process.env.NEXT_VERSION}`,
});
// Combine the content
const changelog = `
# ${process.env.NEXT_VERSION}
${documentText}
${releaseNotesResponse.data.body}
---
`
const existingChangelog = fs.existsSync('CHANGELOG.md') ? fs.readFileSync('CHANGELOG.md', 'utf8') : '';
fs.writeFileSync('CHANGELOG.md', changelog + existingChangelog, 'utf8');
core.setOutput('document_url', documentUrl);
- name: Create PR
id: create-pr
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
token: ${{ steps.generate-token.outputs.token }}
commit-message: 'chore: release ${{ steps.bump-type.outputs.next }}'
title: 'chore: release ${{ steps.bump-type.outputs.next }}'
body: 'Release notes: ${{ steps.outline.outputs.document_url }}'
labels: 'changelog:skip'
branch: 'release/next'
draft: true

View File

@@ -1,148 +0,0 @@
name: release.yml
on:
pull_request:
types: [closed]
paths:
- CHANGELOG.md
jobs:
# Maybe double check PR source branch?
merge_translations:
uses: ./.github/workflows/merge-translations.yml
permissions:
pull-requests: write
secrets:
PUSH_O_MATIC_APP_ID: ${{ secrets.PUSH_O_MATIC_APP_ID }}
PUSH_O_MATIC_APP_KEY: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
WEBLATE_TOKEN: ${{ secrets.WEBLATE_TOKEN }}
build_mobile:
uses: ./.github/workflows/build-mobile.yml
needs: merge_translations
permissions:
contents: read
secrets:
KEY_JKS: ${{ secrets.KEY_JKS }}
ALIAS: ${{ secrets.ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
# iOS secrets
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }}
IOS_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_SHARE_EXTENSION }}misc/release/notes.tmpl
IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
FASTLANE_TEAM_ID: ${{ secrets.FASTLANE_TEAM_ID }}
with:
ref: main
environment: production
prepare_release:
runs-on: ubuntu-latest
needs: build_mobile
permissions:
actions: read # To download the app artifact
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
token: ${{ steps.generate-token.outputs.token }}
persist-credentials: false
ref: main
- name: Extract changelog
id: changelog
run: |
CHANGELOG_PATH=$RUNNER_TEMP/changelog.md
sed -n '1,/^---$/p' CHANGELOG.md | head -n -1 > $CHANGELOG_PATH
echo "path=$CHANGELOG_PATH" >> $GITHUB_OUTPUT
VERSION=$(sed -n 's/^# //p' $CHANGELOG_PATH)
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Download APK
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-apk-signed
github-token: ${{ steps.generate-token.outputs.token }}
- name: Create draft release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
tag_name: ${{ steps.version.outputs.result }}
token: ${{ steps.generate-token.outputs.token }}
body_path: ${{ steps.changelog.outputs.path }}
draft: true
files: |
docker/docker-compose.yml
docker/example.env
docker/hwaccel.ml.yml
docker/hwaccel.transcoding.yml
docker/prometheus.yml
*.apk
- name: Rename Outline document
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
continue-on-error: true
env:
OUTLINE_API_KEY: ${{ secrets.OUTLINE_API_KEY }}
VERSION: ${{ steps.changelog.outputs.version }}
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const outlineKey = process.env.OUTLINE_API_KEY;
const version = process.env.VERSION;
const parentDocumentId = 'da856355-0844-43df-bd71-f8edce5382d9';
const baseUrl = 'https://outline.immich.cloud';
const listResponse = await fetch(`${baseUrl}/api/documents.list`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${outlineKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ parentDocumentId })
});
if (!listResponse.ok) {
throw new Error(`Outline list failed: ${listResponse.statusText}`);
}
const listData = await listResponse.json();
const allDocuments = listData.data || [];
const document = allDocuments.find(doc => doc.title === 'next');
if (document) {
console.log(`Found document 'next', renaming to '${version}'...`);
const updateResponse = await fetch(`${baseUrl}/api/documents.update`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${outlineKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: document.id,
title: version
})
});
if (!updateResponse.ok) {
throw new Error(`Failed to rename document: ${updateResponse.statusText}`);
}
} else {
console.log('No document titled "next" found to rename');
}

View File

@@ -19,21 +19,21 @@ jobs:
working-directory: ./open-api/typescript-sdk
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './open-api/typescript-sdk/.nvmrc'
registry-url: 'https://registry.npmjs.org'

View File

@@ -20,14 +20,14 @@ jobs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Check what should run
id: check
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
with:
github-token: ${{ steps.token.outputs.token }}
filters: |
@@ -49,19 +49,19 @@ jobs:
working-directory: ./mobile
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup Flutter SDK
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: 'stable'
flutter-version-file: ./mobile/pubspec.yaml
@@ -69,6 +69,14 @@ jobs:
- name: Install dependencies
run: dart pub get
- name: Install dependencies for UI package
run: dart pub get
working-directory: ./mobile/packages/ui
- name: Install dependencies for UI Showcase
run: dart pub get
working-directory: ./mobile/packages/ui/showcase
- name: Install DCM
uses: CQLabs/setup-dcm@8697ae0790c0852e964a6ef1d768d62a6675481a # v2.0.1
with:

View File

@@ -17,14 +17,14 @@ jobs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Check what should run
id: check
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
with:
github-token: ${{ steps.token.outputs.token }}
filters: |
@@ -63,21 +63,21 @@ jobs:
working-directory: ./server
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
@@ -108,20 +108,20 @@ jobs:
working-directory: ./cli
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './cli/.nvmrc'
cache: 'pnpm'
@@ -155,20 +155,20 @@ jobs:
working-directory: ./cli
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './cli/.nvmrc'
cache: 'pnpm'
@@ -197,20 +197,20 @@ jobs:
working-directory: ./web
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './web/.nvmrc'
cache: 'pnpm'
@@ -241,20 +241,20 @@ jobs:
working-directory: ./web
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './web/.nvmrc'
cache: 'pnpm'
@@ -279,20 +279,20 @@ jobs:
contents: read
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './web/.nvmrc'
cache: 'pnpm'
@@ -327,20 +327,20 @@ jobs:
working-directory: ./e2e
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './e2e/.nvmrc'
cache: 'pnpm'
@@ -373,21 +373,21 @@ jobs:
working-directory: ./server
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
submodules: 'recursive'
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
@@ -412,21 +412,21 @@ jobs:
runner: [ubuntu-latest, ubuntu-24.04-arm]
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
submodules: 'recursive'
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './e2e/.nvmrc'
cache: 'pnpm'
@@ -446,12 +446,29 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
if: ${{ !cancelled() }}
- name: Docker build
run: docker compose build
- name: Start Docker Compose
run: docker compose up -d --build --renew-anon-volumes --force-recreate --remove-orphans --wait --wait-timeout 300
if: ${{ !cancelled() }}
- name: Run e2e tests (api & cli)
env:
VITEST_DISABLE_DOCKER_SETUP: true
run: pnpm test
if: ${{ !cancelled() }}
- name: Run e2e tests (maintenance)
env:
VITEST_DISABLE_DOCKER_SETUP: true
run: pnpm test:maintenance
if: ${{ !cancelled() }}
- name: Capture Docker logs
if: always()
run: docker compose logs --no-color > docker-compose-logs.txt
working-directory: ./e2e
- name: Archive Docker logs
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: e2e-server-docker-logs-${{ matrix.runner }}
path: e2e/docker-compose-logs.txt
e2e-tests-web:
name: End-to-End Tests (Web)
needs: pre-job
@@ -467,21 +484,21 @@ jobs:
runner: [ubuntu-latest, ubuntu-24.04-arm]
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
submodules: 'recursive'
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './e2e/.nvmrc'
cache: 'pnpm'
@@ -494,33 +511,54 @@ jobs:
run: pnpm install --frozen-lockfile
if: ${{ !cancelled() }}
- name: Install Playwright Browsers
run: npx playwright install chromium --only-shell
run: pnpm exec playwright install chromium --only-shell
if: ${{ !cancelled() }}
- name: Docker build
run: docker compose build
run: docker compose up -d --build --renew-anon-volumes --force-recreate --remove-orphans --wait --wait-timeout 300
if: ${{ !cancelled() }}
- name: Run e2e tests (web)
env:
CI: true
run: npx playwright test --project=chromium
PLAYWRIGHT_DISABLE_WEBSERVER: true
run: pnpm test:web
if: ${{ !cancelled() }}
- name: Archive web results
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- name: Archive e2e test (web) results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: success() || failure()
with:
name: e2e-web-test-results-${{ matrix.runner }}
path: e2e/playwright-report/
- name: Run ui tests (web)
env:
CI: true
run: npx playwright test --project=ui
PLAYWRIGHT_DISABLE_WEBSERVER: true
run: pnpm test:web:ui
if: ${{ !cancelled() }}
- name: Archive ui results
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- name: Archive ui test (web) results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: success() || failure()
with:
name: e2e-ui-test-results-${{ matrix.runner }}
path: e2e/playwright-report/
- name: Run maintenance tests
env:
PLAYWRIGHT_DISABLE_WEBSERVER: true
run: pnpm test:web:maintenance
if: ${{ !cancelled() }}
- name: Archive maintenance tests (web) results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: success() || failure()
with:
name: e2e-maintenance-isolated-test-results-${{ matrix.runner }}
path: e2e/playwright-report/
- name: Capture Docker logs
if: always()
run: docker compose logs --no-color > docker-compose-logs.txt
working-directory: ./e2e
- name: Archive Docker logs
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: e2e-web-docker-logs-${{ matrix.runner }}
path: e2e/docker-compose-logs.txt
success-check-e2e:
name: End-to-End Tests Success
needs: [e2e-tests-server-cli, e2e-tests-web]
@@ -528,7 +566,7 @@ jobs:
runs-on: ubuntu-latest
if: always()
steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
with:
needs: ${{ toJSON(needs) }}
mobile-unit-tests:
@@ -540,17 +578,17 @@ jobs:
contents: read
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup Flutter SDK
uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: 'stable'
flutter-version-file: ./mobile/pubspec.yaml
@@ -572,17 +610,17 @@ jobs:
working-directory: ./machine-learning
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Install uv
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
python-version: 3.11
- name: Install dependencies
@@ -591,9 +629,9 @@ jobs:
- name: Lint with ruff
run: |
uv run ruff check --output-format=github immich_ml
- name: Check black formatting
- name: Format with ruff
run: |
uv run black --check immich_ml
uv run ruff format --check immich_ml
- name: Run mypy type checking
run: |
uv run mypy --strict immich_ml/
@@ -612,20 +650,20 @@ jobs:
working-directory: ./.github
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './.github/.nvmrc'
cache: 'pnpm'
@@ -642,12 +680,12 @@ jobs:
contents: read
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
@@ -663,20 +701,20 @@ jobs:
contents: read
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'
@@ -725,20 +763,20 @@ jobs:
working-directory: ./server
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- name: Setup Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: './server/.nvmrc'
cache: 'pnpm'

View File

@@ -24,14 +24,14 @@ jobs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
- name: Check what should run
id: check
uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
uses: immich-app/devtools/actions/pre-job@f50e3b600b6ac1763ddb8f3dfc69093512b967a1 # pre-job-action-v2.0.3
with:
github-token: ${{ steps.token.outputs.token }}
filters: |
@@ -47,7 +47,7 @@ jobs:
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
steps:
- id: token
uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
uses: immich-app/devtools/actions/create-workflow-token@57ff6ebfd507b045514442683ff06ff1b2f6efbd # create-workflow-token-action-v1.0.2
with:
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
@@ -68,6 +68,6 @@ jobs:
permissions: {}
if: always()
steps:
- uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4
- uses: immich-app/devtools/actions/success-check@53bb77345ee9f953f93bd6fd9980f07a2f24965e # success-check-action-v0.0.5
with:
needs: ${{ toJSON(needs) }}

2
.gitignore vendored
View File

@@ -28,3 +28,5 @@ vite.config.js.timestamp-*
.pnpm-store
.devcontainer/library
.devcontainer/.env*
*.tsbuildinfo
*.tsbuildInfo

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "mobile/.isar"]
path = mobile/.isar
url = https://github.com/isar/isar
[submodule "e2e/test-assets"]
path = e2e/test-assets
url = https://github.com/immich-app/test-assets

View File

@@ -4,12 +4,18 @@ module.exports = {
if (!pkg.name) {
return pkg;
}
// make exiftool-vendored.pl a regular dependency since Docker prod
// images build with --no-optional to reduce image size
if (pkg.name === "exiftool-vendored") {
if (pkg.optionalDependencies["exiftool-vendored.pl"]) {
// make exiftool-vendored.pl a regular dependency
pkg.dependencies["exiftool-vendored.pl"] =
pkg.optionalDependencies["exiftool-vendored.pl"];
delete pkg.optionalDependencies["exiftool-vendored.pl"];
const binaryPackage =
process.platform === "win32"
? "exiftool-vendored.exe"
: "exiftool-vendored.pl";
if (pkg.optionalDependencies[binaryPackage]) {
pkg.dependencies[binaryPackage] =
pkg.optionalDependencies[binaryPackage];
delete pkg.optionalDependencies[binaryPackage];
}
}
return pkg;

View File

@@ -5,6 +5,13 @@
"dbaeumer.vscode-eslint",
"dart-code.flutter",
"dart-code.dart-code",
"dcmdev.dcm-vscode-extension"
"dcmdev.dcm-vscode-extension",
"bradlc.vscode-tailwindcss",
"ms-playwright.playwright",
"vitest.explorer",
"editorconfig.editorconfig",
"foxundermoon.shell-format",
"timonwong.shellcheck",
"bluebrown.yamlfmt"
]
}

60
.vscode/settings.json vendored
View File

@@ -1,8 +1,7 @@
{
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
"editor.formatOnSave": true
},
"[dart]": {
"editor.defaultFormatter": "Dart-Code.dart-code",
@@ -14,51 +13,62 @@
"editor.wordBasedSuggestions": "off"
},
"[javascript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
"editor.formatOnSave": true
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
"editor.formatOnSave": true
},
"[svelte]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
"editor.formatOnSave": true
},
"[typescript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.tabSize": 2
"editor.formatOnSave": true
},
"cSpell.words": ["immich"],
"css.lint.unknownAtRules": "ignore",
"editor.bracketPairColorization.enabled": true,
"editor.formatOnSave": true,
"eslint.useFlatConfig": true,
"eslint.validate": ["javascript", "typescript", "svelte"],
"eslint.workingDirectories": [
{ "directory": "cli", "changeProcessCWD": true },
{ "directory": "e2e", "changeProcessCWD": true },
{ "directory": "server", "changeProcessCWD": true },
{ "directory": "web", "changeProcessCWD": true }
],
"files.watcherExclude": {
"**/.jj/**": true,
"**/.git/**": true,
"**/node_modules/**": true,
"**/build/**": true,
"**/dist/**": true,
"**/.svelte-kit/**": true
},
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart",
"*.ts": "${capture}.spec.ts,${capture}.mock.ts",
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs"
},
"search.exclude": {
"**/node_modules": true,
"**/build": true,
"**/dist": true,
"**/.svelte-kit": true,
"**/open-api/typescript-sdk/src": true
},
"svelte.enable-ts-plugin": true,
"typescript.preferences.importModuleSpecifier": "non-relative"
"tailwindCSS.experimental.configFile": {
"web/src/app.css": "web/src/**"
},
"js/ts.preferences.importModuleSpecifier": "non-relative",
"vitest.maximumConfigs": 10
}

80
.vscode/tasks.json vendored
View File

@@ -1,80 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Fix Permissions, Install Dependencies",
"type": "shell",
"command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0",
"isBackground": true,
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": true,
"clear": false,
"group": "Devcontainer tasks",
"close": true
},
"runOptions": {
"runOn": "default"
},
"problemMatcher": []
},
{
"label": "Immich API Server (Nest)",
"dependsOn": ["Fix Permissions, Install Dependencies"],
"type": "shell",
"command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0",
"isBackground": true,
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": true,
"clear": false,
"group": "Devcontainer tasks",
"close": true
},
"runOptions": {
"runOn": "default"
},
"problemMatcher": []
},
{
"label": "Immich Web Server (Vite)",
"dependsOn": ["Fix Permissions, Install Dependencies"],
"type": "shell",
"command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0",
"isBackground": true,
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "dedicated",
"showReuseMessage": true,
"clear": false,
"group": "Devcontainer tasks",
"close": true
},
"runOptions": {
"runOn": "default"
},
"problemMatcher": []
},
{
"label": "Immich Server and Web",
"dependsOn": ["Immich Web Server (Vite)", "Immich API Server (Nest)"],
"runOptions": {
"runOn": "folderOpen"
},
"problemMatcher": []
},
{
"label": "Build Immich CLI",
"type": "shell",
"command": "pnpm --filter cli build:dev"
}
]
}

View File

@@ -15,17 +15,31 @@ Please try to keep pull requests as focused as possible. A PR should do exactly
If you are looking for something to work on, there are discussions and issues with a `good-first-issue` label on them. These are always a good starting point. If none of them sound interesting or fit your skill set, feel free to reach out on our Discord. We're happy to help you find something to work on!
We usually do not assign issues to new contributors, since it happens often that a PR is never even opened. Again, reach out on Discord if you fear putting a lot of time into fixing an issue, but ending up with a duplicate PR.
## Use of generative AI
We generally discourage PRs entirely generated by an LLM. For any part generated by an LLM, please put extra effort into your self-review. By using generative AI without proper self-review, the time you save ends up being more work we need to put in for proper reviews and code cleanup. Please keep that in mind when submitting code by an LLM. Clearly state the use of LLMs/(generative) AI in your pull request as requested by the template.
We ask you not to open PRs generated with an LLM. We find that code generated like this tends to need a large amount of back-and-forth, which is a very inefficient use of our time. If we want LLM-generated code, it's much faster for us to use an LLM ourselves than to go through an intermediary via a pull request.
## Feature freezes
From time to time, we put a feature freeze on parts of the codebase. For us, this means we won't accept most PRs that make changes in that area. Exempted from this are simple bug fixes that require only minor changes. We will close feature PRs that target a feature-frozen area, even if that feature is highly requested and you put a lot of work into it. Please keep that in mind, and if you're ever uncertain if a PR would be accepted, reach out to us first (e.g., in the aforementioned `#contributing` channel). We hate to throw away work. Currently, we have feature freezes on:
* Sharing/Asset ownership
* (External) libraries
- Sharing/Asset ownership
- (External) libraries
## Non-code contributions
If you want to contribute to Immich but you don't feel comfortable programming in our tech stack, there are other ways you can help the team. All our translations are done through [Weblate](https://hosted.weblate.org/projects/immich). These rely entirely on the community; if you speak a language that isn't fully translated yet, submitting translations there is greatly appreciated! If you like helping others, answering Q&A discussions here on GitHub and replying to people on our Discord is also always appreciated.
If you want to contribute to Immich but you don't feel comfortable programming in our tech stack, there are other ways you can help the team.
### Translations
All our translations are done through [Weblate](https://hosted.weblate.org/projects/immich). These rely entirely on the community; if you speak a language that isn't fully translated yet, submitting translations there is greatly appreciated!
### Datasets
Help us improve our [Immich Datasets](https://datasets.immich.app) by submitting photos and videos taken from a variety of devices, including smartphones, DSLRs, and action cameras, as well as photos with unique features, such as panoramas, burst photos, and photo spheres. These datasets will be publically available for anyone to use, do not submit private/sensitive photos.
### Community support
If you like helping others, answering Q&A discussions here on GitHub and replying to people on our Discord is also always appreciated.

View File

@@ -52,7 +52,7 @@ attach-server:
docker exec -it docker_immich-server_1 sh
renovate:
LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset
LOG_LEVEL=debug pnpm exec renovate --platform=local --repository-cache=reset
# Directories that need to be created for volumes or build output
VOLUME_DIRS = \

View File

@@ -1 +1 @@
24.13.0
24.15.0

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.5.2",
"version": "2.7.5",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",
@@ -13,31 +13,30 @@
"cli"
],
"devDependencies": {
"@eslint/js": "^9.8.0",
"@immich/sdk": "file:../open-api/typescript-sdk",
"@eslint/js": "^10.0.0",
"@immich/sdk": "workspace:*",
"@types/byte-size": "^8.1.0",
"@types/cli-progress": "^3.11.0",
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1",
"@types/node": "^24.10.9",
"@vitest/coverage-v8": "^3.0.0",
"@types/node": "^24.12.2",
"@vitest/coverage-v8": "^4.0.0",
"byte-size": "^9.0.0",
"cli-progress": "^3.12.0",
"commander": "^12.0.0",
"eslint": "^9.14.0",
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^62.0.0",
"globals": "^16.0.0",
"eslint-plugin-unicorn": "^64.0.0",
"globals": "^17.0.0",
"mock-fs": "^5.2.0",
"prettier": "^3.7.4",
"prettier-plugin-organize-imports": "^4.0.0",
"typescript": "^5.3.3",
"typescript-eslint": "^8.28.0",
"vite": "^7.0.0",
"vite-tsconfig-paths": "^6.0.0",
"vitest": "^3.0.0",
"typescript": "^6.0.0",
"typescript-eslint": "^8.58.0",
"vite": "^8.0.0",
"vitest": "^4.0.0",
"vitest-fetch-mock": "^0.4.0",
"yaml": "^2.3.1"
},
@@ -45,12 +44,12 @@
"build": "vite build",
"build:dev": "vite build --sourcemap true",
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
"lint:fix": "npm run lint -- --fix",
"prepack": "npm run build",
"lint:fix": "pnpm run lint --fix",
"prepack": "pnpm run build",
"test": "vitest",
"test:cov": "vitest --coverage",
"format": "prettier --check .",
"format:fix": "prettier --write .",
"format": "prettier --cache --check .",
"format:fix": "prettier --cache --write --list-different .",
"check": "tsc --noEmit"
},
"repository": {
@@ -69,6 +68,6 @@
"micromatch": "^4.0.8"
},
"volta": {
"node": "24.13.0"
"node": "24.15.0"
}
}

View File

@@ -1,13 +1,21 @@
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { setTimeout as sleep } from 'node:timers/promises';
import { describe, expect, it, MockedFunction, vi } from 'vitest';
import { Action, checkBulkUpload, defaults, getSupportedMediaTypes, Reason } from '@immich/sdk';
import { AssetRejectReason, AssetUploadAction, checkBulkUpload, defaults, getSupportedMediaTypes } from '@immich/sdk';
import createFetchMock from 'vitest-fetch-mock';
import { checkForDuplicates, getAlbumName, startWatch, uploadFiles, UploadOptionsDto } from 'src/commands/asset';
import {
checkForDuplicates,
deleteFiles,
findSidecar,
getAlbumName,
startWatch,
uploadFiles,
UploadOptionsDto,
} from 'src/commands/asset';
vi.mock('@immich/sdk');
@@ -50,7 +58,7 @@ describe('uploadFiles', () => {
});
it('returns new assets when upload file is successful', async () => {
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), () => {
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), function () {
return {
status: 200,
body: JSON.stringify({ id: 'fc5621b1-86f6-44a1-9905-403e607df9f5', status: 'created' }),
@@ -67,7 +75,7 @@ describe('uploadFiles', () => {
it('returns new assets when upload file retry is successful', async () => {
let counter = 0;
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), () => {
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), function () {
counter++;
if (counter < retry) {
throw new Error('Network error');
@@ -88,7 +96,7 @@ describe('uploadFiles', () => {
});
it('returns new assets when upload file retry is failed', async () => {
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), () => {
fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), function () {
throw new Error('Network error');
});
@@ -112,7 +120,7 @@ describe('checkForDuplicates', () => {
vi.mocked(checkBulkUpload).mockResolvedValue({
results: [
{
action: Action.Accept,
action: AssetUploadAction.Accept,
id: testFilePath,
},
],
@@ -136,10 +144,10 @@ describe('checkForDuplicates', () => {
vi.mocked(checkBulkUpload).mockResolvedValue({
results: [
{
action: Action.Reject,
action: AssetUploadAction.Reject,
id: testFilePath,
assetId: 'fc5621b1-86f6-44a1-9905-403e607df9f5',
reason: Reason.Duplicate,
reason: AssetRejectReason.Duplicate,
},
],
});
@@ -159,7 +167,7 @@ describe('checkForDuplicates', () => {
vi.mocked(checkBulkUpload).mockResolvedValue({
results: [
{
action: Action.Accept,
action: AssetUploadAction.Accept,
id: testFilePath,
},
],
@@ -179,7 +187,7 @@ describe('checkForDuplicates', () => {
mocked.mockResolvedValue({
results: [
{
action: Action.Accept,
action: AssetUploadAction.Accept,
id: testFilePath,
},
],
@@ -228,16 +236,19 @@ describe('startWatch', () => {
await sleep(100); // to debounce the watcher from considering the test file as a existing file
await fs.promises.writeFile(testFilePath, 'testjpg');
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
expect(checkBulkUpload).toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
assets: [
expect.objectContaining({
id: testFilePath,
}),
],
},
});
await vi.waitFor(
() =>
expect(checkBulkUpload).toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
assets: [
expect.objectContaining({
id: testFilePath,
}),
],
},
}),
{ timeout: 5000 },
);
});
it('should filter out unsupported files', async () => {
@@ -249,16 +260,19 @@ describe('startWatch', () => {
await fs.promises.writeFile(testFilePath, 'testjpg');
await fs.promises.writeFile(unsupportedFilePath, 'testtxt');
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
expect(checkBulkUpload).toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
assets: expect.arrayContaining([
expect.objectContaining({
id: testFilePath,
}),
]),
},
});
await vi.waitFor(
() =>
expect(checkBulkUpload).toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
assets: expect.arrayContaining([
expect.objectContaining({
id: testFilePath,
}),
]),
},
}),
{ timeout: 5000 },
);
expect(checkBulkUpload).not.toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
@@ -283,16 +297,19 @@ describe('startWatch', () => {
await fs.promises.writeFile(testFilePath, 'testjpg');
await fs.promises.writeFile(ignoredFilePath, 'ignoredjpg');
await vi.waitUntil(() => checkBulkUploadMocked.mock.calls.length > 0, 3000);
expect(checkBulkUpload).toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
assets: expect.arrayContaining([
expect.objectContaining({
id: testFilePath,
}),
]),
},
});
await vi.waitFor(
() =>
expect(checkBulkUpload).toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
assets: expect.arrayContaining([
expect.objectContaining({
id: testFilePath,
}),
]),
},
}),
{ timeout: 5000 },
);
expect(checkBulkUpload).not.toHaveBeenCalledWith({
assetBulkUploadCheckDto: {
@@ -309,3 +326,85 @@ describe('startWatch', () => {
await fs.promises.rm(testFolder, { recursive: true, force: true });
});
});
describe('findSidecar', () => {
let testDir: string;
let testFilePath: string;
beforeEach(() => {
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-sidecar-'));
testFilePath = path.join(testDir, 'test.jpg');
fs.writeFileSync(testFilePath, 'test');
});
afterEach(() => {
fs.rmSync(testDir, { recursive: true, force: true });
});
it('should find sidecar file with photo.xmp naming convention', () => {
const sidecarPath = path.join(testDir, 'test.xmp');
fs.writeFileSync(sidecarPath, 'xmp data');
const result = findSidecar(testFilePath);
expect(result).toBe(sidecarPath);
});
it('should find sidecar file with photo.ext.xmp naming convention', () => {
const sidecarPath = path.join(testDir, 'test.jpg.xmp');
fs.writeFileSync(sidecarPath, 'xmp data');
const result = findSidecar(testFilePath);
expect(result).toBe(sidecarPath);
});
it('should prefer photo.ext.xmp over photo.xmp when both exist', () => {
const sidecarPath1 = path.join(testDir, 'test.xmp');
const sidecarPath2 = path.join(testDir, 'test.jpg.xmp');
fs.writeFileSync(sidecarPath1, 'xmp data 1');
fs.writeFileSync(sidecarPath2, 'xmp data 2');
const result = findSidecar(testFilePath);
// Should return the first one found (photo.xmp) based on the order in the code
expect(result).toBe(sidecarPath1);
});
it('should return undefined when no sidecar file exists', () => {
const result = findSidecar(testFilePath);
expect(result).toBeUndefined();
});
});
describe('deleteFiles', () => {
let testDir: string;
let testFilePath: string;
beforeEach(() => {
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-delete-'));
testFilePath = path.join(testDir, 'test.jpg');
fs.writeFileSync(testFilePath, 'test');
});
afterEach(() => {
fs.rmSync(testDir, { recursive: true, force: true });
});
it('should delete asset and sidecar file when main file is deleted', async () => {
const sidecarPath = path.join(testDir, 'test.xmp');
fs.writeFileSync(sidecarPath, 'xmp data');
await deleteFiles([{ id: 'test-id', filepath: testFilePath }], [], { delete: true, concurrency: 1 });
expect(fs.existsSync(testFilePath)).toBe(false);
expect(fs.existsSync(sidecarPath)).toBe(false);
});
it('should not delete sidecar file when delete option is false', async () => {
const sidecarPath = path.join(testDir, 'test.xmp');
fs.writeFileSync(sidecarPath, 'xmp data');
await deleteFiles([{ id: 'test-id', filepath: testFilePath }], [], { delete: false, concurrency: 1 });
expect(fs.existsSync(testFilePath)).toBe(true);
expect(fs.existsSync(sidecarPath)).toBe(true);
});
});

View File

@@ -1,9 +1,10 @@
import {
Action,
AssetBulkUploadCheckItem,
AssetBulkUploadCheckResult,
AssetMediaResponseDto,
AssetMediaStatus,
AssetUploadAction,
Permission,
addAssetsToAlbum,
checkBulkUpload,
createAlbum,
@@ -16,17 +17,15 @@ import { Matcher, watch as watchFs } from 'chokidar';
import { MultiBar, Presets, SingleBar } from 'cli-progress';
import { chunk } from 'lodash-es';
import micromatch from 'micromatch';
import { Stats, createReadStream } from 'node:fs';
import { Stats, createReadStream, existsSync } from 'node:fs';
import { stat, unlink } from 'node:fs/promises';
import path, { basename } from 'node:path';
import { Queue } from 'src/queue';
import { BaseOptions, Batcher, authenticate, crawl, sha1 } from 'src/utils';
import { BaseOptions, Batcher, authenticate, crawl, requirePermissions, s, sha1 } from 'src/utils';
const UPLOAD_WATCH_BATCH_SIZE = 100;
const UPLOAD_WATCH_DEBOUNCE_TIME_MS = 10_000;
const s = (count: number) => (count === 1 ? '' : 's');
// TODO figure out why `id` is missing
type AssetBulkUploadCheckResults = Array<AssetBulkUploadCheckResult & { id: string }>;
type Asset = { id: string; filepath: string };
@@ -136,6 +135,7 @@ export const startWatch = async (
export const upload = async (paths: string[], baseOptions: BaseOptions, options: UploadOptionsDto) => {
await authenticate(baseOptions);
await requirePermissions([Permission.AssetUpload]);
const scanFiles = await scan(paths, options);
@@ -180,18 +180,49 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
}
let multiBar: MultiBar | undefined;
let totalSize = 0;
const statsMap = new Map<string, Stats>();
// Calculate total size first
for (const filepath of files) {
const stats = await stat(filepath);
statsMap.set(filepath, stats);
totalSize += stats.size;
}
if (progress) {
multiBar = new MultiBar(
{ format: '{message} | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' },
{
format: '{message} | {bar} | {percentage}% | ETA: {eta_formatted} | {value}/{total}',
formatValue: (v: number, options, type) => {
// Don't format percentage
if (type === 'percentage') {
return v.toString();
}
return byteSize(v).toString();
},
etaBuffer: 100, // Increase samples for ETA calculation
},
Presets.shades_classic,
);
// Ensure we restore cursor on interrupt
process.on('SIGINT', () => {
if (multiBar) {
multiBar.stop();
}
process.exit(0);
});
} else {
console.log(`Received ${files.length} files, hashing...`);
console.log(`Received ${files.length} files (${byteSize(totalSize)}), hashing...`);
}
const hashProgressBar = multiBar?.create(files.length, 0, { message: 'Hashing files ' });
const checkProgressBar = multiBar?.create(files.length, 0, { message: 'Checking for duplicates' });
const hashProgressBar = multiBar?.create(totalSize, 0, {
message: 'Hashing files ',
});
const checkProgressBar = multiBar?.create(totalSize, 0, {
message: 'Checking for duplicates',
});
const newFiles: string[] = [];
const duplicates: Asset[] = [];
@@ -203,7 +234,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
const results = response.results as AssetBulkUploadCheckResults;
for (const { id: filepath, assetId, action } of results) {
if (action === Action.Accept) {
if (action === AssetUploadAction.Accept) {
newFiles.push(filepath);
} else {
// rejects are always duplicates
@@ -211,7 +242,13 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
}
}
checkProgressBar?.increment(assets.length);
// Update progress based on total size of processed files
let processedSize = 0;
for (const asset of assets) {
const stats = statsMap.get(asset.id);
processedSize += stats?.size || 0;
}
checkProgressBar?.increment(processedSize);
},
{ concurrency, retry: 3 },
);
@@ -221,6 +258,10 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
const queue = new Queue<string, AssetBulkUploadCheckItem[]>(
async (filepath: string): Promise<AssetBulkUploadCheckItem[]> => {
const stats = statsMap.get(filepath);
if (!stats) {
throw new Error(`Stats not found for ${filepath}`);
}
const dto = { id: filepath, checksum: await sha1(filepath) };
results.push(dto);
@@ -231,7 +272,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
void checkBulkUploadQueue.push(batch);
}
hashProgressBar?.increment();
hashProgressBar?.increment(stats.size);
return results;
},
{ concurrency, retry: 3 },
@@ -362,34 +403,22 @@ export const uploadFiles = async (
const uploadFile = async (input: string, stats: Stats): Promise<AssetMediaResponseDto> => {
const { baseUrl, headers } = defaults;
const assetPath = path.parse(input);
const noExtension = path.join(assetPath.dir, assetPath.name);
const sidecarsFiles = await Promise.all(
// XMP sidecars can come in two filename formats. For a photo named photo.ext, the filenames are photo.ext.xmp and photo.xmp
[`${noExtension}.xmp`, `${input}.xmp`].map(async (sidecarPath) => {
try {
const stats = await stat(sidecarPath);
return new UploadFile(sidecarPath, stats.size);
} catch {
return false;
}
}),
);
const sidecarData = sidecarsFiles.find((file): file is UploadFile => file !== false);
const formData = new FormData();
formData.append('deviceAssetId', `${basename(input)}-${stats.size}`.replaceAll(/\s+/g, ''));
formData.append('deviceId', 'CLI');
formData.append('fileCreatedAt', stats.mtime.toISOString());
formData.append('fileModifiedAt', stats.mtime.toISOString());
formData.append('fileSize', String(stats.size));
formData.append('isFavorite', 'false');
formData.append('assetData', new UploadFile(input, stats.size));
if (sidecarData) {
formData.append('sidecarData', sidecarData);
const sidecarPath = findSidecar(input);
if (sidecarPath) {
try {
const stats = await stat(sidecarPath);
const sidecarData = new UploadFile(sidecarPath, stats.size);
formData.append('sidecarData', sidecarData);
} catch {
// noop
}
}
const response = await fetch(`${baseUrl}/assets`, {
@@ -405,7 +434,19 @@ const uploadFile = async (input: string, stats: Stats): Promise<AssetMediaRespon
return response.json();
};
const deleteFiles = async (uploaded: Asset[], duplicates: Asset[], options: UploadOptionsDto): Promise<void> => {
export const findSidecar = (filepath: string): string | undefined => {
const assetPath = path.parse(filepath);
const noExtension = path.join(assetPath.dir, assetPath.name);
// XMP sidecars can come in two filename formats. For a photo named photo.ext, the filenames are photo.ext.xmp and photo.xmp
for (const sidecarPath of [`${noExtension}.xmp`, `${filepath}.xmp`]) {
if (existsSync(sidecarPath)) {
return sidecarPath;
}
}
};
export const deleteFiles = async (uploaded: Asset[], duplicates: Asset[], options: UploadOptionsDto): Promise<void> => {
let fileCount = 0;
if (options.delete) {
fileCount += uploaded.length;
@@ -433,7 +474,15 @@ const deleteFiles = async (uploaded: Asset[], duplicates: Asset[], options: Uplo
const chunkDelete = async (files: Asset[]) => {
for (const assetBatch of chunk(files, options.concurrency)) {
await Promise.all(assetBatch.map((input: Asset) => unlink(input.filepath)));
await Promise.all(
assetBatch.map(async (input: Asset) => {
await unlink(input.filepath);
const sidecarPath = findSidecar(input.filepath);
if (sidecarPath) {
await unlink(sidecarPath);
}
}),
);
deletionProgress.update(assetBatch.length);
}
};

View File

@@ -1,7 +1,15 @@
import { getMyUser } from '@immich/sdk';
import { getMyUser, Permission } from '@immich/sdk';
import { existsSync } from 'node:fs';
import { mkdir, unlink } from 'node:fs/promises';
import { BaseOptions, connect, getAuthFilePath, logError, withError, writeAuthFile } from 'src/utils';
import {
BaseOptions,
connect,
getAuthFilePath,
logError,
requirePermissions,
withError,
writeAuthFile,
} from 'src/utils';
export const login = async (url: string, key: string, options: BaseOptions) => {
console.log(`Logging in to ${url}`);
@@ -9,6 +17,7 @@ export const login = async (url: string, key: string, options: BaseOptions) => {
const { configDirectory: configDir } = options;
await connect(url, key);
await requirePermissions([Permission.UserRead]);
const [error, user] = await withError(getMyUser());
if (error) {

View File

@@ -1,8 +1,9 @@
import { getAssetStatistics, getMyUser, getServerVersion, getSupportedMediaTypes } from '@immich/sdk';
import { BaseOptions, authenticate } from 'src/utils';
import { getAssetStatistics, getMyUser, getServerVersion, getSupportedMediaTypes, Permission } from '@immich/sdk';
import { authenticate, BaseOptions, requirePermissions } from 'src/utils';
export const serverInfo = async (options: BaseOptions) => {
const { url } = await authenticate(options);
await requirePermissions([Permission.ServerAbout, Permission.AssetStatistics, Permission.UserRead]);
const [versionInfo, mediaTypes, stats, userInfo] = await Promise.all([
getServerVersion(),

View File

@@ -1,4 +1,4 @@
import { getMyUser, init, isHttpError } from '@immich/sdk';
import { ApiKeyResponseDto, getMyApiKey, getMyUser, init, isHttpError, Permission } from '@immich/sdk';
import { convertPathToPattern, glob } from 'fast-glob';
import { createHash } from 'node:crypto';
import { createReadStream } from 'node:fs';
@@ -34,6 +34,36 @@ export const authenticate = async (options: BaseOptions): Promise<AuthDto> => {
return auth;
};
export const s = (count: number) => (count === 1 ? '' : 's');
let _apiKey: ApiKeyResponseDto;
export const requirePermissions = async (permissions: Permission[]) => {
if (!_apiKey) {
_apiKey = await getMyApiKey();
}
if (_apiKey.permissions.includes(Permission.All)) {
return;
}
const missing: Permission[] = [];
for (const permission of permissions) {
if (!_apiKey.permissions.includes(permission)) {
missing.push(permission);
}
}
if (missing.length > 0) {
const combined = missing.map((permission) => `"${permission}"`).join(', ');
console.log(
`Missing required permission${s(missing.length)}: ${combined}.
Please make sure your API key has the correct permissions.`,
);
process.exit(1);
}
};
export const connect = async (url: string, key: string) => {
const wellKnownUrl = new URL('.well-known/immich', url);
try {
@@ -51,7 +81,7 @@ export const connect = async (url: string, key: string) => {
const [error] = await withError(getMyUser());
if (isHttpError(error)) {
logError(error, 'Failed to connect to server');
logError(error, `Failed to connect to server ${url}`);
process.exit(1);
}

View File

@@ -15,8 +15,12 @@
"incremental": true,
"skipLibCheck": true,
"esModuleInterop": true,
"baseUrl": "./",
"rootDir": "./src",
"paths": {
"src/*": ["./src/*"],
},
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo",
"types": ["vitest/globals"]
},
"exclude": ["dist", "node_modules"]
"exclude": ["dist", "node_modules", "vite.config.ts"]
}

View File

@@ -1,10 +1,12 @@
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig, UserConfig } from 'vite';
export default defineConfig({
resolve: { alias: { src: '/src' } },
resolve: {
alias: { src: '/src' },
tsconfigPaths: true,
},
build: {
rollupOptions: {
rolldownOptions: {
input: 'src/index.ts',
output: {
dir: 'dist',
@@ -16,5 +18,8 @@ export default defineConfig({
// bundle everything except for Node built-ins
noExternal: /^(?!node:).*$/,
},
plugins: [tsconfigPaths()],
});
test: {
name: 'cli:unit',
globals: true,
},
} as UserConfig);

View File

@@ -1,7 +0,0 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
},
});

View File

@@ -1,6 +1,6 @@
[tools]
terragrunt = "0.98.0"
opentofu = "1.11.4"
terragrunt = "1.0.1"
opentofu = "1.11.6"
[tasks."tg:fmt"]
run = "terragrunt hclfmt"

View File

@@ -14,33 +14,66 @@
name: immich-dev
services:
immich-app-base:
profiles: ['_base']
tmpfs:
- /tmp
volumes:
- ..:/usr/src/app
# - ../../ui:/usr/src/ui
- pnpm_cache:/buildcache/pnpm_cache
- server_node_modules:/usr/src/app/server/node_modules
- web_node_modules:/usr/src/app/web/node_modules
- github_node_modules:/usr/src/app/.github/node_modules
- cli_node_modules:/usr/src/app/cli/node_modules
- docs_node_modules:/usr/src/app/docs/node_modules
- e2e_node_modules:/usr/src/app/e2e/node_modules
- sdk_node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app_node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
immich-init:
extends:
service: immich-app-base
profiles: !reset []
container_name: immich_init
image: immich-server-dev:latest
build:
context: ../
dockerfile: server/Dockerfile.dev
target: dev
command:
- |
pnpm install
touch /tmp/init-complete
exec tail -f /dev/null
volumes:
- pnpm_store_server:/buildcache/pnpm-store
restart: 'no'
healthcheck:
test: ['CMD', 'test', '-f', '/tmp/init-complete']
interval: 2s
timeout: 3s
retries: 300
start_period: 300s
immich-server:
extends:
service: immich-app-base
profiles: !reset []
container_name: immich_server
command: ['immich-dev']
image: immich-server-dev:latest
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
build:
context: ../
dockerfile: server/Dockerfile.dev
target: dev
restart: unless-stopped
volumes:
- ..:/usr/src/app
- ${UPLOAD_LOCATION}/photos:/data
- /etc/localtime:/etc/localtime:ro
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
- pnpm_store_server:/buildcache/pnpm-store
- ../plugins:/build/corePlugin
env_file:
- .env
@@ -58,11 +91,14 @@ services:
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://docs.immich.app
IMMICH_THIRD_PARTY_SUPPORT_URL: https://docs.immich.app/community-guides
IMMICH_HELMET_FILE: 'true'
ports:
- 9230:9230
- 9231:9231
- 2283:2283
depends_on:
immich-init:
condition: service_healthy
redis:
condition: service_started
database:
@@ -71,6 +107,9 @@ services:
disable: false
immich-web:
extends:
service: immich-app-base
profiles: !reset []
container_name: immich_web
image: immich-web-dev:latest
build:
@@ -84,20 +123,11 @@ services:
- 3000:3000
- 24678:24678
volumes:
- ..:/usr/src/app
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
- pnpm_store_web:/buildcache/pnpm-store
restart: unless-stopped
depends_on:
immich-init:
condition: service_healthy
immich-server:
condition: service_started
@@ -116,7 +146,7 @@ services:
- 3003:3003
volumes:
- ../machine-learning/immich_ml:/usr/src/immich_ml
- model-cache:/cache
- model_cache:/cache
env_file:
- .env
depends_on:
@@ -127,7 +157,7 @@ services:
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
healthcheck:
test: redis-cli ping || exit 1
@@ -156,7 +186,7 @@ services:
# image: prom/prometheus
# volumes:
# - ./prometheus.yml:/etc/prometheus/prometheus.yml
# - prometheus-data:/prometheus
# - prometheus_data:/prometheus
# first login uses admin/admin
# add data source for http://immich-prometheus:9090 to get started
@@ -167,20 +197,22 @@ services:
# - 3000:3000
# image: grafana/grafana:10.3.3-ubuntu
# volumes:
# - grafana-data:/var/lib/grafana
# - grafana_data:/var/lib/grafana
volumes:
model-cache:
prometheus-data:
grafana-data:
pnpm-store:
server-node_modules:
web-node_modules:
github-node_modules:
cli-node_modules:
docs-node_modules:
e2e-node_modules:
sdk-node_modules:
app-node_modules:
model_cache:
prometheus_data:
grafana_data:
pnpm_cache:
pnpm_store_server:
pnpm_store_web:
server_node_modules:
web_node_modules:
github_node_modules:
cli_node_modules:
docs_node_modules:
e2e_node_modules:
sdk_node_modules:
app_node_modules:
sveltekit:
coverage:

View File

@@ -56,7 +56,7 @@ services:
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
healthcheck:
test: redis-cli ping || exit 1
restart: always
@@ -85,7 +85,7 @@ services:
container_name: immich_prometheus
ports:
- 9090:9090
image: prom/prometheus@sha256:1f0f50f06acaceb0f5670d2c8a658a599affe7b0d8e78b898c1035653849a702
image: prom/prometheus@sha256:5550dc63da361dc30f6fe02ac0e4dfc736ededfef3c8d12a634db04a67824d78
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
@@ -97,7 +97,7 @@ services:
command: ['./run.sh', '-disable-reporting']
ports:
- 3000:3000
image: grafana/grafana:12.3.1-ubuntu@sha256:d57f1365197aec34c4d80869d8ca45bb7787c7663904950dab214dfb40c1c2fd
image: grafana/grafana:12.4.2-ubuntu@sha256:78839fe49e1425c02416fa8072591533a72bd9598e563b54a07d78f9e27fb5d3
volumes:
- grafana-data:/var/lib/grafana

View File

@@ -0,0 +1,100 @@
#
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
#
# Make sure to use the docker-compose.yml of the current release:
#
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
#
# The compose file on main may not be compatible with the latest release.
name: immich
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
user: '1000:1000'
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
volumes:
# Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '2283:2283'
depends_on:
- redis
- database
restart: always
healthcheck:
disable: false
immich-machine-learning:
container_name: immich_machine_learning
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
# Example tag: ${IMMICH_VERSION:-release}-cuda
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
# extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
# file: hwaccel.ml.yml
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
user: '1000:1000'
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
volumes:
- ./ml-model-cache:/cache
- ./ml-dotcache:/.cache
- ./ml-config:/.config
env_file:
- .env
restart: always
healthcheck:
disable: false
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
user: '1000:1000'
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
volumes:
- ./redis:/data
healthcheck:
test: redis-cli ping || exit 1
restart: always
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
user: '1000:1000'
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: '--data-checksums'
# Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
# DB_STORAGE_TYPE: 'HDD'
volumes:
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
shm_size: 128mb
restart: always
healthcheck:
disable: false
volumes:
model-cache:

View File

@@ -49,7 +49,7 @@ services:
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
healthcheck:
test: redis-cli ping || exit 1
restart: always

View File

@@ -1 +1 @@
24.13.0
24.15.0

View File

@@ -402,6 +402,9 @@ To decrease Redis logs, you can add the following line to the `redis:` section o
### How can I run Immich as a non-root user?
You can change the user in the container by setting the `user` argument in `docker-compose.yml` for each service.
[Example docker-compose.yml file](https://github.com/immich-app/immich/blob/main/docker/docker-compose.rootless.yml)
You may need to add mount points or docker volumes for the following internal container paths:
- `immich-machine-learning:/.config`

View File

@@ -140,7 +140,8 @@ For advanced users or automated recovery scenarios, you can restore a database b
```bash title='Backup'
# Replace <DB_USERNAME> with the database username - usually postgres unless you have changed it.
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME> | gzip > "/path/to/backup/dump.sql.gz"
# Replace <DB_DATABASE_NAME> with the database name - usually immich unless you have changed it.
docker exec -t immich_postgres pg_dump --clean --if-exists --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME> | gzip > "/path/to/backup/dump.sql.gz"
```
```bash title='Restore'
@@ -153,9 +154,10 @@ docker start immich_postgres # Start Postgres server
sleep 10 # Wait for Postgres server to start up
# Check the database user if you deviated from the default
# Replace <DB_USERNAME> with the database username - usually postgres unless you have changed it.
# Replace <DB_DATABASE_NAME> with the database name - usually immich unless you have changed it.
gunzip --stdout "/path/to/backup/dump.sql.gz" \
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
| docker exec -i immich_postgres psql --dbname=postgres --username=<DB_USERNAME> # Restore Backup
| docker exec -i immich_postgres psql --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME> --single-transaction --set ON_ERROR_STOP=on # Restore Backup
docker compose up -d # Start remainder of Immich apps
```
@@ -164,7 +166,8 @@ docker compose up -d # Start remainder of Immich apps
```powershell title='Backup'
# Replace <DB_USERNAME> with the database username - usually postgres unless you have changed it.
[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=<DB_USERNAME>))
# Replace <DB_DATABASE_NAME> with the database name - usually immich unless you have changed it.
[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dump --clean --if-exists --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME>))
```
```powershell title='Restore'
@@ -179,8 +182,9 @@ sleep 10 # Wait for Postgres server to
docker exec -it immich_postgres bash # Enter the Docker shell and run the following command
# If your backup ends in `.gz`, replace `cat` with `gunzip --stdout`
# Replace <DB_USERNAME> with the database username - usually postgres unless you have changed it.
# Replace <DB_DATABASE_NAME> with the database name - usually immich unless you have changed it.
cat "/dump.sql" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | psql --dbname=postgres --username=<DB_USERNAME>
cat "/dump.sql" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | psql --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME> --single-transaction --set ON_ERROR_STOP=on
exit # Exit the Docker shell
docker compose up -d # Start remainder of Immich apps
```
@@ -188,6 +192,10 @@ docker compose up -d # Start remainder of Immich ap
</TabItem>
</Tabs>
:::warning
The backup and restore process changed in v2.5.0, if you have a backup created with an older version of Immich, use the documentation version selector to find manual restore instructions for your backup.
:::
:::note
For the database restore to proceed properly, it requires a completely fresh install (i.e., the Immich server has never run since creating the Docker containers). If the Immich app has run, you may encounter Postgres conflicts (relation already exists, violated foreign key constraints, etc.). In this case, delete the `DB_DATA_LOCATION` folder to reset the database.
:::
@@ -196,9 +204,13 @@ For the database restore to proceed properly, it requires a completely fresh ins
Some deployment methods make it difficult to start the database without also starting the server. In these cases, set the environment variable `DB_SKIP_MIGRATIONS=true` before starting the services. This prevents the server from running migrations that interfere with the restore process. Remove this variable and restart services after the database is restored.
:::
:::tip
The provided restore process ensures your database is never in a broken state by committing all changes in one transaction. This may be undesirable behaviour in some circumstances, you can disable it by removing `--single-transaction --set ON_ERROR_STOP=on` from the command.
:::
## Filesystem
Immich stores two types of content in the filesystem: (a) original, unmodified assets (photos and videos), and (b) generated content. We recommend backing up the entire contents of `UPLOAD_LOCATION`, but only the original content is critical, which is stored in the following folders:
Immich does not handle filesystem backups for you. You have to arrange these yourself! Immich stores two types of content in the filesystem: (a) original, unmodified assets (photos and videos), and (b) generated content. We recommend backing up the entire contents of `UPLOAD_LOCATION`, but only the original content is critical, which is stored in the following folders:
1. `UPLOAD_LOCATION/library`
2. `UPLOAD_LOCATION/upload`

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -67,7 +67,8 @@ graph TD
C --> D["Thumbnail Generation (Large, small, blurred and person)"]
D --> E[Smart Search]
D --> F[Face Detection]
D --> G[Video Transcoding]
E --> H[Duplicate Detection]
F --> I[Facial Recognition]
D --> G[OCR]
D --> H[Video Transcoding]
E --> I[Duplicate Detection]
F --> J[Facial Recognition]
```

View File

@@ -14,6 +14,7 @@ Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an i
- [Authelia](https://www.authelia.com/integration/openid-connect/immich/)
- [Okta](https://www.okta.com/openid-connect/)
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)
- [Keycloak](https://www.keycloak.org)
## Prerequisites
@@ -49,6 +50,10 @@ Before enabling OAuth in Immich, a new client application needs to be configured
- `https://immich.example.com/auth/login`
- `https://immich.example.com/user-settings`
3. Configure Backchannel logout URL
If the authentication server supports it, the **Backchannel logout URL** can be specified, and it is of the form: `http://DOMAIN:PORT/api/oauth/backchannel-logout`.
## Enable OAuth
Once you have a new OAuth client application configured, Immich can be configured using the Administration Settings page, available on the web (Administration -> Settings).
@@ -56,11 +61,15 @@ Once you have a new OAuth client application configured, Immich can be configure
| Setting | Type | Default | Description |
| ---------------------------------------------------- | ------- | -------------------- | ----------------------------------------------------------------------------------- |
| Enabled | boolean | false | Enable/disable OAuth |
| Issuer URL | URL | (required) | Required. Self-discovery URL for client (from previous step) |
| Client ID | string | (required) | Required. Client ID (from previous step) |
| Client Secret | string | (required) | Required. Client Secret (previous step) |
| Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) |
| Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
| `issuer_url` | URL | (required) | Required. Self-discovery URL for client (from previous step) |
| `client_id` | string | (required) | Required. Client ID (from previous step) |
| `client_secret` | string | (required) | Required. Client Secret (previous step) |
| `scope` | string | openid email profile | Full list of scopes to send with the request (space delimited) |
| `id_token_signed_response_alg` | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
| `userinfo_signed_response_alg` | string | none | The algorithm used to sign the userinfo response (examples: RS256, HS256) |
| `prompt` | string | (empty) | Prompt parameter for authorization url (examples: select_account, login, consent) |
| `end_session_endpoint` | URL | (empty) | Http(s) alternative end session endpoint (logout URI) |
| Request timeout | string | 30,000 (30 seconds) | Number of milliseconds to wait for http requests to complete before giving up |
| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** |
| Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** |
| Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** |
@@ -178,6 +187,7 @@ Configuration of OAuth in Immich System Settings
| Scope | openid email profile immich_scope |
| ID Token Signed Response Algorithm | RS256 |
| Userinfo Signed Response Algorithm | RS256 |
| End Session Endpoint | https://auth.example.com/logout?rd=https://immich.example.com/ |
| Storage Label Claim | uid |
| Storage Quota Claim | immich_quota |
| Default Storage Quota (GiB) | 0 (empty for unlimited quota) |
@@ -251,4 +261,40 @@ Configuration of OAuth in Immich System Settings
</details>
<details>
<summary>Keycloak Example</summary>
### Keycloak Example
Here's an example of OAuth configured for Keycloak:
Create your immich client on your Keycloak Realm.
<img src={require('./img/keycloak-general-settings.webp').default} width='100%' title="Keycloak Client general Settings" />
<img src={require('./img/keycloak-access-settings.webp').default} width='100%' title="Keycloak Client Access Settings" />
<img src={require('./img/keycloak-capability-config.webp').default} width='100%' title="Keycloak Client Capability Configuration" />
Configuration of OAuth in Immich System Settings
| Setting | Value |
| ---------------------------- | ----------------------------------------------------- |
| Issuer URL | `https://<KEYCLOAK_DOMAIN>/realms/<YOUR_REALM>` |
| Client ID | immich |
| Client Secret | can be optained from Clients -> immich -> Credentials |
| Scope | openid email profile |
| Signing Algorithm | RS256 |
| Storage Label Claim | preferred_username |
| Role Claim | immich_role |
| Storage Quota Claim | immich_quota |
| Default Storage Quota (GiB) | 0 (empty for unlimited quota) |
| Button Text | Sign in with Keycloak (recommended) |
| Auto Register | Enabled (optional) |
| Auto Launch | Enabled (optional) |
| Mobile Redirect URI Override | Disabled |
| Mobile Redirect URI | |
Role Claim can be managed via Client Role. Remember to create a mapper with claim name `immich_role`.
</details>
[oidc]: https://openid.net/connect/

View File

@@ -98,7 +98,6 @@ entryPoints:
respondingTimeouts:
readTimeout: 600s
idleTimeout: 600s
writeTimeout: 600s
```
The second part is in the `docker-compose.yml` file where immich is in. Add the Traefik specific labels like in the example.

View File

@@ -230,7 +230,7 @@ The default value is `ultrafast`.
### Audio codec (`ffmpeg.targetAudioCodec`) {#ffmpeg.targetAudioCodec}
Which audio codec to use when the audio stream is being transcoded. Can be one of `mp3`, `aac`, `libopus`.
Which audio codec to use when the audio stream is being transcoded. Can be one of `mp3`, `aac`, `opus`.
The default value is `aac`.

View File

@@ -1,4 +1,4 @@
# OpenAPI
# API
Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](https://api.immich.app/).

View File

@@ -24,7 +24,7 @@ Immich has three main clients:
3. CLI - Command-line utility for bulk upload
:::info
All three clients use [OpenAPI](./open-api.md) to auto-generate rest clients for easy integration. For more information about this process, see [OpenAPI](./open-api.md).
All three clients use [OpenAPI](/api.md) to auto-generate rest clients for easy integration. For more information about this process, see [OpenAPI](/api.md).
:::
### Mobile App
@@ -71,7 +71,7 @@ An incoming HTTP request is mapped to a controller (`src/controllers`). Controll
### Domain Transfer Objects (DTOs)
The server uses [Domain Transfer Objects](https://en.wikipedia.org/wiki/Data_transfer_object) as public interfaces for the inputs (query, params, and body) and outputs (response) for each endpoint. DTOs translate to [OpenAPI](./open-api.md) schemas and control the generated code used by each client.
The server uses [Domain Transfer Objects](https://en.wikipedia.org/wiki/Data_transfer_object) as public interfaces for the inputs (query, params, and body) and outputs (response) for each endpoint. DTOs translate to [OpenAPI](/api.md) schemas and control the generated code used by each client.
### Background Jobs

View File

@@ -44,7 +44,7 @@ While this guide focuses on VS Code, you have many options for Dev Container dev
**Self-Hostable Options:**
- [Coder](https://coder.com) - Enterprise-focused, requires Terraform knowledge, self-managed
- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise)
- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise). Check [quick-start guide](#quick-start-guide-for-devpod-with-docker)
:::
## Dev Container Services
@@ -408,7 +408,27 @@ If you encounter issues:
1. Check container logs: View → Output → Select "Dev Containers"
2. Rebuild without cache: "Dev Containers: Rebuild Container Without Cache"
3. Review [common Docker issues](https://docs.docker.com/desktop/troubleshoot/)
4. Ask in [Discord](https://discord.immich.app) `#help-desk-support` channel
4. Ask in [Discord](https://discord.immich.app) `#contributing` channel
### Quick-start guide for DevPod with docker
You will need DevPod CLI (check [DevPod CLI installation guide](https://devpod.sh/docs/getting-started/install)) and Docker Desktop.
```sh
# Step 1: Clone the Repository
git clone https://github.com/immich-app/immich.git
cd immich
# Step 2: Prepare DevPod (if you haven't already)
devpod provider add docker
devpod provider use docker
# Step 3: Build 'immich-server-dev' docker image first manually
docker build -f server/Dockerfile.dev -t immich-server-dev .
# Step 4: Now you can start devcontainer
devpod up .
```
## Mobile Development

View File

@@ -53,7 +53,7 @@ You can use `dart fix --apply` and `dcm fix lib` to potentially correct some iss
## OpenAPI
The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/developer/open-api.md) for more details.
The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/api.md) for more details.
## Database Migrations

View File

@@ -80,9 +80,9 @@ To see local changes to `@immich/ui` in Immich, do the following:
1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui`
2. Build the `@immich/ui` project via `pnpm run build`
3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`)
4. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`)
5. Uncomment the import statement in `web/src/app.css` file `@import '/usr/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';`
3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yml` file (`../../ui:/usr/src/ui`)
4. Uncomment the corresponding alias in the `web/vite.config.ts` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui/packages/ui')`)
5. Uncomment the import statement in `web/src/app.css` file `@import '../../../ui/packages/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';`
6. Start up the stack via `make dev`
7. After making changes in `@immich/ui`, rebuild it (`pnpm run build`)
@@ -90,10 +90,13 @@ To see local changes to `@immich/ui` in Immich, do the following:
#### Setup
1. Setup Flutter toolchain using FVM.
2. Run `flutter pub get` to install the dependencies.
3. Run `make translation` to generate the translation file.
4. Run `fvm flutter run` to start the app.
1. [Install mise](https://mise.jdx.dev/installing-mise.html).
2. Change to the immich (root) directory and trust the mise config with `mise trust`.
3. Install tools with mise: `mise install`.
4. Change to the `mobile/` directory.
5. Run `flutter pub get` to install the dependencies.
6. Run `make translation` to generate the translation file.
7. Run `flutter run` to start the app.
#### Translation

View File

@@ -183,7 +183,7 @@ For example to get a list of files that would be uploaded for further
processing:
```bash
immich upload --dry-run . | tail -n +6 | jq .newFiles[]
immich upload --dry-run --json-output . | tail -n +6 | jq .newFiles[]
```
### Obtain the API Key

View File

@@ -0,0 +1,28 @@
# Duplicates Utility
Immich comes with a duplicates utility to help you detect assets that look visually similar. The duplicate detection feature relies on machine learning and is enabled by default. For more information about when the duplicate detection job runs, see [Jobs and Workers](/administration/jobs-workers). Once an asset has been processed and added to a duplicate group, it becomes available to review in the "Review duplicates" utility, which can be found [here](https://my.immich.app/utilities/duplicates).
## Reviewing duplicates
The review duplicates page allows the user to individually select which assets should be kept and which ones should be trashed. When more than one asset is kept, there is an option to automatically put the kept assets into a stack.
### Automatic preselection
When using "Deduplicate All" or viewing suggestions, Immich automatically preselects which assets to keep based on:
1. **Image size in bytes** — larger files are preferred as they typically have higher quality.
2. **Count of EXIF data** — assets with more metadata are preferred.
### Synchronizing metadata
When resolving duplicates, metadata from trashed assets is automatically synchronized to the kept assets. The following metadata is synchronized:
| Name | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Album | The kept assets will be added to _every_ album that the other assets in the group belong to. |
| Favorite | If any of the assets in the group have been added to favorites, every kept asset will also be added to favorites. |
| Rating | If one or more assets in the duplicate group have a rating, the highest rating is selected and synchronized to the kept assets. |
| Description | Descriptions from each asset are combined together and synchronized to all the kept assets. |
| Visibility | The most restrictive visibility is applied to the kept assets. |
| Location | Latitude and longitude are copied if all assets with geolocation data in the group share the same coordinates. |
| Tag | Tags from all assets in the group are merged and applied to every kept asset. |

View File

@@ -80,6 +80,10 @@ There is an automatic scan job that is scheduled to run once a day. Its schedule
This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page.
### Deleting a Library
When deleting an external library, all assets inside are immediately deleted along with the library. Note that while a library can take a long time to fully delete in the background, it is immediately removed from the library list. If the deletion process is interrupted (for example, due to server restart), it will be cleaned up in the next nightly cron job. The cleanup process can also be manually initiated by clicking the "Scan All Libraries" button in the library list.
## Usage
Let's show a concrete example where we add an existing gallery to Immich. Here, we have the following folders we want to add:

View File

@@ -50,6 +50,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
- The GPU must be supported by ROCm. If it isn't officially supported, you can attempt to use the `HSA_OVERRIDE_GFX_VERSION` environmental variable: `HSA_OVERRIDE_GFX_VERSION=<a supported version, e.g. 10.3.0>`. If this doesn't work, you might need to also set `HSA_USE_SVM=0`.
- The ROCm image is quite large and requires at least 35GiB of free disk space. However, pulling later updates to the service through Docker will generally only amount to a few hundred megabytes as the rest will be cached.
- This backend is new and may experience some issues. For example, GPU power consumption can be higher than usual after running inference, even if the machine learning service is idle. In this case, it will only go back to normal after being idle for 5 minutes (configurable with the [MACHINE_LEARNING_MODEL_TTL](/install/environment-variables) setting).
- MIGraphX is a new backend for AMD cards, which compiles models at runtime. As such, the first few inferences will be slow.
#### OpenVINO
@@ -86,8 +87,8 @@ You do not need to redo any machine learning jobs after enabling hardware accele
## Setup
1. If you do not already have it, download the latest [`hwaccel.ml.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
2. In the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino, rknn] to the `image` section's tag at the end of the line.
2. In `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino, rknn] to the `image` section's tag at the end of the line.
3. Still in the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
4. Redeploy the `immich-machine-learning` container with these updated settings.
### Confirming Device Usage

View File

@@ -66,7 +66,7 @@ Now make sure that the local album is selected in the backup screen (steps 1-2 a
- **Keep on device:** You can choose to restrict removal to `Always keep` **All photos** or **All videos**, regardless of other settings. This setting can hamper freeing up space significantly — with 80 GB of videos and 40 GB photos, selecting `Always keep photos` retains thousands of photos on your device.
2. **Scan & Review:** Before any files are removed, you are presented with a review screen to verify which items will be deleted and how much storage is reclamable.
3. **Deletion:** Confirmed items are moved to your device's native Trash/Recycle Bin.
3. **Deletion:** Confirmed items are moved to your device's native Trash/Recycle Bin. For large queues, Immich processes deletion in batches for stability (`2000` assets per batch on Android, `10000` per batch on iOS).
:::info reclaim storage
To use the reclaimed space right away, you must empty the system/gallery trash manually outside of Immich.

View File

@@ -26,7 +26,7 @@ You can search the following types of content:
| Time frame | Start and end date of a specific time bucket |
| Media type | Image or video or both |
| Display options | In Archive, in Favorites or Not in any album |
| Start rating | User-assigned start rating |
| Star rating | User-assigned star rating |
<img src={require('./img/advanced-search-filters.webp').default} width="70%" title='Advanced search filters' />

View File

@@ -18,6 +18,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
| `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
| `JPEG XL` | `.jxl` | :white_check_mark: | |
| `MPO` | `.mpo` | :white_check_mark: | Multi-Picture |
| `PNG` | `.png` | :white_check_mark: | |
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
| `RAW` | `.raw` | :white_check_mark: | |
@@ -28,16 +29,17 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
## Video formats
| Format | Extension(s) | Supported? | Notes |
| :---------- | :-------------------- | :----------------: | :---- |
| `3GPP` | `.3gp` `.3gpp` | :white_check_mark: | |
| `AVI` | `.avi` | :white_check_mark: | |
| `FLV` | `.flv` | :white_check_mark: | |
| `M4V` | `.m4v` | :white_check_mark: | |
| `MATROSKA` | `.mkv` | :white_check_mark: | |
| `MP2T` | `.mts` `.m2ts` `.m2t` | :white_check_mark: | |
| `MP4` | `.mp4` `.insv` | :white_check_mark: | |
| `MPEG` | `.mpg` `.mpe` `.mpeg` | :white_check_mark: | |
| `QUICKTIME` | `.mov` | :white_check_mark: | |
| `WEBM` | `.webm` | :white_check_mark: | |
| `WMV` | `.wmv` | :white_check_mark: | |
| Format | Extension(s) | Supported? | Notes |
| :---------- | :-------------------------- | :----------------: | :---- |
| `3GPP` | `.3gp` `.3gpp` | :white_check_mark: | |
| `AVI` | `.avi` | :white_check_mark: | |
| `FLV` | `.flv` | :white_check_mark: | |
| `M4V` | `.m4v` | :white_check_mark: | |
| `MATROSKA` | `.mkv` | :white_check_mark: | |
| `MP2T` | `.mts` `.m2ts` `.m2t` `.ts` | :white_check_mark: | |
| `MP4` | `.mp4` `.insv` | :white_check_mark: | |
| `MPEG` | `.mpg` `.mpe` `.mpeg` | :white_check_mark: | |
| `MXF` | `.mxf` | :white_check_mark: | |
| `QUICKTIME` | `.mov` | :white_check_mark: | |
| `WEBM` | `.webm` | :white_check_mark: | |
| `WMV` | `.wmv` | :white_check_mark: | |

View File

@@ -3,8 +3,8 @@
You may decide that you'd like to modify the style document which is used to
draw the maps in Immich. In addition to visual customization, this also allows
you to pick your own map tile provider instead of the default one. The default
`style.json` for [light theme](https://github.com/immich-app/immich/tree/main/server/resources/style-light.json)
and [dark theme](https://github.com/immich-app/immich/blob/main/server/resources/style-dark.json)
`style.json` for [light theme](https://tiles.immich.cloud/v1/style/light.json)
and [dark theme](https://tiles.immich.cloud/v1/style/dark.json)
can be used as a basis for creating your own style.
There are several sources for already-made `style.json` map themes, as well as

View File

@@ -20,8 +20,6 @@ def upload(file):
}
data = {
'deviceAssetId': f'{file}-{stats.st_mtime}',
'deviceId': 'python',
'fileCreatedAt': datetime.fromtimestamp(stats.st_mtime),
'fileModifiedAt': datetime.fromtimestamp(stats.st_mtime),
'isFavorite': 'false',

View File

@@ -8,7 +8,8 @@ A config file can be provided as an alternative to the UI configuration.
### Step 1 - Create a new config file
In JSON format, create a new config file (e.g. `immich.json`) and put it in a location that can be accessed by Immich.
In JSON format, create a new config file (e.g. `immich.json`) and put it in a location mounted in the container that can be accessed by Immich.
YAML-formatted config files are also supported.
The default configuration looks like this:
<details>
@@ -26,7 +27,7 @@ The default configuration looks like this:
"ffmpeg": {
"accel": "disabled",
"accelDecode": false,
"acceptedAudioCodecs": ["aac", "mp3", "libopus"],
"acceptedAudioCodecs": ["aac", "mp3", "opus"],
"acceptedContainers": ["mov", "ogg", "webm"],
"acceptedVideoCodecs": ["h264"],
"bframes": -1,
@@ -192,6 +193,7 @@ The default configuration looks like this:
"defaultStorageQuota": null,
"enabled": false,
"issuerUrl": "",
"endSessionEndpoint": "",
"mobileOverrideEnabled": false,
"mobileRedirectUri": "",
"profileSigningAlgorithm": "none",
@@ -251,6 +253,15 @@ So you can just grab it from there, paste it into a file and you're pretty much
In your `.env` file, set the variable `IMMICH_CONFIG_FILE` to the path of your config.
For more information, refer to the [Environment Variables](/install/environment-variables.md) section.
:::tip
YAML-formatted config files are also supported.
:::
:::info Docker Compose
In your `.env` file, the variables `UPLOAD_LOCATION` and `DB_DATA_LOCATION` concern the location on the host.
However, the variable `IMMICH_CONFIG_FILE` concerns the location inside the container, and informs the `immich-server` container that a configuration file is present.
It is recommended to reuse this variable in your `docker-compose.yml`:
```yaml
volumes:
- ./configuration.yml:${IMMICH_CONFIG_FILE}
```
::

View File

@@ -29,22 +29,23 @@ These environment variables are used by the `docker-compose.yml` file and do **N
## General
| Variable | Description | Default | Containers | Workers |
| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
| `IMMICH_LOG_FORMAT` | Log output format (`console`, `json`) | `console` | server | api, microservices |
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/data` | server | api, microservices |
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api |
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices |
| `IMMICH_ALLOW_SETUP` | When `false` disables the `/auth/admin-sign-up` endpoint | `true` | server | api |
| Variable | Description | Default | Containers | Workers |
| :---------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
| `IMMICH_LOG_FORMAT` | Log output format (`console`, `json`) | `console` | server | api, microservices |
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/data` | server | api, microservices |
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
| `IMMICH_HELMET_FILE` | Path to a json file with [helmet](https://www.npmjs.com/package/helmet) options. Set to `false` to disable. Set to `true` to use `server/helmet.json`. | `false` | server | api |
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices |
| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices |
| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api |
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices |
| `IMMICH_ALLOW_SETUP` | When `false` disables the `/auth/admin-sign-up` endpoint | `true` | server | api |
\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.
@@ -166,6 +167,8 @@ Redis (Sentinel) URL example JSON before encoding:
| `MACHINE_LEARNING_PRELOAD__CLIP__VISUAL` | Comma-separated list of (visual) CLIP model(s) to preload and cache | | machine learning |
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION` | Comma-separated list of (recognition) facial recognition model(s) to preload and cache | | machine learning |
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION` | Comma-separated list of (detection) facial recognition model(s) to preload and cache | | machine learning |
| `MACHINE_LEARNING_PRELOAD__OCR__RECOGNITION` | Comma-separated list of (recognition) OCR model(s) to preload and cache | | machine learning |
| `MACHINE_LEARNING_PRELOAD__OCR__DETECTION` | Comma-separated list of (detection) OCR model(s) to preload and cache | | machine learning |
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |

View File

@@ -8,7 +8,7 @@ Hardware and software requirements for Immich:
## Hardware
- **OS**: Recommended Linux or \*nix operating system (Ubuntu, Debian, etc).
- **OS**: Recommended Linux or \*nix 64-bit operating system (Ubuntu, Debian, etc).
- Non-Linux OSes tend to provide a poor Docker experience and are strongly discouraged.
Our ability to assist with setup or troubleshooting on non-Linux OSes will be severely reduced.
If you still want to try to use a non-Linux OS, you can set it up as follows:
@@ -19,6 +19,10 @@ Hardware and software requirements for Immich:
If you have issues, we recommend that you switch to a supported VM deployment.
- **RAM**: Minimum 6GB, recommended 8GB.
- **CPU**: Minimum 2 cores, recommended 4 cores.
- Immich runs on the `amd64` and `arm64` platforms.
Since `v2.6`, the machine learning container on `amd64` requires the `>= x86-64-v2` [microarchitecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels).
Most CPUs released since ~2012 support this microarchitecture.
If you are using a virtual machine, ensure you have selected a [supported microarchitecture](https://pve.proxmox.com/pve-docs/chapter-qm.html#_qemu_cpu_types).
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
- The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.
@@ -45,7 +49,7 @@ Immich requires [**Docker**](https://docs.docker.com/get-started/get-docker/) wi
The Compose plugin will be installed by both Docker Engine and Desktop by following the linked installation guides; it can also be [separately installed](https://docs.docker.com/compose/install/).
:::note
Immich requires the command `docker compose`; the similarly named `docker-compose` is [deprecated](https://docs.docker.com/compose/migrate/) and is no longer supported by Immich.
Immich requires the command `docker compose`; the similarly named `docker-compose` is [deprecated](https://docs.docker.com/retired/#docker-compose-v1-replaced-by-compose-v2) and is no longer supported by Immich.
:::
### Special requirements for Windows users

View File

@@ -8,8 +8,6 @@ sidebar_position: 85
This is a community contribution and not officially supported by the Immich team, but included here for convenience.
Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/charts/tree/master/community/immich).**
:::
Immich can easily be installed on a Synology NAS using Container Manager within DSM. If you have not installed Container Manager already, you can install it in the Packages Center. Refer to the [Container Manager docs](https://kb.synology.com/en-us/DSM/help/ContainerManager/docker_desc?version=7) for more information on using Container Manager.

View File

@@ -26,6 +26,16 @@ docker image prune
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
[releases]: https://github.com/immich-app/immich/releases
## Versioning Policy
Immich follows [semantic versioning][semver], which tags releases in the format `<major>.<minor>.<patch>`. We intend for breaking changes to be limited to major version releases.
You can configure your Docker image to point to the current major version by using a metatag, such as `:v2`.
Currently, we have no plans to backport patches to earlier versions. We encourage all users to run the most recent release of Immich.
Switching back to an earlier version, even within the same minor release tag, is not supported.
[semver]: https://semver.org/
## Migrating to VectorChord
:::info

View File

@@ -6,6 +6,8 @@ You can read more about the differences between storage template engine on and o
The admin user can set the template by using the template builder in the `Administration -> Settings -> Storage Template`. Immich provides a set of variables that you can use in constructing the template, along with additional custom text. If the template produces [multiple files with the same filename, they won't be overwritten](https://github.com/immich-app/immich/discussions/3324) as a sequence number is appended to the filename.
Date and time variables in storage templates are rendered in the server's local timezone.
```bash title="Default template"
Year/Year-Month-Day/Filename.Extension
```

View File

@@ -6,7 +6,7 @@ const prism = require('prism-react-renderer');
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Immich',
tagline: 'High performance self-hosted photo and video backup solution directly from your mobile phone',
tagline: 'Self-hosted photo and video management solution',
url: 'https://docs.immich.app',
baseUrl: '/',
onBrokenLinks: 'throw',
@@ -93,35 +93,15 @@ const config = {
position: 'right',
},
{
to: '/overview/quick-start',
href: 'https://immich.app/',
position: 'right',
label: 'Docs',
},
{
href: 'https://immich.app/roadmap',
position: 'right',
label: 'Roadmap',
},
{
href: 'https://api.immich.app/',
position: 'right',
label: 'API',
},
{
href: 'https://immich.store',
position: 'right',
label: 'Merch',
label: 'Home',
},
{
href: 'https://github.com/immich-app/immich',
label: 'GitHub',
position: 'right',
},
{
href: 'https://discord.immich.app',
label: 'Discord',
position: 'right',
},
{
type: 'html',
position: 'right',
@@ -134,19 +114,78 @@ const config = {
style: 'light',
links: [
{
title: 'Overview',
title: 'Download',
items: [
{
label: 'Quick start',
to: '/overview/quick-start',
label: 'Android',
href: 'https://get.immich.app/android',
},
{
label: 'Installation',
to: '/install/requirements',
label: 'iOS',
href: 'https://get.immich.app/ios',
},
{
label: 'Contributing',
to: '/overview/support-the-project',
label: 'Server',
href: 'https://immich.app/download',
},
],
},
{
title: 'Company',
items: [
{
label: 'FUTO',
href: 'https://futo.tech/',
},
{
label: 'Purchase',
href: 'https://buy.immich.app/',
},
{
label: 'Merch',
href: 'https://immich.store/',
},
],
},
{
title: 'Sites',
items: [
{
label: 'Home',
href: 'https://immich.app',
},
{
label: 'My Immich',
href: 'https://my.immich.app/',
},
{
label: 'Awesome Immich',
href: 'https://awesome.immich.app/',
},
{
label: 'Immich API',
href: 'https://api.immich.app/',
},
{
label: 'Immich Data',
href: 'https://data.immich.app/',
},
{
label: 'Immich Datasets',
href: 'https://datasets.immich.app/',
},
],
},
{
title: 'Miscellaneous',
items: [
{
label: 'Roadmap',
href: 'https://immich.app/roadmap',
},
{
label: 'Cursed Knowledge',
href: 'https://immich.app/cursed-knowledge',
},
{
label: 'Privacy Policy',
@@ -155,24 +194,7 @@ const config = {
],
},
{
title: 'Documentation',
items: [
{
label: 'Roadmap',
href: 'https://immich.app/roadmap',
},
{
label: 'API',
href: 'https://api.immich.app/',
},
{
label: 'Cursed Knowledge',
href: 'https://immich.app/cursed-knowledge',
},
],
},
{
title: 'Links',
title: 'Social',
items: [
{
label: 'GitHub',

View File

@@ -23,3 +23,9 @@ run = "prettier --check ."
[tasks."format-fix"]
env._.path = "./node_modules/.bin"
run = "prettier --write ."
[tasks.deploy]
run = "wrangler pages deploy build --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
[tools]
wrangler = "4.66.0"

View File

@@ -4,11 +4,11 @@
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"format": "prettier --check .",
"format:fix": "prettier --write .",
"format": "prettier --cache --check .",
"format:fix": "prettier --cache --write --list-different .",
"start": "docusaurus start --port 3005",
"copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0",
"build": "npm run copy:openapi && docusaurus build",
"build": "pnpm run copy:openapi && docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
@@ -17,10 +17,10 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "~3.9.0",
"@docusaurus/preset-classic": "~3.9.0",
"@docusaurus/theme-common": "~3.9.0",
"@docusaurus/theme-mermaid": "~3.9.0",
"@docusaurus/core": "~3.10.0",
"@docusaurus/preset-classic": "~3.10.0",
"@docusaurus/theme-common": "~3.10.0",
"@docusaurus/theme-mermaid": "~3.10.0",
"@mdi/js": "^7.3.67",
"@mdi/react": "^1.6.1",
"@mdx-js/react": "^3.0.0",
@@ -30,17 +30,17 @@
"postcss": "^8.4.25",
"prism-react-renderer": "^2.3.1",
"raw-loader": "^4.0.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwindcss": "^3.2.4",
"url": "^0.11.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "~3.9.0",
"@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.7.0",
"@docusaurus/module-type-aliases": "~3.10.0",
"@docusaurus/tsconfig": "^3.10.0",
"@docusaurus/types": "^3.10.0",
"prettier": "^3.7.4",
"typescript": "^5.1.6"
"typescript": "^6.0.0"
},
"browserslist": {
"production": [
@@ -58,6 +58,6 @@
"node": ">=20"
},
"volta": {
"node": "24.13.0"
"node": "24.15.0"
}
}

View File

@@ -32,3 +32,7 @@ If you would like to migrate from one media location to another, simply successf
4. Start up Immich
After version `1.136.0`, Immich can detect when a media location has moved and will automatically update the database paths to keep them in sync.
## Schema drift
Schema drift is when the database schema is out of sync with the code. This could be the result of manual database tinkering, issues during a database restore, or something else. Schema drift can lead to data corruption, application bugs, and other unpredictable behavior. Please reconcile the differences as soon as possible. Specifically, missing `CONSTRAINT`s can result in duplicate assets being uploaded, since the server relies on a checksum `CONSTRAINT` to prevent duplicates.

View File

@@ -23,6 +23,7 @@
/features/storage-template /administration/storage-template 307
/features/user-management /administration/user-management 307
/developer/contributing /developer/pr-checklist 307
/developer/open-api /api 307
/guides/machine-learning /guides/remote-machine-learning 307
/administration/password-login /administration/system-settings 307
/features/search /features/searching 307

View File

@@ -1,7 +1,15 @@
[
{
"label": "v2.5.2",
"url": "https://docs.v2.5.2.archive.immich.app"
"label": "v2.7.5",
"url": "https://docs.v2.7.5.archive.immich.app"
},
{
"label": "v2.6.3",
"url": "https://docs.v2.6.3.archive.immich.app"
},
{
"label": "v2.5.6",
"url": "https://docs.v2.5.6.archive.immich.app"
},
{
"label": "v2.4.1",

View File

@@ -1,8 +1,4 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": "."
}
"extends": "@docusaurus/tsconfig"
}

View File

@@ -1,5 +1,12 @@
import { exportJWK, generateKeyPair } from 'jose';
import {
calculateJwkThumbprint,
exportJWK,
importPKCS8,
importSPKI,
SignJWT,
} from 'jose';
import Provider from 'oidc-provider';
import { PRIVATE_KEY_PEM, PUBLIC_KEY_PEM } from './test-keys';
export enum OAuthClient {
DEFAULT = 'client-default',
@@ -10,6 +17,7 @@ export enum OAuthClient {
export enum OAuthUser {
NO_EMAIL = 'no-email',
NO_NAME = 'no-name',
ID_TOKEN_CLAIMS = 'id-token-claims',
WITH_QUOTA = 'with-quota',
WITH_USERNAME = 'with-username',
WITH_ROLE = 'with-role',
@@ -43,6 +51,29 @@ const claims = [
},
];
const privateKey = await importPKCS8(PRIVATE_KEY_PEM, 'RS256', {
extractable: true,
});
const publicKey = await importSPKI(PUBLIC_KEY_PEM, 'RS256', {
extractable: true,
});
const kid = await calculateJwkThumbprint(await exportJWK(publicKey));
export async function generateLogoutToken(iss: string, sub: string) {
return await new SignJWT({
iss: iss,
aud: OAuthClient.DEFAULT,
iat: Math.floor(Date.now() / 1000),
jti: crypto.randomUUID(),
sub: sub,
events: {
'http://schemas.openid.net/event/backchannel-logout': {},
},
})
.setProtectedHeader({ alg: 'RS256', typ: 'logout+jwt', kid: kid })
.sign(privateKey);
}
const withDefaultClaims = (sub: string) => ({
sub,
email: `${sub}@immich.app`,
@@ -52,12 +83,23 @@ const withDefaultClaims = (sub: string) => ({
email_verified: true,
});
const getClaims = (sub: string) => claims.find((user) => user.sub === sub) || withDefaultClaims(sub);
const getClaims = (sub: string, use?: string) => {
if (sub === OAuthUser.ID_TOKEN_CLAIMS) {
return {
sub,
email: `oauth-${sub}@immich.app`,
email_verified: true,
name: use === 'id_token' ? 'ID Token User' : 'Userinfo User',
};
}
return claims.find((user) => user.sub === sub) || withDefaultClaims(sub);
};
const setup = async () => {
const { privateKey, publicKey } = await generateKeyPair('RS256');
const redirectUris = ['http://127.0.0.1:2285/auth/login', 'https://photos.immich.app/oauth/mobile-redirect'];
const redirectUris = [
'http://127.0.0.1:2285/auth/login',
'https://photos.immich.app/oauth/mobile-redirect',
];
const port = 2286;
const host = '0.0.0.0';
const oidc = new Provider(`http://${host}:${port}`, {
@@ -66,7 +108,10 @@ const setup = async () => {
console.error(error);
ctx.body = 'Internal Server Error';
},
findAccount: (ctx, sub) => ({ accountId: sub, claims: () => getClaims(sub) }),
findAccount: (ctx, sub) => ({
accountId: sub,
claims: (use) => getClaims(sub, use),
}),
scopes: ['openid', 'email', 'profile'],
claims: {
openid: ['sub'],
@@ -94,6 +139,7 @@ const setup = async () => {
state: 'oidc.state',
},
},
conformIdTokenClaims: false,
pkce: {
required: () => false,
},
@@ -125,7 +171,10 @@ const setup = async () => {
],
});
const onStart = () => console.log(`[e2e-auth-server] http://${host}:${port}/.well-known/openid-configuration`);
const onStart = () =>
console.log(
`[e2e-auth-server] http://${host}:${port}/.well-known/openid-configuration`,
);
const app = oidc.listen(port, host, onStart);
return () => app.close();
};

View File

@@ -7,7 +7,7 @@
"start": "tsx startup.ts"
},
"devDependencies": {
"jose": "^5.6.3",
"jose": "^6.0.0",
"@types/oidc-provider": "^9.0.0",
"oidc-provider": "^9.0.0",
"tsx": "^4.20.6"

View File

@@ -0,0 +1,38 @@
export const PRIVATE_KEY_PEM = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVj5C7hzN3E2HO
TcJ+DN/e2NSTQFj4rPylz4J8xjm8Es7l0k2kK5EEGvUNVGZbw7s055c+6kwP9eqg
B5XFE7+26Fcq1sou6Tbm310kU4dnMW5l2CgwrhaGyb1pNysao0AMLT60dFYqtUwn
ha9ceCsa+ZU1JrknVf3rONtppBvhWoI7CO9XX1keVQ0unHPzCWUjpXTzC8OGEbmB
2w7ZIUf8OfJkd5RZ4OtIpML71W9n13aDxT50x2/EW/pFLFtQ/oaleOKHpvlRXDRX
W86G4moUJym3gHMXMUj2aOcFG2UJnpLruKz3i5qZwYiTRlBP6O9EIQNCVtYxchuN
V1CCcBU1AgMBAAECggEAJLfXMu8Nx89ynPVyyUMMaFfoEpHC9iR0L5obQVpiPMYK
VRqVVLecdftPS9s7eQ58BNBRzdC0ZVu841aRYs3HLNbsZZhPkYZQpAxU//Dg5okY
fzj7Hv5yidt4HN9+Pd8z/3lRMnj4WapifLaBt8xJ2ujJBMBRxzJBsXDnT0+Kx7+y
bYDeuVfyUTEikaK3QZTbuRF3D3eiuN16GG+hv8UqTF2eYbPxdiLjYpTSHa4mH88C
qfJz2Xt4SEzmyeo3G+MO17wDFOwtEe8ojlJfULHnHJSFdUwTfYIFM1bg5/fJ9MOS
/fO3TSG+wkQqjQa6eoGssAzP87fL2XNLzlDtGY/7uQKBgQDHuJHOtf1EjOvNYiP7
EN+8QGs41ghzt9CQRQxWbHpusR3IW3P83KMXwYmrlG70oOUXBRGSB/ESXUofXc5W
pu5+Y55S44aUnu/a9yOBttYW0dtHZSL0zFT+PlVASwUzFZ2zcH1KXlUkSpfL5OAD
PyDDTnBZ2AWh45fRO9wLo6PPuQKBgQC/tI03RqU3mOjqukKbquYeIpXHfRU5Z0DM
u9ru1THYEl6fmkMXycxo/mvW3awyFuyKy/VodqIgKnFgumEqCHZh6OAMm/LC7TfA
l9tjFSs/MyOqQVD4kbX+z6Oq4c4GccDoXfsQ3gzECoBapegi/F+6/25y+/C8ghXb
J/Jg1GQXXQKBgQDFgWbfzuVZZyrBfu4qGLPJDMN7/114YizknwPma3xf/tN/EcGQ
K/k1QvWMMkvPq1UiAKcxjJ0AFjV482FcG9T6NDWbrtmmG88C8Sex3Ue2ZW2+GuwI
vhDHJIlV/Vp0/Elp7DJa2xLDwuh+gCZvz3vs6KL+ljxrrhCyn8mp0PfsMQKBgFFZ
KnuETOO0zVGdzFoGQTQUdP58A5+iQwsdxB+I9Ge+E80iRso3ZbhADj7VPhbbR3D2
b6LuhImluQrUzBpsEOAnU7vGCVPSGdBuIDiBaSKebsn2gYeZPWNtdQQ0YZq2dqek
Cb/0mfIuipzsvf7qnSza62F7q4IyqVegMegI+Jg5AoGATM3NMy7JZeKzSkm+3ohU
3xZOwgqKV9SH+0OeYWpuBxT7D7FlrKKI4NJ3XN3hg2f/DJAF6dH11CPe7pk94yol
HMbh+PQUQ6GYvAzxIOvagWboQ3lzeyubNMpyFjfOrIE/WOQCUBZ9tIwCHIarIuyi
QRuNOj3+U8T/n1Ww352HBdw=
-----END PRIVATE KEY-----`;
export const PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlY+Qu4czdxNhzk3Cfgzf
3tjUk0BY+Kz8pc+CfMY5vBLO5dJNpCuRBBr1DVRmW8O7NOeXPupMD/XqoAeVxRO/
tuhXKtbKLuk25t9dJFOHZzFuZdgoMK4Whsm9aTcrGqNADC0+tHRWKrVMJ4WvXHgr
GvmVNSa5J1X96zjbaaQb4VqCOwjvV19ZHlUNLpxz8wllI6V08wvDhhG5gdsO2SFH
/DnyZHeUWeDrSKTC+9VvZ9d2g8U+dMdvxFv6RSxbUP6GpXjih6b5UVw0V1vOhuJq
FCcpt4BzFzFI9mjnBRtlCZ6S67is94uamcGIk0ZQT+jvRCEDQlbWMXIbjVdQgnAV
NQIDAQAB
-----END PUBLIC KEY-----`;

View File

@@ -1 +1 @@
24.13.0
24.15.0

View File

@@ -1,86 +1,77 @@
name: immich-e2e
services:
immich-app-base:
extends:
file: ../docker/docker-compose.dev.yml
service: immich-app-base
immich-init:
extends:
file: ../docker/docker-compose.dev.yml
service: immich-init
container_name: immich-e2e-init
immich-server:
extends:
file: ../docker/docker-compose.dev.yml
service: immich-server
container_name: immich-e2e-server
command: ['immich-dev']
image: immich-server-dev:latest
build:
context: ../
dockerfile: server/Dockerfile.dev
target: dev
ports: !reset []
env_file: !reset []
environment:
- DB_HOSTNAME=database
- DB_USERNAME=postgres
- DB_PASSWORD=postgres
- DB_DATABASE_NAME=immich
- IMMICH_MACHINE_LEARNING_ENABLED=false
- IMMICH_TELEMETRY_INCLUDE=all
- IMMICH_ENV=testing
- IMMICH_PORT=2285
- IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true
DB_HOSTNAME: database
DB_USERNAME: postgres
DB_PASSWORD: postgres
DB_DATABASE_NAME: immich
IMMICH_MACHINE_LEARNING_ENABLED: 'false'
IMMICH_TELEMETRY_INCLUDE: all
IMMICH_ENV: testing
IMMICH_PORT: '2285'
IMMICH_IGNORE_MOUNT_CHECK_ERRORS: 'true'
volumes:
- ./test-assets:/test-assets
- ..:/usr/src/app
- ${UPLOAD_LOCATION}/photos:/data
- /etc/localtime:/etc/localtime:ro
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
- ../plugins:/build/corePlugin
depends_on:
immich-init:
condition: service_healthy
redis:
condition: service_started
database:
condition: service_healthy
immich-web:
extends:
file: ../docker/docker-compose.dev.yml
service: immich-web
container_name: immich-e2e-web
image: immich-web-dev:latest
build:
context: ../
dockerfile: server/Dockerfile.dev
target: dev
command: ['immich-web']
ports:
ports: !override
- 2285:3000
environment:
- IMMICH_SERVER_URL=http://immich-server:2285/
volumes:
- ..:/usr/src/app
- pnpm-store:/usr/src/app/.pnpm-store
- server-node_modules:/usr/src/app/server/node_modules
- web-node_modules:/usr/src/app/web/node_modules
- github-node_modules:/usr/src/app/.github/node_modules
- cli-node_modules:/usr/src/app/cli/node_modules
- docs-node_modules:/usr/src/app/docs/node_modules
- e2e-node_modules:/usr/src/app/e2e/node_modules
- sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules
- app-node_modules:/usr/src/app/node_modules
- sveltekit:/usr/src/app/web/.svelte-kit
- coverage:/usr/src/app/web/coverage
IMMICH_SERVER_URL: http://immich-server:2285/
depends_on:
immich-init:
condition: service_healthy
restart: unless-stopped
redis:
image: redis:6.2-alpine@sha256:37e002448575b32a599109664107e374c8709546905c372a34d64919043b9ceb
extends:
file: ../docker/docker-compose.dev.yml
service: redis
container_name: immich-e2e-redis
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:6f3e9d2c2177af16c2988ff71425d79d89ca630ec2f9c8db03209ab716542338
extends:
file: ../docker/docker-compose.dev.yml
service: database
container_name: immich-e2e-postgres
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
env_file: !reset []
ports: !override
- 5435:5432
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: immich
ports:
- 5435:5432
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres -d immich']
interval: 1s
@@ -89,17 +80,19 @@ services:
start_period: 10s
volumes:
model-cache:
prometheus-data:
grafana-data:
pnpm-store:
server-node_modules:
web-node_modules:
github-node_modules:
cli-node_modules:
docs-node_modules:
e2e-node_modules:
sdk-node_modules:
app-node_modules:
model_cache:
prometheus_data:
grafana_data:
pnpm_cache:
pnpm_store_server:
pnpm_store_web:
server_node_modules:
web_node_modules:
github_node_modules:
cli_node_modules:
docs_node_modules:
e2e_node_modules:
sdk_node_modules:
app_node_modules:
sveltekit:
coverage:

View File

@@ -2,6 +2,7 @@ name: immich-e2e
services:
e2e-auth-server:
container_name: immich-e2e-auth-server
build:
context: ../e2e-auth-server
ports:
@@ -22,15 +23,15 @@ services:
- BUILD_SOURCE_REF=e2e
- BUILD_SOURCE_COMMIT=e2eeeeeeeeeeeeeeeeee
environment:
- DB_HOSTNAME=database
- DB_USERNAME=postgres
- DB_PASSWORD=postgres
- DB_DATABASE_NAME=immich
- IMMICH_MACHINE_LEARNING_ENABLED=false
- IMMICH_TELEMETRY_INCLUDE=all
- IMMICH_ENV=testing
- IMMICH_PORT=2285
- IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true
DB_HOSTNAME: database
DB_USERNAME: postgres
DB_PASSWORD: postgres
DB_DATABASE_NAME: immich
IMMICH_MACHINE_LEARNING_ENABLED: 'false'
IMMICH_TELEMETRY_INCLUDE: all
IMMICH_ENV: testing
IMMICH_PORT: '2285'
IMMICH_IGNORE_MOUNT_CHECK_ERRORS: 'true'
volumes:
- ./test-assets:/test-assets
depends_on:
@@ -42,10 +43,14 @@ services:
- 2285:2285
redis:
image: redis:6.2-alpine@sha256:37e002448575b32a599109664107e374c8709546905c372a34d64919043b9ceb
container_name: immich-e2e-redis
image: docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9
healthcheck:
test: redis-cli ping || exit 1
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:6f3e9d2c2177af16c2988ff71425d79d89ca630ec2f9c8db03209ab716542338
container_name: immich-e2e-postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
environment:
POSTGRES_PASSWORD: postgres
@@ -53,6 +58,7 @@ services:
POSTGRES_DB: immich
ports:
- 5435:5432
shm_size: 128mb
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres -d immich']
interval: 1s

View File

@@ -1,43 +1,48 @@
{
"name": "immich-e2e",
"version": "2.5.2",
"version": "2.7.5",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "vitest --run",
"test:watch": "vitest",
"test:web": "npx playwright test",
"start:web": "npx playwright test --ui",
"format": "prettier --check .",
"format:fix": "prettier --write .",
"test:maintenance": "vitest --run --config vitest.maintenance.config.ts",
"test:web": "pnpm exec playwright test --project=web",
"test:web:maintenance": "pnpm exec playwright test --project=maintenance",
"test:web:ui": "pnpm exec playwright test --project=ui",
"start:web": "pnpm exec playwright test --ui --project=web",
"start:web:maintenance": "pnpm exec playwright test --ui --project=maintenance",
"start:web:ui": "pnpm exec playwright test --ui --project=ui",
"format": "prettier --cache --check .",
"format:fix": "prettier --cache --write --list-different .",
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
"lint:fix": "npm run lint -- --fix",
"lint:fix": "pnpm run lint --fix",
"check": "tsc --noEmit"
},
"keywords": [],
"author": "",
"license": "GNU Affero General Public License version 3",
"devDependencies": {
"@eslint/js": "^9.8.0",
"@eslint/js": "^10.0.0",
"@faker-js/faker": "^10.1.0",
"@immich/cli": "file:../cli",
"@immich/e2e-auth-server": "file:../e2e-auth-server",
"@immich/sdk": "file:../open-api/typescript-sdk",
"@immich/cli": "workspace:*",
"@immich/e2e-auth-server": "workspace:*",
"@immich/sdk": "workspace:*",
"@playwright/test": "^1.44.1",
"@socket.io/component-emitter": "^3.1.2",
"@types/luxon": "^3.4.2",
"@types/node": "^24.10.9",
"@types/node": "^24.12.2",
"@types/pg": "^8.15.1",
"@types/pngjs": "^6.0.4",
"@types/supertest": "^6.0.2",
"@types/supertest": "^7.0.0",
"dotenv": "^17.2.3",
"eslint": "^9.14.0",
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^62.0.0",
"exiftool-vendored": "^34.3.0",
"globals": "^16.0.0",
"eslint-plugin-unicorn": "^64.0.0",
"exiftool-vendored": "^35.0.0",
"globals": "^17.0.0",
"luxon": "^3.4.4",
"pg": "^8.11.3",
"pngjs": "^7.0.0",
@@ -46,12 +51,13 @@
"sharp": "^0.34.5",
"socket.io-client": "^4.7.4",
"supertest": "^7.0.0",
"typescript": "^5.3.3",
"typescript": "^6.0.0",
"typescript-eslint": "^8.28.0",
"utimes": "^5.2.1",
"vitest": "^3.0.0"
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.0.0"
},
"volta": {
"node": "24.13.0"
"node": "24.15.0"
}
}

Some files were not shown because too many files have changed in this diff Show More